Umbrella: #396 (portal-boundary hardening). From the 2026-06-18 security audit, action C.
Problem
The token is all-or-nothing. A phone whose only job is push-to-talk holds a credential that can open GET /ws/terminal/{name} → tmux attach → a root-equivalent interactive shell. A leaked PTT token = full RCE on the dev box.
Scope
- Per-device capability scopes: at minimum
full vs ptt.
- Middleware maps device → scope → route allowlist.
ptt scope may transcribe + send to a whitelisted session only — no terminal WS, no create/recreate/spawn/fork, no config writes, no safety-config, no scheduler-run.
Verification
Pair a ptt-scoped device; confirm it can transcribe + send to its whitelisted session but gets 403 on /ws/terminal/*, /api/create, /api/config, /api/safety/config. A full device retains everything.
From #396 audit (action C). Depends on per-device credentials. This is the change that shrinks a leaked phone token from RCE to "can talk to one session."
Umbrella: #396 (portal-boundary hardening). From the 2026-06-18 security audit, action C.
Problem
The token is all-or-nothing. A phone whose only job is push-to-talk holds a credential that can open
GET /ws/terminal/{name}→tmux attach→ a root-equivalent interactive shell. A leaked PTT token = full RCE on the dev box.Scope
fullvsptt.pttscope may transcribe + send to a whitelisted session only — no terminal WS, no create/recreate/spawn/fork, no config writes, no safety-config, no scheduler-run.Verification
Pair a
ptt-scoped device; confirm it can transcribe + send to its whitelisted session but gets 403 on/ws/terminal/*,/api/create,/api/config,/api/safety/config. Afulldevice retains everything.From #396 audit (action C). Depends on per-device credentials. This is the change that shrinks a leaked phone token from RCE to "can talk to one session."