feat: Add smbfs mount manager, Jellyfin READMEs, Glances host visibility, planning updates

New secubox-app-smbfs package for SMB/CIFS remote directory management
with smbfsctl CLI (add/remove/mount/umount/test/status), UCI config,
auto-mount init script, and Jellyfin/Lyrion media path integration.

Glances LXC: host bind mounts (/rom, /overlay, /boot, /srv), Docker
socket fix (symlink loop), fs plugin @exit_after patch, hostname/OS
identity, pre-generated /etc/mtab.

KISS READMEs for secubox-app-jellyfin and luci-app-jellyfin. Planning
files updated with Domoticz IoT, AI Gateway strategy, App Store P2P
emancipation, and v2 roadmap items.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-02-04 21:02:46 +01:00
parent eebc84d0b9
commit 79bb3c43f4
13 changed files with 1076 additions and 60 deletions

View File

@ -132,3 +132,7 @@ _Last updated: 2026-02-04_
- `streamlit`: Chunked upload to bypass uhttpd 64KB JSON limit, UTF-8 `.py` file upload fix, auto-install requirements from ZIP, non-standard filename support. - `streamlit`: Chunked upload to bypass uhttpd 64KB JSON limit, UTF-8 `.py` file upload fix, auto-install requirements from ZIP, non-standard filename support.
- `crowdsec-dashboard`: Decisions list fix (wrong RPC expect key). - `crowdsec-dashboard`: Decisions list fix (wrong RPC expect key).
- RPCD: BusyBox ash `local` keyword compatibility fix (wrap call handlers in function). - RPCD: BusyBox ash `local` keyword compatibility fix (wrap call handlers in function).
- `glances`: Full host system visibility — LXC bind mounts for `/rom`, `/overlay`, `/boot`, `/srv`, Docker socket at `/run/docker.sock` (symlink loop fix), `@exit_after` fs plugin patch (multiprocessing fails in LXC), host hostname via `lxc.uts.name`, OpenWrt OS identity from `/etc/openwrt_release`, pre-generated `/etc/mtab` from host `/proc/mounts`.
- `zigbee2mqtt`: Direct `/dev/ttyUSB0` passthrough (socat TCP bridge fails ASH protocol), adapter `ezsp`→`ember` (z2m 2.x), `ZIGBEE2MQTT_DATA` env var, `mosquitto-nossl` dependency.
- `smbfs`: New SMB/CIFS remote mount manager package — UCI config, `smbfsctl` CLI (add/remove/mount/umount/test/status), auto-mount init script, credentials storage, Jellyfin+Lyrion integration, catalog entry.
- `jellyfin`: KISS READMEs for both backend and LuCI packages.

View File

@ -5,6 +5,8 @@ _Last updated: 2026-02-04_
## Resolved ## Resolved
- ~~Expose cyberpunk option inside SecuBox Settings~~ — Done: `THEME_CHOICES` now includes `cyberpunk` in `settings.js`. - ~~Expose cyberpunk option inside SecuBox Settings~~ — Done: `THEME_CHOICES` now includes `cyberpunk` in `settings.js`.
- ~~Glances full system monitoring~~ — Done: LXC host bind mounts, Docker socket, fs plugin patch, hostname/OS identity (2026-02-04).
- ~~Zigbee2MQTT dongle connection~~ — Done: adapter `ezsp`→`ember`, `ZIGBEE2MQTT_DATA` env var, direct `/dev/ttyUSB0` passthrough (2026-02-04).
## Open ## Open
@ -41,3 +43,65 @@ _Last updated: 2026-02-04_
9. **Testing** 9. **Testing**
- Capture screenshot baselines for dark/light/cyberpunk themes. - Capture screenshot baselines for dark/light/cyberpunk themes.
- Automate browser cache busting (append `?v=<git sha>` to view URLs). - Automate browser cache busting (append `?v=<git sha>` to view URLs).
10. **SMB/CIFS Shared Remote Directories**
- Implement smbfs/cifs mount management for shared remote directories.
- Media handling: backups, sources, Lyrion music library, Jellyfin media paths.
- UCI config + LuCI UI for mount management (credentials, auto-mount, mount points).
- Integration hooks for media apps (Jellyfin, Lyrion, backup scripts).
11. **Metablogizer Upload Failures**
- Investigate and fix failed file uploads in Metablogizer.
- May be related to uhttpd 64KB JSON limit (similar to Streamlit fix).
12. **SecuBox v2 Roadmap & Objectives**
- EnigmaBox integration evaluation (community vote?).
- VoIP integration (SIP/WebRTC).
- Domoticz home automation integration.
- SSMTP / mail host / MX record management.
- Reverse MWAN WireGuard peers (multi-WAN failover over mesh).
- Nextcloud self-hosted cloud storage.
- Version v2 release planning and feature prioritization.
**AI Management Layer** (ref: `SecuBox_LocalAI_Strategic_Analysis.html`):
- Phase 1 (v0.18): Upgrade LocalAI → 3.9, MCP Server, Threat Analyst agent, DNS Guard migration.
- Phase 2 (v0.19): CVE Triage + Network Anomaly agents, LocalRecall memory, AI Insights dashboard.
- Phase 3 (v1.0): Config Advisor (ANSSI prep), P2P Mesh Intelligence, Factory auto-provisioning.
- Hybrid approach: Ollama (inference) + LocalAI (orchestrator) + LocalAGI (agents) + LocalRecall (memory).
- MCP tools: crowdsec.alerts, waf.logs, dns.queries, network.flows, system.metrics, wireguard.status, uci.config.
**AI Gateway Hybrid Architecture** (ref: `SecuBox_AI_Gateway_Hybrid_Architecture.html`):
- `secubox-ai-gateway` package: LiteLLM Proxy (port 4000) + Data Classifier + MCP Server.
- Data classification: LOCAL ONLY (raw network data) / SANITIZED (IPs scrubbed) / CLOUD DIRECT (generic).
- Providers: Mistral (EU sovereign, priority 1) > Claude > GPT > Gemini > xAI (all opt-in).
- Offline resilience: Local tier always active, cloud is bonus not dependency.
- Budget cap: configurable monthly cloud spend limit via LiteLLM.
- ANSSI CSPN: Data Classifier + Mistral EU + offline mode = triple sovereignty proof.
13. **Punk Exposure Multi-Domain DNS**
- Multi-domain DNS with P2P exposure and Tor endpoints.
- Classical HTTPS endpoint (DNS provider API: OVH, Gandi, Cloudflare).
- Administrable DNS provider API integration via `dnsctl`.
- Mapped to local services, mesh-federated, locally tweakable.
- Follows Peek / Poke / Emancipate model (see `PUNK-EXPOSURE.md`).
14. **Jellyfin Post-Install**
- Complete startup wizard (media library configuration).
- ~~README documentation~~ — Done (2026-02-04).
15. **Domoticz IoT Integration & SecuBox Peering**
- Create dedicated `luci-app-domoticz` (currently no LuCI app — only generic vhost-manager).
- MQTT auto-bridge: auto-configure Domoticz ↔ zigbee2mqtt via Mosquitto broker.
- Zigbee device discovery: expose z2m device list in Domoticz setup wizard.
- SecuBox P2P mesh: register Domoticz as a mesh service (`secubox-p2p register-service`).
- Tor/DNS exposure channels: add to exposure scanner and Punk Exposure model.
- USB device passthrough: document `/srv/devices` for additional IoT dongles.
- Backup integration: include `/srv/domoticz/config` in secubox-recovery.
- Service registry: add Domoticz to `secubox-p2p` catalog and health checks.
16. **App Store P2P Emancipation**
- Emancipate the app store WebUI as a remote P2P/torrent endpoint.
- Generative remote IPK distribution (like master-link dynamic join IPK generation).
- Decentralized package distribution across mesh nodes.
- Compatible with existing bonus-feed and secubox-feed infrastructure.
- Torrent-style swarming for large IPK downloads across mesh peers.

View File

@ -2,35 +2,39 @@
## Active Threads ## Active Threads
- **SMB/CIFS Remote Mount Manager**
Status: DONE — package created (2026-02-04)
Notes: New `secubox-app-smbfs` package with `smbfsctl` CLI, UCI config, init script, catalog entry.
Integrates with Jellyfin and Lyrion media paths.
- **Jellyfin README**
Status: DONE (2026-02-04)
Notes: KISS READMEs created for both `secubox-app-jellyfin` and `luci-app-jellyfin`.
- **Glances Full System Monitoring**
Status: COMPLETE (2026-02-04)
Notes: LXC host bind mounts, Docker socket, fs plugin patch, hostname/OS identity.
- **Zigbee2mqtt LXC Rewrite** - **Zigbee2mqtt LXC Rewrite**
Status: COMPLETE (2026-02-04) Status: COMPLETE (2026-02-04)
Notes: Rewritten from Docker to LXC Alpine container. Feed rebuilt. Notes: Direct `/dev/ttyUSB0` passthrough, adapter `ezsp`→`ember`, `ZIGBEE2MQTT_DATA` env var.
Deploy fix (2026-02-04): adapter `ezsp`→`ember` (z2m 2.x rename), added `ZIGBEE2MQTT_DATA` env var to start script, added `mosquitto-nossl` dependency. Direct `/dev/ttyUSB0` passthrough works; socat TCP bridge does NOT work (ASH RSTACK timeout).
- **Jellyfin Media Server** ## Strategic Documents Received
Status: COMPLETE (2026-02-04)
Notes: New secubox-app-jellyfin + luci-app-jellyfin with LXC, HAProxy integration, uninstall/update/backup.
- **Device Intel & DNS Provider** - `SecuBox_LocalAI_Strategic_Analysis.html` — AI Management Layer roadmap (LocalAI 3.9 + LocalAGI + MCP).
Status: COMPLETE (2026-02-04) - `SecuBox_AI_Gateway_Hybrid_Architecture.html` — Hybrid Local/Cloud architecture (LiteLLM + Data Classifier + multi-provider).
Notes: New packages added. BusyBox compatibility, OUI emoji display, SDK build pattern aligned.
- **Exposure KISS Redesign**
Status: COMPLETE (2026-02-04)
Notes: Enriched service names, vhost integration, DNS domain sorting, toggle switch fix.
- **Streamlit Upload Fixes**
Status: COMPLETE (2026-02-04)
Notes: Chunked upload (uhttpd 64KB limit), UTF-8 fix, ZIP requirements auto-install, rename support.
## Next Up ## Next Up
- Port the chip header layout to remaining SecuBox derivative apps (client-guardian, auth-guardian) — still pending, neither has `sh-page-header` pattern. 1. **Domoticz IoT Integration** — LuCI app, MQTT auto-bridge, zigbee2mqtt integration, P2P mesh.
- Rebuild bonus feed with all 2026-02-04 changes (partially done — zigbee2mqtt and device-intel included, verify completeness). 2. **Metablogizer Upload Fixes** — Investigate failed uploads.
- Commit uncommitted working tree changes (bonus-feed IPKs, zigbee2mqttctl). 3. **App Store P2P Emancipation** — Remote P2P/torrent endpoint, generative IPK distribution.
4. Port chip header layout to client-guardian, auth-guardian.
5. Rebuild bonus feed with all 2026-02-04 changes.
6. Commit uncommitted working tree changes (bonus-feed IPKs, glancesctl, zigbee2mqttctl, smbfs, jellyfin READMEs).
## Blockers / Risks ## Blockers / Risks
- Cyberpunk theme is now exposed in Settings UI (dark/light/system/cyberpunk) — previous blocker resolved.
- No automated regression tests for LuCI views; manual verification required after each SCP deploy. - No automated regression tests for LuCI views; manual verification required after each SCP deploy.
- `zigbee2mqttctl` has uncommitted changes in working tree. - Glances + Zigbee2MQTT + SMB/CIFS source changes uncommitted in working tree.
- Strategic AI documents noted but not yet implemented (v0.18+ roadmap).

View File

@ -0,0 +1,48 @@
# LuCI Jellyfin Dashboard
Web interface for managing Jellyfin media server with real-time status, container controls, and integration management.
## Installation
```bash
opkg install luci-app-jellyfin
```
## Access
LuCI menu: **Services -> Jellyfin**
## Sections
- **Service Status** -- Container state (running/stopped/not installed), uptime, Docker health, disk usage
- **Integration Status** -- HAProxy (disabled/pending/configured), Mesh P2P, Firewall WAN
- **Actions** -- Install, Start, Stop, Restart, Update, Backup, Uninstall, Open Web UI
- **Configuration** -- Port, image, data path, timezone, domain, HAProxy SSL, media paths, GPU transcoding, mesh toggle
- **Logs** -- Live container log viewer (last 50 lines)
## RPCD Methods
Backend: `luci.jellyfin`
| Method | Description |
|--------|-------------|
| `status` | Full service status, config, and integrations |
| `start` | Start Jellyfin container |
| `stop` | Stop Jellyfin container |
| `restart` | Restart Jellyfin container |
| `install` | Pull image and create container |
| `uninstall` | Remove container and data |
| `update` | Pull latest image and recreate |
| `configure_haproxy` | Register HAProxy vhost |
| `backup` | Create config/data backup |
| `restore` | Restore from backup archive |
| `logs` | Fetch container logs |
## Dependencies
- `luci-base`
- `secubox-app-jellyfin`
## License
Apache-2.0

View File

@ -5,7 +5,7 @@ config glances 'main'
option api_port '61209' option api_port '61209'
option web_host '0.0.0.0' option web_host '0.0.0.0'
option refresh_rate '3' option refresh_rate '3'
option memory_limit '128M' option memory_limit '256M'
config monitoring 'monitoring' config monitoring 'monitoring'
option monitor_docker '1' option monitor_docker '1'

View File

@ -164,6 +164,9 @@ lxc_create_docker_rootfs() {
# Configure container # Configure container
echo "nameserver 8.8.8.8" > "$rootfs/etc/resolv.conf" echo "nameserver 8.8.8.8" > "$rootfs/etc/resolv.conf"
mkdir -p "$rootfs/var/log/glances" "$rootfs/etc/glances" "$rootfs/tmp" mkdir -p "$rootfs/var/log/glances" "$rootfs/etc/glances" "$rootfs/tmp"
mkdir -p "$rootfs/host" "$rootfs/rom" "$rootfs/overlay" "$rootfs/boot" "$rootfs/srv"
mkdir -p "$rootfs/run"
touch "$rootfs/run/docker.sock"
# Ensure /bin/sh exists # Ensure /bin/sh exists
if [ ! -x "$rootfs/bin/sh" ]; then if [ ! -x "$rootfs/bin/sh" ]; then
@ -177,37 +180,46 @@ lxc_create_docker_rootfs() {
fi fi
fi fi
# Create startup script # Patch Glances: disable @exit_after on fs plugin (multiprocessing
cat > "$rootfs/opt/start-glances.sh" << 'START' # fails inside LXC containers, causing all disk_usage calls to return None)
#!/bin/sh local fs_init="$rootfs/app/glances/plugins/fs/__init__.py"
export PATH="/usr/local/bin:/usr/bin:/bin:$PATH" if [ -f "$fs_init" ]; then
export PYTHONPATH="/app:$PYTHONPATH" sed -i 's/^@exit_after(/#@exit_after(/' "$fs_init"
cd / log_info "Patched Glances fs plugin for LXC compatibility"
fi
# Setup hostname resolution (required for socket.gethostbyname to work) # Generate /etc/mtab from host mounts (psutil reads this for disk_partitions)
REAL_HOSTNAME=$(hostname 2>/dev/null || echo glances) grep -E '^(/dev/|overlayfs:)' /proc/mounts | \
cat > /etc/hosts << EOF grep -v '/srv/docker/overlay' > "$rootfs/etc/mtab" 2>/dev/null
127.0.0.1 localhost $REAL_HOSTNAME glances
::1 localhost ip6-localhost ip6-loopback
EOF
# Read environment variables # Create startup script (written via file, not heredoc, to preserve shebang)
WEB_PORT="${GLANCES_WEB_PORT:-61208}" printf '%s\n' '#!/bin/sh' \
WEB_HOST="${GLANCES_WEB_HOST:-0.0.0.0}" 'export PATH="/usr/local/bin:/usr/bin:/bin:$PATH"' \
REFRESH="${GLANCES_REFRESH:-3}" 'export PYTHONPATH="/app:$PYTHONPATH"' \
'cd /' \
# Build args for web server mode '' \
# Use -B for bind address and -p for port separately '# Set hostname from host kernel (proc:mixed exposes this)' \
# Disable autodiscover and check-update to avoid DNS resolution issues 'REAL_HOSTNAME=$(cat /proc/sys/kernel/hostname 2>/dev/null || echo glances)' \
ARGS="-w -B $WEB_HOST -p $WEB_PORT -t $REFRESH --disable-autodiscover --disable-check-update" 'hostname "$REAL_HOSTNAME" 2>/dev/null' \
'printf "127.0.0.1\tlocalhost %s glances\n::1\t\tlocalhost ip6-localhost\n" "$REAL_HOSTNAME" > /etc/hosts' \
# Disable plugins if configured '' \
[ "$GLANCES_NO_DOCKER" = "1" ] && ARGS="$ARGS --disable-plugin docker" '# Copy host os-release so Glances reports OpenWrt, not Alpine' \
[ "$GLANCES_NO_SENSORS" = "1" ] && ARGS="$ARGS --disable-plugin sensors" 'if [ -f /host/openwrt_release ]; then' \
" RELEASE=\$(grep DISTRIB_RELEASE /host/openwrt_release 2>/dev/null | cut -d\"'\" -f2)" \
echo "Starting Glances web server on $WEB_HOST:$WEB_PORT..." ' printf '"'"'NAME="OpenWrt"\nVERSION="%s"\nID=openwrt\nPRETTY_NAME="OpenWrt %s"\n'"'"' "$RELEASE" "$RELEASE" > /etc/os-release' \
exec /venv/bin/python -m glances $ARGS 'fi' \
START '' \
'WEB_PORT="${GLANCES_WEB_PORT:-61208}"' \
'WEB_HOST="${GLANCES_WEB_HOST:-0.0.0.0}"' \
'REFRESH="${GLANCES_REFRESH:-3}"' \
'' \
'ARGS="-w -B $WEB_HOST -p $WEB_PORT -t $REFRESH --disable-autodiscover --disable-check-update"' \
'[ "$GLANCES_NO_DOCKER" = "1" ] && ARGS="$ARGS --disable-plugin docker"' \
'[ "$GLANCES_NO_SENSORS" = "1" ] && ARGS="$ARGS --disable-plugin sensors"' \
'' \
'echo "Starting Glances on ${WEB_HOST}:${WEB_PORT} host=${REAL_HOSTNAME}..."' \
'exec /venv/bin/python -m glances $ARGS' \
> "$rootfs/opt/start-glances.sh"
chmod +x "$rootfs/opt/start-glances.sh" chmod +x "$rootfs/opt/start-glances.sh"
log_info "Glances Docker image extracted successfully" log_info "Glances Docker image extracted successfully"
@ -216,19 +228,33 @@ START
lxc_create_config() { lxc_create_config() {
load_config load_config
cat > "$LXC_CONFIG" << EOF local hostname=$(cat /proc/sys/kernel/hostname 2>/dev/null || echo glances)
# Glances LXC Configuration local mem_bytes=$(echo "$memory_limit" | sed 's/M/000000/')
lxc.uts.name = $LXC_NAME
# Root filesystem cat > "$LXC_CONFIG" << EOF
# Glances LXC Configuration — full host visibility
lxc.uts.name = $hostname
lxc.rootfs.path = dir:$LXC_ROOTFS lxc.rootfs.path = dir:$LXC_ROOTFS
# Network - use host network for full system visibility # Network - host network for full visibility
lxc.net.0.type = none lxc.net.0.type = none
# Mounts - give access to host system info via proc:mixed # Auto-mounts
lxc.mount.auto = proc:mixed sys:ro cgroup:mixed lxc.mount.auto = proc:mixed sys:ro cgroup:mixed
# Host filesystem bind mounts (read-only) for disk monitoring
lxc.mount.entry = /rom rom none bind,ro,create=dir 0 0
lxc.mount.entry = /overlay overlay none bind,ro,create=dir 0 0
lxc.mount.entry = /boot boot none bind,ro,create=dir 0 0
lxc.mount.entry = /srv srv none bind,ro,create=dir 0 0
# Host info for OS identification
lxc.mount.entry = /etc/openwrt_release host/openwrt_release none bind,ro,create=file 0 0
lxc.mount.entry = /etc/openwrt_version host/openwrt_version none bind,ro,create=file 0 0
# Docker socket for container monitoring (mount at /run, not /var/run which is a symlink)
lxc.mount.entry = /var/run/docker.sock run/docker.sock none bind,create=file 0 0
# Environment variables # Environment variables
lxc.environment = GLANCES_WEB_PORT=$web_port lxc.environment = GLANCES_WEB_PORT=$web_port
lxc.environment = GLANCES_WEB_HOST=$web_host lxc.environment = GLANCES_WEB_HOST=$web_host
@ -236,11 +262,11 @@ lxc.environment = GLANCES_REFRESH=$refresh_rate
lxc.environment = GLANCES_NO_DOCKER=$([ "$monitor_docker" = "0" ] && echo 1 || echo 0) lxc.environment = GLANCES_NO_DOCKER=$([ "$monitor_docker" = "0" ] && echo 1 || echo 0)
lxc.environment = GLANCES_NO_SENSORS=$([ "$monitor_sensors" = "0" ] && echo 1 || echo 0) lxc.environment = GLANCES_NO_SENSORS=$([ "$monitor_sensors" = "0" ] && echo 1 || echo 0)
# Capabilities - minimal for monitoring # Capabilities
lxc.cap.drop = sys_admin sys_module mac_admin mac_override sys_rawio lxc.cap.drop = sys_module mac_admin mac_override sys_rawio
# cgroups limits # cgroups limits
lxc.cgroup.memory.limit_in_bytes = $memory_limit lxc.cgroup2.memory.max = $mem_bytes
# Init # Init
lxc.init.cmd = /opt/start-glances.sh lxc.init.cmd = /opt/start-glances.sh
@ -271,6 +297,10 @@ lxc_run() {
# Regenerate config to pick up any UCI changes # Regenerate config to pick up any UCI changes
lxc_create_config lxc_create_config
# Regenerate /etc/mtab from host mounts (psutil reads this)
grep -E '^(/dev/|overlayfs:)' /proc/mounts | \
grep -v '/srv/docker/overlay' > "$LXC_ROOTFS/etc/mtab" 2>/dev/null
log_info "Starting Glances LXC container..." log_info "Starting Glances LXC container..."
log_info "Web interface: http://0.0.0.0:$web_port" log_info "Web interface: http://0.0.0.0:$web_port"
exec lxc-start -n "$LXC_NAME" -F -f "$LXC_CONFIG" exec lxc-start -n "$LXC_NAME" -F -f "$LXC_CONFIG"

View File

@ -0,0 +1,93 @@
# SecuBox Jellyfin Media Server
Free media server for streaming movies, TV shows, music, and photos. Runs Jellyfin inside Docker on SecuBox-powered OpenWrt systems.
## Installation
```sh
opkg install secubox-app-jellyfin
jellyfinctl install
```
## Configuration
UCI config file: `/etc/config/jellyfin`
```
config jellyfin 'main'
option enabled '0'
option image 'jellyfin/jellyfin:latest'
option data_path '/srv/jellyfin'
option port '8096'
option timezone 'Europe/Paris'
config jellyfin 'media'
list media_path '/mnt/media/movies'
list media_path '/mnt/media/music'
config jellyfin 'network'
option domain 'jellyfin.secubox.local'
option haproxy '0'
option firewall_wan '0'
config jellyfin 'transcoding'
option hw_accel '0'
config jellyfin 'mesh'
option enabled '0'
```
## Usage
```sh
# Service control
/etc/init.d/jellyfin start
/etc/init.d/jellyfin stop
# Controller CLI
jellyfinctl install # Pull Docker image and create container
jellyfinctl status # Show container and integration status
jellyfinctl update # Pull latest image and recreate container
jellyfinctl logs # Show container logs (-f to follow)
jellyfinctl shell # Open shell inside container
jellyfinctl backup # Backup config and data
jellyfinctl restore <file> # Restore from backup archive
jellyfinctl uninstall # Stop and remove container and data
# Integrations
jellyfinctl configure-haproxy # Register HAProxy vhost with SSL
jellyfinctl remove-haproxy # Remove HAProxy vhost
jellyfinctl configure-fw # Open WAN firewall port
jellyfinctl remove-fw # Close WAN firewall port
jellyfinctl register-mesh # Register in SecuBox P2P mesh
jellyfinctl unregister-mesh # Remove from mesh registry
```
Web UI: `http://<device-ip>:8096`
## Features
- Docker-based Jellyfin with full lifecycle management
- Multi-path media libraries (movies, music, photos, shows)
- Hardware GPU transcoding support
- HAProxy reverse proxy with SSL/ACME integration
- Firewall WAN port exposure
- SecuBox P2P mesh service registration
- Full config and data backup/restore
- Container shell access and log streaming
## Files
- `/etc/config/jellyfin` -- UCI configuration
- `/etc/init.d/jellyfin` -- procd service script
- `/usr/sbin/jellyfinctl` -- controller CLI
## Dependencies
- `dockerd`
- `docker`
- `containerd`
## License
Apache-2.0

View File

@ -0,0 +1,64 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=secubox-app-smbfs
PKG_VERSION:=1.0.0
PKG_RELEASE:=1
PKG_ARCH:=all
PKG_MAINTAINER:=CyberMind Studio <contact@cybermind.fr>
PKG_LICENSE:=Apache-2.0
PKG_FLAGS:=nonshared
include $(INCLUDE_DIR)/package.mk
define Package/secubox-app-smbfs
SECTION:=utils
CATEGORY:=Utilities
PKGARCH:=all
SUBMENU:=SecuBox Apps
TITLE:=SecuBox SMB/CIFS remote mount manager
DEPENDS:=+kmod-fs-cifs +cifsmount
endef
define Package/secubox-app-smbfs/description
SMB/CIFS remote directory mount manager for SecuBox. Manages shared
network mounts for media servers (Jellyfin, Lyrion), backups, and
general-purpose remote storage over SMB/CIFS protocol.
endef
define Package/secubox-app-smbfs/conffiles
/etc/config/smbfs
endef
define Build/Compile
endef
define Package/secubox-app-smbfs/install
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_CONF) ./files/etc/config/smbfs $(1)/etc/config/smbfs
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) ./files/etc/init.d/smbfs $(1)/etc/init.d/smbfs
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) ./files/usr/sbin/smbfsctl $(1)/usr/sbin/smbfsctl
endef
define Package/secubox-app-smbfs/postinst
#!/bin/sh
[ -n "$${IPKG_INSTROOT}" ] || {
echo ""
echo "============================================"
echo " SecuBox SMB/CIFS Mount Manager Installed"
echo "============================================"
echo ""
echo "Quick Start:"
echo " 1. Add share: smbfsctl add myshare //server/share /mnt/smb/myshare"
echo " 2. Set creds: smbfsctl credentials myshare user password"
echo " 3. Mount: smbfsctl mount myshare"
echo " 4. Auto-mount: smbfsctl enable myshare"
echo ""
}
exit 0
endef
$(eval $(call BuildPackage,secubox-app-smbfs))

View File

@ -0,0 +1,101 @@
# SecuBox SMB/CIFS Remote Mount Manager
Manages SMB/CIFS network shares for media servers (Jellyfin, Lyrion), backups, and general-purpose remote storage.
## Installation
```sh
opkg install secubox-app-smbfs
```
## Configuration
UCI config file: `/etc/config/smbfs`
```
config smbfs 'global'
option enabled '1'
option mount_base '/mnt/smb'
option cifs_version '3.0'
option timeout '10'
config mount 'movies'
option enabled '1'
option server '//192.168.1.100/movies'
option mountpoint '/mnt/smb/movies'
option username 'media'
option _password 'secret'
option read_only '1'
option auto_mount '1'
option description 'NAS movie library'
```
## Usage
```sh
# Add a share
smbfsctl add movies //nas/movies /mnt/smb/movies
# Set credentials
smbfsctl credentials movies user password
# Set options
smbfsctl set movies read_only 1
smbfsctl set movies description 'Movie library'
# Test connectivity
smbfsctl test movies
# Mount / unmount
smbfsctl mount movies
smbfsctl umount movies
# Enable auto-mount at boot
smbfsctl enable movies
# List all shares
smbfsctl list
# Show detailed mount status
smbfsctl status
# Mount all enabled shares
smbfsctl mount-all
```
## Integration with Media Apps
```sh
# Jellyfin: add mounted share as media library
uci add_list jellyfin.media.media_path='/mnt/smb/movies'
uci commit jellyfin
# Lyrion: point music library to mounted share
uci set lyrion.main.media_path='/mnt/smb/music'
uci commit lyrion
```
## Features
- UCI-based share configuration with credentials storage
- Auto-mount at boot for enabled shares
- Read-only or read-write mount modes
- CIFS protocol version selection (2.0, 2.1, 3.0)
- Connectivity test before mounting
- Mount status with disk usage reporting
- Integration with Jellyfin and Lyrion media paths
## Files
- `/etc/config/smbfs` -- UCI configuration
- `/etc/init.d/smbfs` -- procd init script (auto-mount)
- `/usr/sbin/smbfsctl` -- controller CLI
## Dependencies
- `kmod-fs-cifs` -- CIFS kernel module
- `cifsmount` -- mount.cifs utility
## License
Apache-2.0

View File

@ -0,0 +1,21 @@
# SecuBox SMB/CIFS Remote Mount Manager
# Each 'mount' section defines a remote SMB share
config smbfs 'global'
option enabled '1'
option mount_base '/mnt/smb'
option cifs_version '3.0'
option timeout '10'
# Example share (disabled by default):
# config mount 'media'
# option enabled '0'
# option server '//192.168.1.100/media'
# option mountpoint '/mnt/smb/media'
# option username 'guest'
# option _password ''
# option domain ''
# option cifs_version '3.0'
# option read_only '1'
# option auto_mount '1'
# option description 'Media library share'

View File

@ -0,0 +1,27 @@
#!/bin/sh /etc/rc.common
# SecuBox SMB/CIFS mount manager init script
START=90
STOP=15
USE_PROCD=1
start_service() {
local enabled
enabled="$(uci -q get smbfs.global.enabled)"
[ "$enabled" = "1" ] || return 0
/usr/sbin/smbfsctl mount-all
}
stop_service() {
/usr/sbin/smbfsctl umount-all
}
reload_service() {
stop_service
start_service
}
service_triggers() {
procd_add_reload_trigger "smbfs"
}

View File

@ -0,0 +1,504 @@
#!/bin/sh
# SecuBox SMB/CIFS remote mount manager
# Copyright (C) 2025-2026 CyberMind.fr
CONFIG="smbfs"
usage() {
cat <<'EOF'
Usage: smbfsctl <command> [args]
Share Management:
add <name> <server> <mountpoint> Add a new SMB share
remove <name> Remove a share definition
enable <name> Enable auto-mount for a share
disable <name> Disable auto-mount for a share
credentials <name> <user> <pass> Set credentials for a share
set <name> <key> <value> Set a share option
list List all configured shares
Mount Operations:
mount <name> Mount a specific share
mount-all Mount all enabled shares
umount <name> Unmount a specific share
umount-all Unmount all shares
status Show mount status of all shares
test <name> Test connectivity to a share
Examples:
smbfsctl add movies //nas/movies /mnt/smb/movies
smbfsctl credentials movies user mypass
smbfsctl set movies read_only 1
smbfsctl mount movies
smbfsctl enable movies
EOF
}
require_root() { [ "$(id -u)" -eq 0 ] || { echo "[ERROR] Root required" >&2; exit 1; }; }
log_info() { echo "[INFO] $*"; }
log_warn() { echo "[WARN] $*" >&2; }
log_error() { echo "[ERROR] $*" >&2; }
uci_get() { uci -q get ${CONFIG}.$1; }
uci_set() { uci set ${CONFIG}.$1="$2" && uci commit ${CONFIG}; }
# Load global config
load_global() {
mount_base="$(uci_get global.mount_base || echo /mnt/smb)"
default_vers="$(uci_get global.cifs_version || echo 3.0)"
timeout="$(uci_get global.timeout || echo 10)"
}
# Get list of configured share section names
get_shares() {
uci -q show "$CONFIG" | grep "=${CONFIG}\[" | sed "s/.*\.\(.*\)=.*/\1/" | sort -u
uci -q show "$CONFIG" | grep "=mount" | sed "s/${CONFIG}\.\(.*\)=mount/\1/" | sort -u
}
# Check if share section exists
share_exists() {
local type
type="$(uci -q get ${CONFIG}.$1)"
[ "$type" = "mount" ]
}
# Build mount options for a share
build_mount_opts() {
local name="$1"
local username password domain vers ro opts
username="$(uci_get ${name}.username)"
password="$(uci_get ${name}._password)"
domain="$(uci_get ${name}.domain)"
vers="$(uci_get ${name}.cifs_version || echo "$default_vers")"
ro="$(uci_get ${name}.read_only)"
opts="vers=${vers}"
if [ -n "$username" ] && [ "$username" != "guest" ]; then
opts="${opts},username=${username}"
[ -n "$password" ] && opts="${opts},password=${password}"
[ -n "$domain" ] && opts="${opts},domain=${domain}"
else
opts="${opts},guest"
fi
[ "$ro" = "1" ] && opts="${opts},ro" || opts="${opts},rw"
# Reasonable defaults for network mounts
opts="${opts},iocharset=utf8,noperm,noserverino"
echo "$opts"
}
# Check if a share is currently mounted
is_mounted() {
local mountpoint="$1"
grep -q " ${mountpoint} cifs " /proc/mounts 2>/dev/null
}
# =============================================================================
# SHARE MANAGEMENT
# =============================================================================
cmd_add() {
local name="$1" server="$2" mountpoint="$3"
[ -z "$name" ] || [ -z "$server" ] || [ -z "$mountpoint" ] && {
echo "Usage: smbfsctl add <name> <server> <mountpoint>" >&2
exit 1
}
if share_exists "$name"; then
log_error "Share '$name' already exists"
exit 1
fi
load_global
uci add ${CONFIG} mount >/dev/null
# Rename the unnamed section to the given name
local idx
idx=$(uci -q show ${CONFIG} | grep "=mount$" | tail -1 | sed "s/${CONFIG}\.\(.*\)=mount/\1/")
uci rename ${CONFIG}.${idx}="${name}"
uci set ${CONFIG}.${name}.enabled='0'
uci set ${CONFIG}.${name}.server="$server"
uci set ${CONFIG}.${name}.mountpoint="$mountpoint"
uci set ${CONFIG}.${name}.username='guest'
uci set ${CONFIG}.${name}._password=''
uci set ${CONFIG}.${name}.domain=''
uci set ${CONFIG}.${name}.cifs_version="$default_vers"
uci set ${CONFIG}.${name}.read_only='1'
uci set ${CONFIG}.${name}.auto_mount='0'
uci set ${CONFIG}.${name}.description=''
uci commit ${CONFIG}
log_info "Share '$name' added: $server -> $mountpoint"
log_info "Set credentials: smbfsctl credentials $name <user> <pass>"
}
cmd_remove() {
local name="$1"
[ -z "$name" ] && { echo "Usage: smbfsctl remove <name>" >&2; exit 1; }
if ! share_exists "$name"; then
log_error "Share '$name' not found"
exit 1
fi
# Unmount first if mounted
local mountpoint
mountpoint="$(uci_get ${name}.mountpoint)"
if [ -n "$mountpoint" ] && is_mounted "$mountpoint"; then
umount "$mountpoint" 2>/dev/null || umount -l "$mountpoint" 2>/dev/null
fi
uci delete ${CONFIG}.${name}
uci commit ${CONFIG}
log_info "Share '$name' removed"
}
cmd_enable() {
local name="$1"
[ -z "$name" ] && { echo "Usage: smbfsctl enable <name>" >&2; exit 1; }
share_exists "$name" || { log_error "Share '$name' not found"; exit 1; }
uci_set ${name}.enabled '1'
uci_set ${name}.auto_mount '1'
log_info "Share '$name' enabled for auto-mount"
}
cmd_disable() {
local name="$1"
[ -z "$name" ] && { echo "Usage: smbfsctl disable <name>" >&2; exit 1; }
share_exists "$name" || { log_error "Share '$name' not found"; exit 1; }
uci_set ${name}.enabled '0'
uci_set ${name}.auto_mount '0'
log_info "Share '$name' disabled"
}
cmd_credentials() {
local name="$1" user="$2" pass="$3"
[ -z "$name" ] || [ -z "$user" ] && {
echo "Usage: smbfsctl credentials <name> <user> <pass>" >&2
exit 1
}
share_exists "$name" || { log_error "Share '$name' not found"; exit 1; }
uci_set ${name}.username "$user"
uci_set ${name}._password "$pass"
log_info "Credentials set for '$name' (user: $user)"
}
cmd_set() {
local name="$1" key="$2" value="$3"
[ -z "$name" ] || [ -z "$key" ] && {
echo "Usage: smbfsctl set <name> <key> <value>" >&2
exit 1
}
share_exists "$name" || { log_error "Share '$name' not found"; exit 1; }
uci_set ${name}.${key} "$value"
log_info "Set ${name}.${key} = $value"
}
cmd_list() {
load_global
local found=0
local shares
shares="$(get_shares)"
if [ -z "$shares" ]; then
echo "No SMB shares configured."
echo "Add one: smbfsctl add <name> //server/share /mnt/smb/name"
return
fi
printf "%-12s %-8s %-28s %-24s %s\n" "NAME" "STATUS" "SERVER" "MOUNTPOINT" "USER"
printf "%-12s %-8s %-28s %-24s %s\n" "----" "------" "------" "----------" "----"
for name in $shares; do
local enabled server mountpoint username status
enabled="$(uci_get ${name}.enabled)"
server="$(uci_get ${name}.server)"
mountpoint="$(uci_get ${name}.mountpoint)"
username="$(uci_get ${name}.username)"
if [ -n "$mountpoint" ] && is_mounted "$mountpoint"; then
status="mounted"
elif [ "$enabled" = "1" ]; then
status="enabled"
else
status="disabled"
fi
printf "%-12s %-8s %-28s %-24s %s\n" "$name" "$status" "$server" "$mountpoint" "${username:-guest}"
found=1
done
}
# =============================================================================
# MOUNT OPERATIONS
# =============================================================================
cmd_mount() {
require_root
local name="$1"
[ -z "$name" ] && { echo "Usage: smbfsctl mount <name>" >&2; exit 1; }
share_exists "$name" || { log_error "Share '$name' not found"; exit 1; }
load_global
local server mountpoint
server="$(uci_get ${name}.server)"
mountpoint="$(uci_get ${name}.mountpoint)"
[ -z "$server" ] && { log_error "No server configured for '$name'"; exit 1; }
[ -z "$mountpoint" ] && { log_error "No mountpoint configured for '$name'"; exit 1; }
if is_mounted "$mountpoint"; then
log_info "'$name' already mounted at $mountpoint"
return 0
fi
# Create mountpoint
mkdir -p "$mountpoint"
local opts
opts="$(build_mount_opts "$name")"
log_info "Mounting $server -> $mountpoint"
if mount -t cifs "$server" "$mountpoint" -o "$opts" 2>&1; then
log_info "Mounted '$name' successfully"
else
log_error "Failed to mount '$name'"
return 1
fi
}
cmd_mount_all() {
require_root
load_global
local shares count=0 fail=0
shares="$(get_shares)"
for name in $shares; do
local enabled auto_mount
enabled="$(uci_get ${name}.enabled)"
auto_mount="$(uci_get ${name}.auto_mount)"
[ "$enabled" = "1" ] && [ "$auto_mount" = "1" ] || continue
if cmd_mount_single "$name"; then
count=$((count + 1))
else
fail=$((fail + 1))
fi
done
log_info "Mounted $count share(s), $fail failure(s)"
}
# Internal: mount a single share (no arg validation)
cmd_mount_single() {
local name="$1"
local server mountpoint
server="$(uci_get ${name}.server)"
mountpoint="$(uci_get ${name}.mountpoint)"
[ -z "$server" ] || [ -z "$mountpoint" ] && return 1
if is_mounted "$mountpoint"; then
return 0
fi
mkdir -p "$mountpoint"
local opts
opts="$(build_mount_opts "$name")"
mount -t cifs "$server" "$mountpoint" -o "$opts" 2>/dev/null
}
cmd_umount() {
require_root
local name="$1"
[ -z "$name" ] && { echo "Usage: smbfsctl umount <name>" >&2; exit 1; }
share_exists "$name" || { log_error "Share '$name' not found"; exit 1; }
local mountpoint
mountpoint="$(uci_get ${name}.mountpoint)"
if [ -n "$mountpoint" ] && is_mounted "$mountpoint"; then
umount "$mountpoint" 2>/dev/null || umount -l "$mountpoint" 2>/dev/null
log_info "Unmounted '$name' from $mountpoint"
else
log_info "'$name' is not mounted"
fi
}
cmd_umount_all() {
require_root
load_global
local shares
shares="$(get_shares)"
for name in $shares; do
local mountpoint
mountpoint="$(uci_get ${name}.mountpoint)"
if [ -n "$mountpoint" ] && is_mounted "$mountpoint"; then
umount "$mountpoint" 2>/dev/null || umount -l "$mountpoint" 2>/dev/null
log_info "Unmounted '$name'"
fi
done
}
cmd_status() {
load_global
echo "=== SMB/CIFS Mount Status ==="
echo ""
local shares
shares="$(get_shares)"
if [ -z "$shares" ]; then
echo "No shares configured."
return
fi
for name in $shares; do
local enabled server mountpoint desc
enabled="$(uci_get ${name}.enabled)"
server="$(uci_get ${name}.server)"
mountpoint="$(uci_get ${name}.mountpoint)"
desc="$(uci_get ${name}.description)"
printf "Share: %s" "$name"
[ -n "$desc" ] && printf " (%s)" "$desc"
echo ""
printf " Server: %s\n" "$server"
printf " Mountpoint: %s\n" "$mountpoint"
printf " Enabled: %s\n" "$([ "$enabled" = "1" ] && echo "yes" || echo "no")"
if [ -n "$mountpoint" ] && is_mounted "$mountpoint"; then
# Get mount stats
local usage
usage=$(df -h "$mountpoint" 2>/dev/null | tail -1)
printf " Status: MOUNTED\n"
if [ -n "$usage" ]; then
local size used avail pct
size=$(echo "$usage" | awk '{print $2}')
used=$(echo "$usage" | awk '{print $3}')
avail=$(echo "$usage" | awk '{print $4}')
pct=$(echo "$usage" | awk '{print $5}')
printf " Disk: %s used / %s total (%s free, %s)\n" "$used" "$size" "$avail" "$pct"
fi
else
printf " Status: NOT MOUNTED\n"
fi
echo ""
done
}
cmd_test() {
local name="$1"
[ -z "$name" ] && { echo "Usage: smbfsctl test <name>" >&2; exit 1; }
share_exists "$name" || { log_error "Share '$name' not found"; exit 1; }
load_global
local server
server="$(uci_get ${name}.server)"
# Extract hostname from //host/share
local host
host=$(echo "$server" | sed 's|^//||; s|/.*||')
log_info "Testing connectivity to $host..."
# Test network reachability
if ping -c 1 -W "$timeout" "$host" >/dev/null 2>&1; then
log_info "Host $host is reachable"
else
log_error "Host $host is not reachable"
return 1
fi
# Test SMB port (445)
local smb_ok=0
if [ -f /proc/net/tcp ]; then
# Try a TCP connection via shell
if (echo > /dev/tcp/"$host"/445) 2>/dev/null; then
smb_ok=1
fi
fi
# Fallback: try netstat or just attempt mount
if [ "$smb_ok" = "1" ]; then
log_info "SMB port 445 is open on $host"
else
log_warn "Could not verify SMB port 445 (will attempt mount anyway)"
fi
# Try a test mount
require_root
local mountpoint="/tmp/smbfs-test-$$"
mkdir -p "$mountpoint"
local opts
opts="$(build_mount_opts "$name")"
if mount -t cifs "$server" "$mountpoint" -o "$opts" 2>&1; then
log_info "Test mount successful — share is accessible"
local count
count=$(ls -1 "$mountpoint" 2>/dev/null | wc -l)
log_info "Contents: $count items visible"
umount "$mountpoint" 2>/dev/null
else
log_error "Test mount failed — check server, credentials, or share name"
rmdir "$mountpoint" 2>/dev/null
return 1
fi
rmdir "$mountpoint" 2>/dev/null
log_info "Test complete: share '$name' is working"
}
# =============================================================================
# MAIN
# =============================================================================
case "${1:-}" in
add) shift; cmd_add "$@" ;;
remove) shift; cmd_remove "$@" ;;
enable) shift; cmd_enable "$@" ;;
disable) shift; cmd_disable "$@" ;;
credentials) shift; cmd_credentials "$@" ;;
set) shift; cmd_set "$@" ;;
list) shift; cmd_list "$@" ;;
mount) shift; cmd_mount "$@" ;;
mount-all) shift; cmd_mount_all "$@" ;;
umount) shift; cmd_umount "$@" ;;
umount-all) shift; cmd_umount_all "$@" ;;
status) shift; cmd_status "$@" ;;
test) shift; cmd_test "$@" ;;
help|--help|-h|'') usage ;;
*) echo "Unknown command: $1" >&2; usage >&2; exit 1 ;;
esac

View File

@ -2393,6 +2393,62 @@
"refresh_interval": 30, "refresh_interval": 30,
"metrics": [] "metrics": []
} }
},
{
"id": "secubox-app-smbfs",
"name": "SMB/CIFS Mounts",
"version": "1.0.0",
"category": "system",
"runtime": "native",
"description": "SMB/CIFS remote directory mount manager for media servers, backups, and shared storage",
"author": "CyberMind.fr",
"license": "Apache-2.0",
"icon": "\ud83d\udcc1",
"tags": [
"storage",
"smb",
"cifs",
"nas",
"media",
"backup"
],
"packages": {
"required": [
"secubox-app-smbfs",
"kmod-fs-cifs",
"cifsmount"
]
},
"capabilities": [
"storage",
"nas",
"media-library"
],
"requirements": {
"min_ram_mb": 16,
"min_storage_mb": 1
},
"status": "stable",
"notes": "Manages SMB/CIFS network mounts. Integrates with Jellyfin and Lyrion media paths.",
"pkg_version": "1.0.0-1",
"app_version": "1.0.0",
"changelog": {
"1.0.0": {
"date": "2026-02-04",
"changes": [
"Initial release",
"UCI-based share management",
"Auto-mount at boot",
"Jellyfin and Lyrion integration"
]
}
},
"widget": {
"enabled": false,
"template": "default",
"refresh_interval": 60,
"metrics": []
}
} }
], ],
"featured_sections": { "featured_sections": {