- Add multi-instance mode: run multiple apps on different ports
- New UCI config structure with 'instance' sections
- Container starts multiple streamlit processes via STREAMLIT_INSTANCES env
- CLI commands: instance list/add/remove/enable/disable
- Each instance has its own port, requirements auto-install
- Backward compatible: single-app mode still works
- Bumped to 1.0.0-r4
Example config:
config instance 'dashboard'
option app 'dashboard.py'
option port '8502'
option enabled '1'
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Auto-detect and install app-specific requirements on container start
- Supports: <app>.requirements.txt, <app>_requirements.txt, requirements.txt
- Uses hash-based caching to avoid reinstalling on each restart
- Bumped to 1.0.0-r3
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes:
- HAProxy: Prevent duplicate server names when both inline and separate
server UCI sections exist for same backend
- Streamlit: Force --server.headless=true in start script (required for server)
- Dashboard: Optimize get_dashboard_data RPC call (6.56s → 0.09s) by using
fast catalog counting instead of slow appstore list command
- Exposure: Add themed dashboard with SecuBox styling
- ACL: Add missing RPCD permissions for various LuCI apps
Version bumps:
- luci-app-exposure: 1.0.0-r3
- secubox-core: 0.10.0-r5
- secubox-app-haproxy: 1.0.0-r18
- secubox-app-streamlit: 1.0.0-r2
- Portal: v0.15.51
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add cert_is_production() to detect Let's Encrypt staging certificates
- Add cert_validate_public() to verify certificate publicly via curl/openssl
- Add cert_info() to display certificate details (domain, issuer, dates)
- Add cmd_cert_verify command for on-demand certificate verification
- Update cmd_cert_list to show staging/production status with icons
- Update cmd_cert_add to warn about staging mode and verify after issuance
- Bump package release to r16
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace buttons with toggle switches for enabling/disabling exposures
- Show current exposure status with colored indicators
- Load and display Tor hidden services and SSL backends status
- Add stats cards for exposable services, Tor services, and SSL backends
- Modal dialogs for configuring exposure parameters on toggle
- Bump luci-app-exposure to 1.0.0-r2
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix HAProxy certificate key naming (.key -> .crt.key) for directory loading
- Add auto-fix in container startup script for existing certificates
- Add list_exposed_services RPC method to fetch services from secubox-exposure
- Add dynamic port scanning for running services discovery
- Add "Quick Select" dropdown in Add Server modal for service auto-fill
- Bump luci-app-haproxy to 1.0.0-r8
- Bump secubox-app-haproxy to 1.0.0-r15
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- HAProxy overview: Add prominent emergency banner showing service status
with quick health indicators (Container/HAProxy/Config) and one-click
Restart/Start/Stop buttons
- SecuBox dashboard: Add Critical Services Quick Restart section with
buttons for HAProxy, CrowdSec, Tor Shield, and Gitea
- Metabolizer config: Fix portal_path to /www/blog
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Exposure Manager:
- Fix RPCD subshell issues in status and ssl_list methods
- Fix JS views to handle both array and object API responses
MagicMirror2:
- Change default port from 8082 to 8085 (avoid CyberFeed conflict)
- Update mm2ctl, RPCD, settings.js, dashboard.js, config
Tor Shield:
- Add restart method to RPCD and API
- Add health status minicard (Service, Bootstrap, DNS, Kill Switch)
Portal:
- Add 'active-ports' section for detected services
- Separate portal apps (Services) from detected ports (Active Ports)
Service Detection:
- Prioritize port-based identification over process name
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- RPCD: Use temp file for scan to avoid pipe subshell issues
- api.js: Use baseclass.extend() for proper LuCI module pattern
- Menu: Remove UCI dependency that caused 404
- Makefile: Make haproxy/tor optional dependencies
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
New features in this release:
- Service Exposure integration in network section
- Security stats on dashboard (WAN drops, firewall rejects, CrowdSec)
- Threat Monitor in security cards
- Fixed http:// URLs for local services
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
New app entry for service-exposure in portal network apps:
- Port conflict management
- Tor hidden services
- HAProxy SSL backends
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
New unified tool for service exposure management:
- Port conflict detection and resolution (scan, conflicts, fix-port)
- Dynamic Tor hidden service management (tor add/list/remove)
- HAProxy SSL reverse proxy configuration (ssl add/list/remove)
Commands:
secubox-exposure scan # List listening services
secubox-exposure conflicts # Detect port collisions
secubox-exposure tor add gitea # Create .onion for service
secubox-exposure ssl add svc domain # Add HAProxy SSL backend
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Service detection now prioritizes process name matching over port-based
detection for more accurate identification of netifyd, streamlit,
cyberfeed, metabolizer, magicmirror, and picobrew services.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Services in LXC/Docker containers don't have SSL certificates,
so always use http:// instead of inheriting the browser's protocol.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add threat-monitor app to security section in portal.js
- Add security stats RPC call (get_security_stats)
- Display packets blocked and alerts on dashboard
- Add Threat Monitor to featured quick access apps
- Show WAN dropped + firewall rejects in events section
- Link to Threat Monitor dashboard from events
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Security Stats:
- Add get_security_stats RPCD method for quick overview
- Track WAN drops, firewall rejects, CrowdSec bans
- Add secubox-stats CLI tool for quick stats check
Gitea Mirror Commands:
- Add mirror-sync to trigger mirror repository sync
- Add mirror-list to show all mirrored repos
- Add mirror-create to create new mirrors from GitHub URLs
- Add repo-list to list all repositories
- Requires API token: uci set gitea.main.api_token=<token>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- haproxy: Add explicit restart_service function
- tor-shield: Add explicit restart_service function
- wireguard-dashboard/qrcode.js: Use baseclass.extend() pattern
to fix "factory yields invalid constructor" error
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
HAProxy requires certificate files to contain both the fullchain
(cert + intermediate CA) and the private key concatenated together.
Changes:
- haproxyctl: Fix cert_add to create combined .pem files
- haproxy-sync-certs: New script to sync ACME certs to HAProxy format
- haproxy.sh: ACME deploy hook for HAProxy
- init.d: Sync certs before starting HAProxy
- Makefile: Install new scripts, add cron job for cert sync
This fixes the "No Private Key found" error when HAProxy tries to
load certificates that only contain the fullchain without the key.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The RPC expect clause unwraps responses - when `expect: { peers: [] }`
is used, the response `{peers: [...]}` gets unwrapped to just `[...]`.
Fixed:
- api.js: getAllData and getMonitoringData now handle both array
and object formats for peers, interfaces, and rates
- overview.js: render and polling functions now safely unwrap
data that may be array or nested object
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
RPCD backend:
- Fix critical json_init bug: variables must be extracted BEFORE
json_init() which wipes the loaded JSON. Affected functions:
save_settings, do_enable, set_bridges, add/remove_hidden_service
- Fix process detection: use pgrep instead of pid file
- Fix uptime calculation: get PID from pgrep, not pid file
- Fix RPC expect unwrapping in getDashboardData for presets
Init script:
- Remove PidFile directive (procd manages the process)
- Clean up stale files before starting to avoid permission issues
- Set proper ownership on torrc after generation
- Fix iptables chain creation to handle "already exists" gracefully
- Remove from OUTPUT chain before attempting chain deletion
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The RPC expect clause unwraps the response, so circuits data may be
an array directly rather than an object with circuits property.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix subshell bug in get_circuits (pipe loses JSON state)
- Add has_control flag to status for frontend awareness
- Fix UseBridges without bridge lines causing Tor to fail
- Fix hidden service directory ownership (tor:tor)
- Change log output from file to syslog
- Fix run directory ownership and permissions (700)
- Add CookieAuthentication for control socket auth
- Use socat instead of nc (BusyBox lacks Unix socket support)
- Add socat as package dependency
- Optimize duplicate curl calls in status check
- Use fallback IP services for real_ip detection
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add get_services RPCD method to detect listening TCP services
- Map known ports to service names, icons, and categories
- Display clickable service cards in portal Services tab
- Services link directly to their URLs (e.g., :3000 for Gitea)
- Filter to show only externally accessible services with URLs
- Add ACL permissions for portal and admin apps
Detected services include: Gitea, HexoJS, CyberFeed, Streamlit,
HAProxy Stats, Netifyd, LuCI, Lyrion, and more.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
HexoJS now serves dynamically on :4000 via HAProxy vhost routing.
- Disabled auto_publish in metabolizer
- Disabled portal in hexojs
- /www stays free for SecuBox portal and other apps
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add 'generate' as alias for 'build' command
- Rename cmd_publish for drafts to cmd_publish_draft
- Fix duplicate cmd_publish functions
- Add portal config section with /www/blog path
- publish draft <slug> for drafts, publish for portal
- Metabolizer integration now working
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- RPCD _add_backend now parses inline 'server' option format
- Servers embedded in backend response with inline flag
- update_server converts inline servers to separate UCI sections
- delete_server handles both inline and separate server sections
- API and UI pass inline flag for proper handling
Fixes server port editing in LuCI backends interface.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add showEditVhostModal() for editing virtual host properties
- Add showEditBackendModal() for editing backend configuration
- Add showEditServerModal() for editing server properties
- Modern card-based UI with inline edit/delete actions
- Toggle enable/disable for backends
- Fix haproxyctl to read server option from backend UCI sections
- Add debug logging to container startup script
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Change all hardcoded /blog/ paths to use / as root:
Theme configuration:
- _config.yml: Menu paths now /cybersecurity/ instead of /blog/cybersecurity/
- Blog submenu path changed to /categories/
Layout templates:
- post.ejs: Category link uses url_for with root path
- index.ejs: "Voir le blog" links to /categories/
- category.ejs: Breadcrumb and back links use /categories/
Scripts:
- dynamic-blog.js: Category paths now /{slug}/ instead of /blog/{slug}/
- Menu blog path changed to /categories/
Presets:
- tech.yml: Menu paths updated
- portfolio.yml: Blog link updated
hexoctl:
- Default portal_path changed from /www/blog to /www
- Help text updated
This allows the blog to be served from the root URL with categories
at /{category}/ paths.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Change pidfile from haproxy-lxc.pid to haproxy.pid for consistency
with the actual container name.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Change container references from 'haproxy-lxc' to 'haproxy' to match
the actual container name used by secubox-app-haproxy. This fixes
the LuCI status view showing container_running: false.
Fixes affected methods:
- method_status: container existence and state checks
- method_get_stats: container running check
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Templates now properly use Hexo's url_for() helper:
- apps.ejs: navigation links
- category.ejs: breadcrumb, apps, services, blog links
- showcase.ejs: contact, portfolio, apps, services links
This ensures all links work correctly when root is set to a
subdirectory (e.g., /blog/ instead of /)
Bumped release to r5
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Calculate web root from portal path (e.g., /www/blog → /blog/)
- Update _config.yml root setting before regenerating
- Run hexo clean && generate to apply new root
- Bumped release to r4
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Piped while loop runs in subshell, JSON additions don't persist
- Use temp file + redirect to avoid subshell issue
- Also fix list_backups with same pattern
- Bumped release to r2
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Server now binds to 0.0.0.0 instead of localhost for external access
- Added publish command to copy static files to /www/blog/
- Startup script always regenerates to ensure correct binding
- Bumped release to r3
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add gitea_status, gitea_sync, gitea_clone, gitea_save_config RPCD methods
- Add Gitea section to sync.js with config form and sync buttons
- Update ACL for new Gitea methods
- Fix luci-app-metabolizer install section for RPCD executable
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove yaml import, use simple string parsing for front matter
- Remove dependency on host metabolizerctl
- Use environment variables for paths (METABOLIZER_CONTENT)
- Remove switch_page calls that fail in container
- CMS now works standalone inside Streamlit container
- Bump to r2
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Export PATH at top of startup script for git binary
- Export HOME=/data for proper environment
- Set SCRIPT_TYPE=sh in app.ini (no bash in Alpine)
- Bump to r5
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Alpine's adduser wasn't creating the group properly, causing
chown git:git to fail with "unknown group".
- Add explicit addgroup -g 1000 git before adduser
- Use -G git flag to assign user to the group
- Bump to r4
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use /bin/sh instead of /bin/bash for git user shell
- Check for su-exec binary instead of marker file for deps
- Always recreate git user on startup (doesn't persist in container)
- Set explicit UID 1000 for git user
- Bump release to r3
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>