# Lockdown Behavior This page is the exact behavior reference for what lockdown gates, and what continues to work. ## Chrome and Menus - The Project and Workspace taskbar buttons never render while locked, and their click handlers and menu executors refuse even if a stale click arrives. - Workspace name tabs are hidden unless `:chrome :full`; clicking them still refuses because switching is pinned. - Minimised-restore badges are hidden at `:chrome :none` and `:chrome :app`. - Panel title-bar close ✕ and minimise ▾ buttons disappear, as does the group collapse button. Their hit-tests return nothing. - The right-click group border menu does not open. Focus movement, panel interaction, app menus, dialogs and tooltips are unaffected. ## Single-App Presentation (`:chrome :app`) `:chrome :app` is the most aggressive chrome level: it removes the workspace entirely and leaves one app below its own title/menu strip. | Element | `:chrome :none` | `:chrome :app` | |---|---|---| | Project/Workspace buttons | hidden | hidden | | Workspace name tabs | hidden | hidden | | Minimised-restore badges | hidden | hidden | | Bottom taskbar row | reserved (empty) | not reserved | | Group title bars / borders | drawn | none (one panel) | | Panel-name palette | opens on app-name click | disabled (hotspot inert) | | App title/menu strip | drawn | drawn (close/minimise buttons hidden) | | Docks / pop-out | off | off | ### Launch and layout - Named workspace configs are ignored. The app comes from the CLI `retra/retro app <ref>`, then the policy `:app`; the launcher is never a fallback. - The policy `:app` source is exempt from the `:apps` allowlist by path (like the vendor launcher). After the initial launch, `[tui/launch]` and `[tui/adopt]` cannot displace the running app. - `:layout true` does not re-enable layout mutation under `:chrome :app`; the single panel is the session. ### Lifecycle - `[tui/close]` and `[surface/quit]` on the sole app request a host quit; the host also exits when the app's thread dies. - A failed initial launch (bad source, refused by `:images-only`, …) leaves the host up with the error in the log / error console — it does not fall back to the launcher and does not exit. - Ctrl+Shift+Q (both hosts), Ctrl+Q (`retra`) and the window X (`retro`) quit as usual. App menus, dialogs, focus, pub/sub and every runtime feature listed under What Still Runs below behave exactly as in a normal locked session. ## Fixed Layout Every structural mutation is refused: | Action | Mouse | Keyboard / prefix | Stdlib | |---|---|---|---| | Resize split borders | drag refused | `+`/`-` prefix | `[tui/resize]` | | Split panel | — | `v` / `h` prefix, pie | `[tui/split]` | | Close panel | ✕ button hidden | `x` prefix, pie | `[tui/close]` | | Drag / reorder panels | title drag refused | — | — | | Zoom | — | `z` prefix, pie | `[tui/zoom]` | | Maximise group | — | `↑` prefix, pie | — | | Collapse / minimise | button hidden | double-click refused | — | | Add to group | border menu hidden | — | `[tui/add-to-group]` | | Pop-out | palette item dimmed | `p` prefix | `[tui/pop-out]` | | Dock regions | — | `d` prefix | `[tui/dock/*]` | `popout_enabled` and `docks_enabled` stay off in lockdown, so the pop-out/dock subsystems are inert. The vendor's own workspace spec is still applied at startup — lockdown pins the layout the vendor declared, it does not prevent the initial spec from building splits and groups. (`:chrome :app` is the exception: it ignores workspace specs and pins the one panel.) ## Launcher and Apps - The launcher source is exactly the policy `:launcher` path; the stdext fallback chain is suppressed. - `[tui/launch]`, `[tui/adopt]` and the app selector all consult the `:apps` allowlist before starting anything. The vendor launcher and the policy `:app` source are exempt (matched by source path) so they can always start; everything else goes through the allowlist. - If the workspace config opened no apps, the policy `:app` runs first, then the vendor launcher. Under `:chrome :app` the launcher is suppressed entirely — the session needs a CLI `app <ref>` or `:app`, and a failed launch leaves the host up with the error visible. - With `:images-only true`, loading a source `.dft` app fails cleanly in the shared app loader; only `.dftc` AOT images are accepted. Note that the launcher UI itself is a Deft app. Lockdown enforces the allowlist at the host launch verb, not in the launcher's own list rendering — a badly written vendor launcher can show an app the host will then refuse to start. (Under `:chrome :app` there is no launcher UI at all.) ## Project and Workspace Pinning - Project switching (Project menu, directory dialog, restart-in-dir) refuses. - Workspace switching (tabs, workspace switcher) refuses. - New / Save / Save As / Remove Workspace refuse, including the Save As dialog result path and the remove confirm. - The host does not record the launch directory in the recent-projects list while locked — a pinned device writes no user config. ## What Still Runs Lockdown controls the interface, not the language runtime. Inside a locked host: - Apps run normally: drawing, input, widgets, dialogs, pub/sub, RPC, config, workers, HTTP, and so on. - `[tui/focus-id]`, focus chords and focus-by-index still move panel focus. - Theme cycling (`Ctrl+B t`) and app-level behavior are unaffected. - The debug server, `eval`, `reload`, the filesystem, and the shell remain available to app code. ## Security Posture UI gating is a commercial/UX control, not a security boundary on its own. An attacker with a shell can start the host without `--lockdown`, call the stdlib directly, or attach to a runtime. Making lockdown tamper-resistant is a layered job: - Runtime lockdown (planned): disable `eval`/`reload`, source loading, package installs, config writes and shell execution inside a locked runtime. - AOT images + `:images-only` (available): ship code as `.dftc` so there is no source to edit on the device. - Signed images and licenses (planned): verify origin before load. - OS confinement (available): the Landlock sandbox (`DEFT_SANDBOX=1`), a read-only root filesystem, a dedicated user, and no shell in the boot path. See Deployment for how to combine these.