# 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.