grep -c returns exit code 1 when count is 0, which triggered
the `|| echo 0` fallback to also output "0", resulting in:
"waf_threats_today":0
0,
This broke JSON parsing and caused the dashboard to show
CrowdSec as "STOPPED" even when running.
Fix: Use `|| :` (no-op) and set defaults with `: "${var:=0}"`
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace stat -c %Y with date -r for BusyBox compatibility (stat not available)
- Fix get_cache_age() to properly return early when cache file missing
- Fix grep -c || echo 0 pattern that caused "invalid number '0\n0'" errors
- Add proper numeric defaults using : "${var:=0}" pattern
- Add freshness metadata (_freshness) with age, timestamp, and fresh boolean
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add visible "Updated Xs ago" timestamps and freshness indicators to
make cached stats look more alive and help users know data currency.
Backend changes:
- luci.metrics: Add _freshness metadata (age, fresh, timestamp_epoch)
to overview, waf_stats, and connections responses
- luci.crowdsec-dashboard: Add _freshness metadata to get_overview
response using sed injection into cached JSON
Frontend changes:
- metrics/dashboard.js: Display freshness indicator (green/yellow/red)
in header, animate value changes with flash effect
- crowdsec-dashboard/overview.js: Display freshness indicator next to
running badge, update on poll
Shared utilities (kiss-theme.js):
- formatAge(seconds): Format "Xs ago", "Xm ago", "Xh ago"
- getFreshnessClass(age): Return fresh/recent/stale based on age
- getFreshnessColor(class): Return #00c853/#ff9800/#f44336
- freshnessIndicator(age, id): Create indicator DOM element
- updateFreshness(age, id): Update existing indicator
Freshness thresholds:
- Fresh (green): < 15s for metrics, < 30s for CrowdSec
- Recent (yellow): < 45s for metrics, < 90s for CrowdSec
- Stale (red): > threshold
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add complete French (fr) and Chinese (zh) translations for all documentation:
- Root files: README, CHANGELOG, SECURITY, BETA-RELEASE
- docs/: All 16 core documentation files
- DOCS/: All 19 deep-dive documents including embedded/ and archive/
- package/secubox/: All 123+ package READMEs
- Misc: secubox-tools/, scripts/, EXAMPLES/, config-backups/, streamlit-apps/
Total: 346 translation files created
Each file includes language switcher links for easy navigation between
English, French, and Chinese versions.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add "Sync WAF Routes" button to HAProxy vhosts page in LuCI
- Add sync_mitmproxy_routes RPC method to HAProxy RPCD backend
- Fix mitmproxyctl and secubox-route to handle LuCI backends (luci, luci_default, luci_control)
- Remove outdated port 8081 skip filter in route sync that prevented LuCI routes
- These changes allow vhosts with original_backend='luci' to be properly
routed through the WAF
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The buggy tr '[:upper:]' '[:lower:]' was also used for sanitizing
site names, causing 'ziptest' to become 'ziwtest'. Use awk tolower()
for all lowercase conversions.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Allow selecting and uploading multiple files at once
- Each file gets its own name input field
- Files are processed sequentially with progress indicator
- Errors are collected and displayed at the end
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
BusyBox tr '[:upper:]' '[:lower:]' has a bug that converts 'p' to 'w',
causing .zip to be detected as .ziw. Use awk tolower() instead.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Windows uploads may include carriage returns in filenames, causing
.zip to be detected as .ziw. Strip \r\n from extension string.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Wrap for loop with output redirection in subshell for
BusyBox ash compatibility when generating Packages index.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add secubox-app-repo and luci-app-repo to local feed
- Regenerate Packages index
- Update all embedded packages
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add usign dependency for package signing
- Sign Packages files after generation in repo-sync
- Generate signing keys automatically if not present
- Remove duplicate ACL file (now only in luci-app-repo)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Auto-create repo directories on install
- Detect device architecture and configure customfeeds.conf
- Add prerm script to cleanup on uninstall
- Points opkg to local repo at 192.168.255.1:8888
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- WAF blocked now counts mitmproxy scenario decisions (1031 blocks)
- Removed waf_threats field (redundant with waf_blocked)
- Fixed dashboard to show 3 WAF stats: Bans, Alerts, Blocked
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
UI:
- Clean card grid with colored stat values
- Services status bar (HAProxy, WAF, CrowdSec) with glowing dots
- Two-panel layout for WAF/Security and Connections
- Live clock with pulsing indicator
- Proper KissTheme.wrap() integration
Performance:
- Double-buffer cache at /tmp/secubox/metrics-cache.json
- 30s TTL with async background refresh
- Cron job for periodic cache updates
- Instant RPCD response (no computation on request)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
mitmproxy's haproxy_router.py addon already implements hot-reload:
- Checks routes file mtime on every request
- Auto-reloads when file changes
Removed unnecessary mitmproxy restart after adding routes.
Just ensure file permissions are correct (644) for hot-reload to work.
This makes publishing faster and avoids service disruption.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add mitmproxy restart after _add_mitmproxy_route() to load new routes
- mitmproxy loads routes at startup only, so restart is required
- Run restart in background to avoid blocking publish command
Also fixed on router:
- Disabled health check for mitmproxy_inspector backend
- HAProxy health check fails because mitmproxy returns 404 for
requests without valid Host header
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Changed vhost backend from direct metablog_* to mitmproxy_inspector
- Added original_backend tracking for mitmproxy route resolution
- Changed server address from 192.168.255.1 to 127.0.0.1
- Added _add_mitmproxy_route helper for route registration
- Fixed both cmd_publish() and _emancipate_haproxy() functions
This ensures all newly published sites go through WAF inspection
rather than bypassing security checks.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- RPCD handler returns immediately with job_id (~0.04s)
- Background script uses file output to avoid pipe inheritance issues
- LuCI JS polls job_status every 2s until completion
- Uses setsid for proper process detachment
- jsonfilter for reliable parameter parsing
Fixes "Failed to publish" error caused by ubus timeout during
40+ second publish operations.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- dropletctl: Remove pipe to grep that blocked on background children
- metablogizerctl: Background HAProxy generate/reload (~90s with 95 certs)
- dpi-lan-collector: Pre-compute flow counts in single pass instead of
spawning grep per client (eliminates broken pipe errors)
Publish time reduced from ~2 min to ~35 seconds.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add headless UCI option to use mitmdump instead of mitmweb
- Enable headless by default for WAF (mitmproxy-in) instance
- Increase default memory limit from 256MB to 2GB
- Fix LXC config generation to always recreate on service start
- Fix rootfs check path (/usr/local/bin not /usr/bin)
- Use exec in startup script for proper foreground execution
Headless mode runs mitmdump (CLI) instead of mitmweb (web UI),
saving ~3.3GB RAM while maintaining full WAF functionality.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- New heatmap.js component with SVG world map and country centroids
- Colored dots show threat distribution: orange (local), cyan (CAPI), red (WAF)
- Dot size scales logarithmically with threat count (4-20px)
- Hover tooltips show country code and count
- Added geo_local_raw and geo_capi_raw fields to RPCD backend
- CAPI geo extraction from decisions with GeoIP metadata
- CSS styling for heatmap container, dots, and legend
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Changed waf_bans_today to count all mitmproxy-* scenarios instead of
only mitmproxy-waf (which doesn't exist). Now correctly counts
mitmproxy-scanner, mitmproxy-botscan, etc.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Make refresh_cache async to prevent rpcd watchdog kills
- Fix JSON escaping for top_scenarios/countries arrays
- Show decisions as "Active Bans" when alerts_raw is empty
- Display ban expiry time instead of creation time
- Update cron to run cache refresh in background
Fixes LuCI crashes caused by 16s blocking refresh calls.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- alerts_24h now uses local_decisions count instead of empty file
- top_scenarios_raw now extracts from decisions JSON (was parsing CAPI metrics)
- top_countries_raw now correctly parses IsoCode from alerts GeoIP metadata
- Double-buffer caching via cron job already in place
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Rewrite smtp-relay/settings.js with proper KISS theme styling
- Rewrite secubox-users/overview.js with proper KISS theme styling
- Use KissTheme.wrap() for consistent dark theme rendering
- Add stat cards with colored values matching mailserver reference
- Add proper form styling with inline CSS variables
- Add NZB tools (SABnzbd, NZBHydra) to KISS menu Downloads
- Add webtorrent to portal tree Downloads category
- Fix portal tree webtorrent pattern
KISS = Keep It Simple Sexy
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The updateUI function was incorrectly disabling the Rescan button when
Lyrion was running. Rescan should only be enabled when running.
- Split forEach into separate button handlers
- Start button: disabled when running (correct)
- Rescan button: disabled when NOT running (fixed)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Optimize exposure RPCD: O(n) single-pass awk parsing for vhost_list
and ssl_list (fixes XHR timeout on 200+ vhosts)
- Fix portal tree URLs: Use get_menu_path() to read actual LuCI menu
paths from JSON instead of hardcoded paths
- Add Downloads category to portal tree (torrent, droplet patterns)
- Add new apps to System category (config-vault, reporter, smtp-relay,
rtty, dpi-dual, metacatalog)
- Enhance KISS theme menu: Add Downloads, Monitoring categories
- Fix Lyrion URL: Use HTTPS vhost instead of dynamic port URL
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Move Web Interface section to top for visibility
- Always show Open Lyrion Web UI button with dynamic URL
- Display URL text next to button
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove KissTheme dependency, use direct RPC calls
- Dark theme colors (#12121a, #1a1a24, #00d4aa, #00a0ff)
- Update common.css with matching dark styles
- Simplified DOM rendering with inline styles
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove KissTheme dependency, use standard LuCI with inline styles
- Dark theme matching SecuBox palette (#12121a, #1a1a24, #00d4aa, #00a0ff)
- Simplified view with direct DOM rendering instead of form.Map
- Cards grid layout for status, WiFi interfaces, DHCP, config
- Inline Trust/Block action buttons with proper styling
- Responsive tables for clients and alerts
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Change menu path from /admin/secubox/security/threat-analyst to
/admin/services/threat-analyst for proper placement
- Rewrite dashboard.css with dark theme colors matching SecuBox palette
(#12121a, #1a1a24, #0a0a12 backgrounds; #00d4aa, #00a0ff accents)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
WAF Dashboard:
- Use cached bans from cron (waf-stats-update) instead of slow cscli
- Fixes "Failed to load bans" timeout issue
DPI Dual-Stream:
- Add LAN Flow Analysis card showing active clients, destinations, protocols
- LAN passive flow analysis was working but not displayed
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update lastUpdate to 2026-03-16
- Update layer progress: core 98%, ai 95%, mirrornet 90%, certification 75%
- Mark milestones v0.18, v0.19, v1.0 as completed
- Add v1.1 Extended Mesh as in-progress
- Update stats: 190 packages, 243 vhosts, 174 WAF routes, 1850 commits
- Update feature status: AI security, AI memory, mesh network to production
- Update config-management to production with config-vault
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- New secubox-app-smtp-relay package with centralized SMTP config
- Shared library with send_mail(), send_html_mail(), send_text_mail()
- CLI: smtp-relayctl with status/test/send/configure/admin commands
- RPCD: 5 methods for LuCI integration
- LuCI settings page with mode selection and test button
- Modes: external (SMTP server), local (auto-detect mailserver), direct
- Migrated reporter and bandwidth-manager to use shared library
- Backwards-compatible fallback to legacy per-app config
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>