diff --git a/CLAUDE.md b/CLAUDE.md index ec8cb448..c93a9fa0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -108,3 +108,64 @@ ssh root@192.168.255.1 '/etc/init.d/rpcd restart' ``` - If unsure, check `OPENWRT_ONLY_PACKAGES` in `secubox-tools/local-build.sh` + +## RPCD Backend Scripting (Shell-based RPC handlers) + +### jshn Argument Size Limits +- **`json_add_string` cannot handle large values** (e.g., base64-encoded images/SVGs) +- jshn passes values as shell arguments, which hit BusyBox's argument size limit ("Argument list too long") +- **Workaround**: Build JSON output manually via file I/O instead of jshn: + ```sh + local tmpfile="/tmp/wg_output_$$.json" + printf '{"field":"' > "$tmpfile" + # Stream large data via pipe/redirect (never as argument) + some_command | base64 -w 0 >> "$tmpfile" + printf '"}\n' >> "$tmpfile" + cat "$tmpfile" + rm -f "$tmpfile" + ``` +- This applies to any RPCD method that returns large blobs (QR codes, certificates, etc.) + +### UCI Private Data Storage +- Use underscore-prefixed option names for internal/hidden data: `uci set network.section._private_field="value"` +- These are not shown in standard LuCI forms but are accessible via `uci -q get` +- Useful for storing client private keys, internal state, etc. + +## LuCI JavaScript Frontend + +### RPC `expect` Field Behavior +- **`rpc.declare({ expect: { field: '' } })` unwraps the response** — it returns ONLY the value of `field`, not the full object +- If the backend returns `{"config": "...", "error": "..."}` and expect is `{ config: '' }`, the result is just the config string — `result.error` is undefined +- **Use `expect: { }` (empty object) when you need the full response** including error fields +- Use `expect: { field: default }` only when you always want just that one field and don't need error handling + +### Module Caching +- **LuCI's JS module loader caches parsed modules in memory** — `Ctrl+Shift+R` does NOT clear this +- Clearing browser cache, `rm /tmp/luci-indexcache*`, and `rm /tmp/luci-modulecache/*` may not be enough +- **Reliable fix**: Force full page navigation with cache-busting query param: + ```js + window.location.href = window.location.pathname + '?' + Date.now(); + ``` +- For development, set `uci set uhttpd.main.no_cache=1 && uci commit uhttpd && /etc/init.d/uhttpd restart` + +### Quick Deploy for LuCI JS/RPCD Changes +- LuCI JS views and shared resources can be deployed directly to the router without rebuilding: + ```bash + # Deploy JS views + scp htdocs/luci-static/resources/view//*.js root@192.168.255.1:/www/luci-static/resources/view// + + # Deploy shared JS libraries + scp htdocs/luci-static/resources//*.js root@192.168.255.1:/www/luci-static/resources// + + # Deploy RPCD handler and restart + scp root/usr/libexec/rpcd/ root@192.168.255.1:/usr/libexec/rpcd/ + ssh root@192.168.255.1 '/etc/init.d/rpcd restart' + + # Clear LuCI caches on router + ssh root@192.168.255.1 'rm -f /tmp/luci-indexcache* /tmp/luci-modulecache/*' + ``` + +### Common Pitfalls +- **RPC params order matters**: The `params` array in `rpc.declare()` must match the positional arguments in `addPeer(arg1, arg2, ...)` calls — adding a new param means updating ALL callers +- **sessionStorage is volatile**: Data stored in `sessionStorage` is lost on tab close/refresh — don't rely on it for persistent data; use UCI backend storage instead +- **Interface name conflicts**: When creating WireGuard interfaces, always check for existing names (wg0, wg1, etc.) and auto-increment to the next available name