Security model

The rule: the agent only ever sees 1s and 0s about a file.

The human approves every plan in a console the agent cannot drive, in an account the agent cannot read. Skills, rules and MCP tool descriptions only guide the agent; the console’s checks are the guarantee. This page summarises the full security model.

Boundaries

Boundaries and what enforces them
BoundaryEnforced by
The agent cannot read the file, the credentials, the plugins or the audita separate OS account lockfile (home 0700); lockfile doctor checks from the agent’s side that opening it fails
The agent cannot drive the consolelockkit’s isolation checks: the console runs in its own login session (or a desktop terminal of its own account, Wayland only), refuses X11 on production, refuses relays
Only the clients group reaches the consolesocket directory /run/lockfile (2750, setgid group lockfile-clients), socket 0660, kernel peer credentials checked on every connection
The agent cannot replace the binary the console runs/usr/local/bin/lockfile owned by root; install refuses a user-writable source unless its sha256 is pinned
A plugin cannot read anything but its input, nor talk to the networkLandlock (execute and read its own binary only; TCP denied with ABI ≥ 4), seccomp (no sockets, ptrace, mount, bpf, keyctl, second execve), rlimits, empty environment, input as a read-only fd 3, stdout capped at 3 bytes, stderr discarded; see the hardening coming in v0.2.1
A plugin answers only its signed questionthe manifest’s question and meanings of 1 and 0 are signed with the binary; marketplace review; a changed question is a loosening the human types to accept
Plugins and releases are what their publisher builtcosign keyless signatures checked through lockkit (index, manifest, binary, lockfile releases); no unsigned mode; a missing cosign refuses
The agent cannot widen the policyno socket method installs, trusts or loosens anything; a change request only queues text for the human

What the agent can see: the exhaustive list

Everything the console sends to the client (socket, CLI, MCP) is one of these, from closed lists. The end-to-end tests walk every response of every method against this list.

Everything the agent can receive
OutputWhy it is not file information
plugin answer 1 / 0the one allowed thing
result: null + plugin_errorabsence of an answer; residual: it depends on the file (a plugin failed on it); at most one symbol per plugin, capped by max_plugins_per_handle
fetch_failedthe plan could not start; residual: says “this key could not be fetched” (missing, too big, denied and network are not distinguished)
handle f_…, plan id pl_…random, from crypto/rand
plugin name, version, official, warning: unofficial_marketplace, question, meaning of 1/0plugin metadata from the signed manifest
profile names and production flag, busylockfile configuration; busy means the console is waiting for the human on another request
approval_denied, approval_timeouta human decision; the reason the human types is never forwarded
refusal codes (E_*, with a static hint)computed from the request and the local install, before or without reading the file; plugin_yanked is in the list but not produced yet by v0.2.0
status: console up, production, auto mode, security level, timers, plugin protocol, sandbox okconsole state

Never sent: size, media type, sha256, key listings, file or path names, page or byte counts, library or OS error text, per-plugin timing, progress during a run, stderr. The client also refuses to forward any console text it does not recognise: an unknown error becomes console error.

Side channels

  • Timing. A run’s response leaves the console only at the next multiple of answer_quantum (default 5 s) after the run started; a fetch failure after approval too. Residual: a plan that takes longer than one quantum shows it took longer.
  • The console window. At desktop security level 1 the agent’s account can read the console’s window. It shows the agent’s own request, the plugin metadata and the outcome codes; causes, sizes and hashes go to the audit only. Residual: the start-up lines show the profile’s location, and a reason you type with a denial (n <reason>) is echoed in the window: do not type file details there.
  • Exit codes depend on the outcome only, never on an answer.

Data at rest

  • Fetched files live in a random 0700 workspace in the console account, deleted on release, on handle expiry, on console exit, and swept at start. Deletion is an unlink, not a secure erase.
  • Credentials: the S3 SDK keeps the secret access key in the console’s memory for the session; core dumps are disabled.
  • The audit log (~lockfile/.local/state/lockfile/audit.log, 0600) is the one place file metadata exists: key, size, sha256, fetch cause, each plugin’s precise outcome, stderr byte count. Never file bytes. lockfile audit reads it, in the console account only.

Sandbox hardening coming in v0.2.1

“The agent only ever sees 1s and 0s” is the design goal and what the wire carries; the v0.2.0 plugin sandbox is not yet complete around it. A review found side channels a plugin could use to pass more than its bit to another process on the machine:

  • SysV IPC and POSIX message queues are not denied to plugins;
  • /proc is visible to plugins unless it is mounted with hidepid;
  • on older Landlock ABIs, signals through fcntl are not blocked.

v0.2.1 denies SysV IPC and message queues and requires /proc mounted with hidepid=invisible. Until then, install only plugins you trust from the official marketplace.

Residual risks

  • A plugin whose code does not match its question (it lies, or answers a content question under a structural name) is mitigated by review, build from reviewed source, signing and the unofficial-marketplace warnings; it is not prevented.
  • Many plugins over one file combine bits: bounded by max_plugins_per_handle and by the per-plan approval that states how many answers the plan reveals.
  • A question can itself be about content (“is the amount above 10 000?”): the official marketplace’s review refuses such questions; an unofficial marketplace may not.
  • fetch_failed and plugin_error each carry one symbol that depends on the file.
  • Fetched files are unlinked, not securely erased (a residual on SSDs and copy-on-write filesystems).
  • Malware running as the console account, or as root, is out of scope.

Read the source documents: