# Lockdown Configuration Lockdown is enabled from three immutable sources. All of them resolve once at startup, before the workspace config is evaluated. ## Command Line ```sh retra --lockdown # or --kiosk retro --lockdown workspace kiosk ```` The flag forces lockdown on with default policy values. It cannot be turned off from the prelude or by an app. ## Environment ```sh DEFT_LOCKDOWN=1 retra ```` Any non-empty value other than `0` / `false` enables lockdown. This is the convenient switch for an autostart unit or launcher script. ## Prelude The project prelude (`.deft/prelude.dft`) carries the full policy:
%Prelude{
    :pkgs @{ "/opt/vendor/packages" }
    :lockdown %{
        :active true                       # a map implies active
        :chrome :minimal                   # :full | :minimal | :none | :app
        :layout false                      # false = fixed layout
        :project false                     # allow project switching
        :workspace false                   # allow New/Save/Save As/Remove
        :launcher "acme/pos/pos.dft"       # vendor launcher (false = none)
        :apps @{ "pos" "inventory" }       # launch allowlist (app names)
        :images-only true                  # refuse source .dft, .dftc only
        :app "acme/pos/pos.dft"            # initial app instead of launcher
    } }
`:lockdown true` is shorthand for the defaults. The CLI flag and `DEFT_LOCKDOWN` can start the host locked even when the prelude only carries field settings. ## Policy Reference | Key | Type | Default | Meaning | |---|---|---|---| | `:active` | bool | `true` in a map | Master switch. | | `:chrome` | keyword | `:minimal` | Taskbar chrome level; `:app` is the single-app presentation. | | `:layout` | bool | `false` | Allow layout mutation (resize/split/close/drag/zoom/pop-out/dock). | | `:project` | bool | `false` | Allow switching the project root. | | `:workspace` | bool | `false` | Allow New / Save / Save As / Remove Workspace. | | `:launcher` | string / bool | unset | Vendor launcher source path. `false` disables the launcher. | | `:apps` | vector | unset | Launch allowlist (app names). Omitted = unrestricted; `@{}` = deny all. | | `:images-only` | bool | `false` | Refuse source `.dft` apps; only `.dftc` images load. | | `:app` | string | unset | Initial app source launched when the workspace config opened nothing. | ## Chrome Levels `:chrome` controls how much taskbar chrome remains while locked: - `:full` — workspace tabs stay visible (informational; switching still refused). The Project/Workspace buttons never render in lockdown. - `:minimal` (default) — workspace tabs hidden as well. - `:none` — additionally hides the minimised-restore badges, leaving a content-free bar. - `:app` — the single-app kiosk presentation. All workspace chrome goes away: no taskbar row at all, no tabs/badges/group chrome, no panel-name palette. One app fills the host below its own title/menu strip (app name + menus), the layout is pinned to that one panel, the launcher is suppressed and named workspace configs are ignored. The policy `:app` (or a CLI `retro/retra app <ref>`) names the app, and closing it exits the host. The next section describes the full contract. Panel title-bar close/minimise buttons and group collapse buttons are hidden at every level. ## Single-App Presentation (`:chrome :app`) `retra app <ref>` / `retro app <ref>`, or a prelude that names the app, run one full-window app with no workspace chrome. The prelude must name the app when no CLI reference is given:
%Prelude{
    :lockdown %{
        :chrome :app
        :app "acme/pos/pos.dft" } }
Launch `retra` (or `retro`) with that prelude and the app opens full-bleed. `retra app acme/pos/inventory` overrides the prelude `:app` for a one-off session. Semantics: - Chrome. The bottom workspace taskbar row is not reserved — `:chrome :app` is more aggressive than `:none`. Workspace tabs, badges, group title bars and the panel-name palette do not render, and the palette hotspot is inert. The app's own title bar (app name + menu labels) stays, so `[tui/set-menu]` / `[tui/menu-update]` remain reachable; its close/minimise buttons stay hidden. - One app. Named workspace configs are ignored. Launch precedence is the CLI `app <ref>`, then the policy `:app`. The launcher is never a fallback: with neither source set, or when the launch fails, the host stays up with the error visible. The policy `:app` source is exempt from the `:apps` allowlist by path (like the vendor launcher), and once the app is running no other launch can displace it. - Fixed layout. `:layout true` cannot re-enable resizing, splitting, closing, zooming or popping out in this mode; the single panel is the session. - Exit. Closing the sole app via `[tui/close]` (or `[surface/quit]` in `retro`) ends the host, as does the app's thread exiting. A failed initial launch (no app ever ran) leaves the host running so the error is visible — supervise the process and act on the exit code. Ctrl+Shift+Q (both hosts), Ctrl+Q (`retra`) and the window X (`retro`) still quit. - Title. `retro` sets the OS window title from the CLI reference or the `:app` source. For a kiosk that keeps a launcher and several apps, use `:chrome :minimal` / `:none` with `:launcher` / `:apps` instead — `:chrome :app` is deliberately single-purpose. ## Launcher and Allowlist When locked, the launcher is only the `:launcher` source. The built-in stdext launcher fallback chain is suppressed, so a pinned device never scans the package roots for the stock launcher. The launcher itself is exempt from the allowlist (it is matched by its configured source path); everything it launches goes through the allowlist. Allowlist entries match the app names passed to `[tui/launch]` — the package/app names the launcher scans (`"editor"`, `"sheet"`, …). Omitted `:apps` leaves launches unrestricted (chrome/layout lockdown only); an empty `@{}` denies every app except the vendor launcher and the policy `:app` source (both matched by path). ## Initial App If the workspace config opens no apps, the host launches `:app` first; when that is unset it starts the vendor launcher. With both unset and the launcher disabled, the session opens to a blank desktop. Under `:chrome :app` there is no launcher fallback: the session needs a CLI `app <ref>` or a policy `:app`, and a failed launch leaves the host up with the error visible instead of falling back or exiting. The initial app name is derived from the source basename (`acme/pos/` pos.dft` → `pos`). The vendor launcher and the policy `:app` source are` exempt from the allowlist by path; every other launch goes through it. ## Confirming the Policy `retra`/`retro` print the resolved policy to stderr at startup: ``` lockdown: active (chrome=none, layout=locked, allowlist=restricted) ```` `layout` reads `locked`/`unlocked`, `allowlist` reads `open`/`restricted`. ## Scope Lockdown applies to `retro` and `retra` in their workspace mode; the standalone script mode (`retro <script.dft>`) is unchanged. The `:chrome :app` level only takes effect while the policy is active — an inactive policy is never single-app.