# Claude Instructions for SecuBox OpenWrt ## Session Startup — Follow the Roadmap **Before starting any work, always read the project planning files to understand current priorities and context:** 1. `.claude/TODO.md` — Open tasks and backlog, ordered by priority 2. `.claude/WIP.md` — Active threads, next-up items, and blockers 3. `.claude/HISTORY.md` — Completed milestones with dates (gap analysis reference) 4. `.claude/context.md` — Module map, stack overview, and templates 5. `package/secubox/PUNK-EXPOSURE.md` — Architectural spec for exposure features 6. `.claude/FAQ-TROUBLESHOOTING.md` — **Consult before debugging** — resolved issues and known fixes for LXC cgroups, networking, HAProxy, mitmproxy, DNS **When the user says "continue" or "next"**, consult WIP.md "Next Up" and TODO.md "Open" to pick the next task. When completing work, update these files to keep them current. New features and fixes must be appended to HISTORY.md with the date. ## Security Policies ### WAF Bypass Prohibition - **NEVER set `waf_bypass='1'` on any HAProxy vhost** — All traffic MUST pass through the mitmproxy WAF for inspection - When adding new services/domains to HAProxy, route through `mitmproxy_inspector` backend and add the upstream route to `/srv/mitmproxy/haproxy-routes.json` - If a service needs special handling (WebSocket, long connections), configure mitmproxy to properly forward the traffic rather than bypassing WAF - Use `mitmproxyctl sync-routes` to regenerate routes from HAProxy backends, then manually add any missing routes for backends that don't have standard server entries ### Mitmproxy Route Configuration When adding a new service that routes through HAProxy → mitmproxy: 1. Add vhost: `haproxyctl vhost add ` 2. Backend will default to `mitmproxy_inspector` (correct) 3. Add route to mitmproxy: Edit `/srv/mitmproxy/haproxy-routes.json` and `/srv/mitmproxy-in/haproxy-routes.json`: ```json "domain.example.com": ["127.0.0.1", PORT] ``` 4. Restart mitmproxy: `/etc/init.d/mitmproxy restart` ## OpenWrt Shell Scripting Guidelines ### Process Detection - **Use `pgrep crowdsec` instead of `pgrep -x crowdsec`** - The `-x` flag requires an exact process name match which doesn't work reliably on OpenWrt/BusyBox - Same applies to other daemons: use `pgrep ` without `-x` ### Command Availability - `timeout` command is NOT available on OpenWrt by default - use alternatives or check with `command -v timeout` - `ss` command may not be available - use `netstat` or `/proc/net/tcp` as fallbacks - `sqlite3` may not be installed - provide fallback methods (e.g., delete database file instead of running SQL) ### JSON Parsing - **Use `jsonfilter` instead of `jq`** - jsonfilter is native to OpenWrt (part of libubox), jq is often not installed - Syntax examples: ```bash # Get a field value jsonfilter -i /path/to/file.json -e '@.field_name' # Get nested field jsonfilter -i /path/to/file.json -e '@.parent.child' # Get array length (count elements) jsonfilter -i /path/to/file.json -e '@[*]' | wc -l # Get array element jsonfilter -i /path/to/file.json -e '@[0]' ``` - Always check for empty results: `[ -z "$result" ] && result=0` ### Port Detection When checking if a port is listening, use this order of fallbacks: 1. `/proc/net/tcp` (always available) - ports are in hex (e.g., 8080 = 1F90) 2. `netstat -tln` (usually available) 3. `ss -tln` (may not be available) ### Logging - OpenWrt uses `logread` instead of traditional log files - Use `logread -l N` to get last N lines - CrowdSec writes to `/var/log/crowdsec.log` ## Performance Patterns — Double Caching ### Static Stats Pre-caching For dashboards that show aggregated stats (WAF threats, bans, service metrics), use a **cron-generated static JSON file** instead of computing stats on each request: 1. **Create a cron job** that updates stats periodically: ```bash # /etc/cron.d/waf-stats * * * * * root /usr/sbin/waf-stats-update >/dev/null 2>&1 ``` 2. **Write stats to a static file**: ```bash # /usr/sbin/waf-stats-update CACHE_FILE="/tmp/secubox/waf-stats.json" # ... compute stats ... cat > "$CACHE_FILE" << EOF {"running": true, "threats_today": $count, "updated": "$(date -Iseconds)"} EOF ``` 3. **RPCD reads from cache** (fast, no computation): ```sh get_cached_status() { [ -f "$CACHE_FILE" ] && cat "$CACHE_FILE" || echo '{"error":"no cache"}' } ``` ### Progressive Enhancement in LuCI JS Load minimal data first, then enhance with async details: ```javascript load: function() { return Promise.all([ // Fast cached status (always succeeds) callStatusCached().catch(function() { return {}; }), // Slower detail calls with timeout Promise.race([ callAlerts(), new Promise(function(_, r) { setTimeout(r, 5000); }) ]).catch(function() { return { alerts: [] }; }) ]); } ``` ### When to Use Double Caching - **Always** for stats dashboards (WAF, CrowdSec, bandwidth, etc.) - **Always** when RPC calls query log files or compute aggregates - **Always** when data can be stale by 1 minute without user impact - **Never** for real-time control actions (start/stop/restart) ## Build & Sync Workflow ### CRITICAL: Sync Local Feed Before Building - **ALWAYS sync the local-feed before building packages from edited source trees** - The build system uses `secubox-tools/local-feed/` NOT `package/secubox/` directly - If you edit files in `package/secubox//`, those changes won't be built unless synced **Before building after edits:** ```bash # Option 1: Sync specific package to local-feed rsync -av --delete package/secubox// secubox-tools/local-feed// # Option 2: Sync all SecuBox packages for pkg in package/secubox/*/; do name=$(basename "$pkg") rsync -av --delete "$pkg" "secubox-tools/local-feed/$name/" done # Then build ./secubox-tools/local-build.sh build ``` **Quick deploy without rebuild (for RPCD/shell scripts):** ```bash # Copy script directly to router for testing scp package/secubox//root/usr/libexec/rpcd/