Sessions file defined as __SESSIONS_FILE but referenced as _SESSIONS_FILE in
every handler -> NameError on session save/load, so valid logins failed and
/status 500d. Normalised all refs to _SESSIONS_FILE. Verified: login endpoint
returns proper JSON (401 on bad creds), sessions persist.
Bare 'dh $@' rules never installed api/www/config, so the service could not
chdir to its (nonexistent) WorkingDirectory and crash-looped 4042x; ExecStart
also called /usr/bin/uvicorn which is not present. Add override_dh_auto_install
and use /usr/bin/python3 -m uvicorn. Deployed live: active, 0 restarts, API 200.
nDPId's zmq distributor is not run on gk2 (perf revert #722/#723), so the
ndpid DB is empty and the panel showed nothing. Add DpiBridge reading
/var/lib/secubox/dpi/{cumulative,state}.json and fall the flows/protocols/
applications/top/stats endpoints back to it (nDPId preferred if it has data).
JA3/JA4 + per-flow risks stay empty (no equivalent). Live: 1.3M flows, TLS/DNS/
BitTorrent top protocols, real top apps.
secubox-ndpid crash-looped 1111x with 226/NAMESPACE: ReadWritePaths listed
/run/ndpid (nonexistent) — the socket is /run/secubox/ndpid.sock. Removed it.
Added nginx/ndpid.conf routing /api/v1/ndpid/ to the module socket + rules
install. Service now stable; API serves (status/health 200, gated endpoints
401). Data is empty because nDPId's zmq distributor is not run on gk2 (DPI =
ndpiReader/dpi module); the module plumbing is fixed regardless.
Add an 'lxc' zone and interface-based auto-classification: a discovered client
is placed by the interface it was seen on (br-lxc -> lxc, configurable via nac
interface_zones), after explicit nft-set moves but before the legacy JSON
fallback. Applied consistently across /clients, /zones, /summary. Frontend
ZONE_COLOR gains lxc=cyan. gk2: 10 containers in lxc, 3 devices in lan.
/zones reported only raw nftables set members, which are empty for
ARP-discovered clients that default into a zone but were never added to the
nft set, so every zone card showed 0 members. Group discovered clients by
resolved zone (nft set / JSON assignment / fallback) instead.
_parse_arp() filtered every neighbour not in a hardcoded LAN_INTERFACES set
(lan0-3/br0/br-lan/eth0-1), so on gk2 (clients on br-lxc + eth2) the dashboard
showed nothing but the template. Add br-lxc to the default set and read extra
interfaces from nac config lan_interfaces. gk2 config adds eth2 → 13 clients.
Apply the /shared/hybrid-dark.css overlay + hybrid-dark body class to the 10
non-crt-light panels the fleet pass skipped (soc, waf, threat-analyst, health,
security-posture, webradio, master-link, metoblizer, proxypac, sentinelle) via
a universal inject-before-</head> + body-class swap. Flagships (certs/wireguard/
users/nac) stay bespoke; frigate is a redirect stub (left as-is).
Add /shared/hybrid-dark.css: a shared overlay that remaps the design tokens
(consumed by crt-light.css, sidebar-light.css and every module's inline styles)
to the cyan hybrid-dark palette from /certs/ + /wireguard/. body.hybrid-dark
(specificity 0,1,1) beats :root and inline :root, so one token remap flips the
whole tree — no per-panel rewrite. Each panel gains the overlay <link> + the
hybrid-dark body class (2-line additive swap; crt-light.css kept as base).
See .claude/WEBUI-PANEL-GUIDELINES.md.
Panels with fully bespoke styles (certs/wireguard/users/nac already hybrid;
~14 non-crt-light custom panels) are untouched.
Convert /nac/ from light crt-light to dark-cyan hybrid-dark (certs/wireguard):
shared #sidebar, Courier Prime, cyan palette, stat grid + tabs (Clients/Zones/
Quarantine/Parental/Alerts/Policy/History) + dense client rows with state
border. Exposes the full NAC API surface (was clients+quarantine only).
Functionality unchanged (same endpoints/methods/bodies). Security: esc() on all
API values, data-act delegation (mac/zone never interpolated into onclick),
sbx_token bearer, 401 toast.
Convert /users/ from light crt-light to the dark-cyan hybrid-dark convention
(certs/wireguard): shared #sidebar, Courier Prime, cyan palette, stat grid +
tabs (Users/Sessions) + dense list rows, toast/modal. Functionality unchanged
(same endpoints/methods/bodies). Security: esc() on all API values, data-act
delegation (no username/session-id interpolated into onclick), sbx_token only
(dropped jwt_token loop), 401 toast. Pre-existing /import response-shape
mismatch left untouched (behavior-preserving) — flagged for follow-up.
- git_commit_push(): stage-all + commit + push origin HEAD, best-effort
(no repo / no changes / push-rejected never raise; commit kept local)
- /site/{name}/upload wires it under the per-site lock, off the event loop
- postinst rewrites the push URL to Gitea's internal LXC endpoint for the
service user (public host NAT-hairpins → pack transfer stalls); seeds
known_hosts. Verified live on gk2: upload → commit → push = ok
Bringing wg-admin down from the panel cut a user's own access with no way to
bring it back from a panel they could no longer reach. Warn explicitly on
admin/mesh bring-down that it may sever access to SecuBox.
Root cause of 'surf stops when wg-toolbox VPN activated' after a reboot: the
persistent nft drop-in (loaded atomically by nftables.service via
/etc/nftables.conf include) failed to load, so the wg-toolbox DNAT never
existed after boot and all tunnel traffic was blackholed.
Two boot-time aborts, both fixed:
- referenced 'inet filter input' which does not exist on gk2 at load time →
fold the UDP-51820 handshake accept into the wg-toolbox table's own input
base chain (self-contained, no external table dependency)
- used iif/oif "wg-toolbox" (resolves an interface index at LOAD time, fails
when wg-quick@wg-toolbox has not created the iface yet) → iifname/oifname
(per-packet name match, load-order independent)
nft -c -f /etc/nftables.conf now passes; the DNAT survives reboot.
- _parse_wg_show: parse all-dump by field count (5=iface/9=peer), drop private_key
(sudo route had made it leak 48 bits via /stats,/summary,/peers/status);
also restores peer parsing (old detection misclassified every peer line)
- webui: data-attr + delegated click handlers; guard peer-drawer loading flicker
- api: clear read cache after up/down/add/remove so mutations show immediately
Add a "LAN clients (from NAC)" subsection to the Tor-VPN selective-routing
panel in both /tor/ and /toolbox/#tor webuis. Lists NAC clients that are
online and not quarantined, cross-referenced against the current
/toolbox/vpn/clients selectors to derive each toggle's on/off state.
Toggling calls the existing POST/DELETE /toolbox/vpn/client {kind:"ip"}
endpoints and refreshes both lists. IPv6 client IPs are filtered out
client-side (VPN backend is v4-only, Phase 1). Existing free-text
add-selector control (MAC/CIDR/off-NAC clients) is unchanged.
Swap the standalone MESH-blue charter in reality.html for the fleet
crt-light/P31-phosphor skin (Courier Prime, --p31-* palette,
.main{margin-left:220px}) matching secubox-tor and other webuis, and
replace the bespoke breadcrumb sidebar with the shared sidebar.js/
sidebar-light.css injector so /reality/ appears in the common navbar.
All panels (server list+create, validate-target, apply, clients+link
QR, volume-guard config+stats) and their JS/fetch wiring are unchanged.
Add menu.d/562-reality.json (mesh category) and install it via
debian/rules, mirroring secubox-tor. python3-segno was already
declared in debian/control.
APIRouter(prefix="/api/mesh/reality", dependencies=[require_jwt]) —
router-level JWT gate on every route. Servers/clients CRUD,
validate-target, apply (force-overridable sanity gate), link (vless://
+ QR), guard get/put, stats. All subprocess-touching handlers are
plain def so FastAPI threadpools them off the event loop. main.py adds
the uvicorn ASGI app target (secubox_reality.main:app) — not in the
original file list but required to serve the router.
xray api statsquery via loopback StatsService, samples stored in
SQLite; per-window delta = max-min across samples (cumulative
counters). Soft warn / hard disable (stop-not-destroy). CLI entry
point for the 5-min systemd timer.
Plain-def systemctl wrappers (start/stop/enable/disable/restart/status)
for the hardened templated unit; stop_instance() never disables/destroys
so the volume-guard DISABLE action preserves IP/keys.