sharness.ai / security / threat model · this page as markdown

Threat model

Status: written down, as of 25 Oct 2026; the code it describes is at the experiments stage. This page is the long form of the table on Why. It names what SHarness protects, who it protects it from, where the trust boundaries are, and, surface by surface, what the code that exists today does about each threat and what only the designed launcher will. The CLAUDE.md rule holds throughout: the kernel is trusted; root and kernel compromise are out of scope.

1. What is being protected

AssetWhere it isWhat losing it means
a secret the agent's work needs: a vault key, a model API key, a tokentoday: on disk, on argv, in the transcript. Designed: in one process, sharness.kms, sealed everywhere elseanyone who reads the transcript or the disk has it, for as long as it is valid
the person's data: the workspace, a vault's contents, the files the agent may readthe machine the agent runs onsent to a host the agent was told to reach; destroyed
the person's authority: what the agent may do on the person's behalf (push, publish, pay, delete)the credentials and the network reach the agent hasthe agent does it because a page told it to
the machinethe host, its other users and processesa dropped file runs; a fork bomb; a socket to a daemon
the evidence: what this site says was measuredthis site, the CLI's outputa pass the policy did not earn makes a reader trust what did not hold
the project's supply chain: the package on PyPI, the files on this site, the CI that makes themGitHub, PyPI, GitHub Pages, a CDNa reader installs or runs something the project did not write

2. Who, and with what

ActorHasWants
a prompt-injected agenteverything its process can do: commands, files, network, editing a policy, printing a secretwhat the injected text asked for, with the agent's whole grant
untrusted content in the workspace: a cloned repository, an archive, a vault's fileswhat a file can be: a symlink, a package folder with a familiar name, a hook, a file named like a policyto run as the agent before any policy applies; to make a policy mean more than its text
a tool or MCP server the agent startswhat its process can dothe same, one step down the tree
a compromised component (designed: fs, net, llm, bash)what its policy allows, and a reason to try what it does notthe KMS; the other components' files; the network
a party on the networkthe answers to the agent's requests; a host name the injected text can mentionthe data; the next injection
another user or process on the machinethe shared places: /tmp, Unix sockets, the cgroup treeto tamper with what the child reads or writes
a compromised upstream: a CDN, a package index, an action's repository, a mirrordifferent bytes at the same addressto run on a reader's machine, in CI, or in the job that publishes
root, or a kernel buganythingout of scope; the microVM around the harness is the answer (Containment §2)

3. The two trust boundaries

 the person ───── supplies sha256:M by a channel the agent cannot write to ─────────────────────
                                           │
                                           ▼   boundary 1: the hash. Below it, text is data.
 the launcher (designed) ── verifies M; applies every policy from ABOVE the agent: a Boundary for all
   └ the agent's session ── reads untrusted text, runs tools. Today it applies policies ITSELF:
        │                    a Boundary for its children, a Setting for itself
        └ a confined child (sharness-ai run, today) ── Landlock paths and ports · namespace ·
                                                       rlimits · cgroup · no_new_privs
                                           │
                                           ▼   boundary 2: the kernel. Trusted. Root is inside it.
 the kernel, the hardware, the platform's VM

Boundary 1 has a precondition the design states and this page repeats: the person must obtain sha256:M independently of the agent. A hash read off the agent's own output and pasted back was chosen by the agent. The browser mockup and the playground exist so the person can compute the hash from the file themselves, on their own machine.

Boundary 2 is the kernel. Everything SHarness enforces is a kernel mechanism (Landlock, namespaces, cgroups, rlimits, seccomp when it comes) or a cryptographic one between processes (mutual TLS, sealing). A process inside a policy that finds a kernel bug is outside the model. A process that is root inside the same kernel is the one the policy is a Setting for.

4. Surface by surface

"Today" is sharness-ai run and hello on the dev branch as of 25 Oct 2026. "Designed" is the launcher and components, not built. Barrier kinds are ABP's: Boundary (enforced above the subject), Setting (the subject could flip it), Expectation (prose). The register row for each gap is on Known risks.

SurfaceThreatToday, for a confined childDesigned, for the agent itselfNot covered by anything, and said so
files: readthe child reads a secret, another user's data, another process's environment through /procLandlock: the listed paths only; hello lists neither /proc nor /devthe same from the launcher; per-component uids as a second layera listed directory is listed whole (SD-08)
files: writethe child writes outside the workspace, or somewhere it can later run fromLandlock: the listed paths only; a writable path never carries the execute right (SR-01); a writable path that resolves elsewhere is refused before anything runs (SR-02)sharness.fs as the only writer, with content ruleswhat it may write, it may write; /tmp is shared (advice)
files: execthe child runs what it fetchedexecute only beneath the exec list; write and exec kept apartthe process-tree execute allowlist through sharness.bash (E06, not run)an interpreter on the exec list runs code from a writable path. Landlock alone cannot say otherwise; the dual-LLM split and sharness.bash are the design's answer
network: TCPexfiltration; the host the injection namednetwork: "none": an empty namespace where one can be made, else Landlock denies every TCP connect (ABI 4+); ports as Landlock port rules; if neither is possible the command is refused (SR-03)sharness.net as the only client, with host, endpoint and id allowlistsv0 says ports, not hosts (SD-03)
network: UDPDNS as a channel outthe namespace, when it appliedthe namespace from the launcherLandlock does not see UDP (SD-04); said on stderr
network: Unix sockets by patha Docker daemon, an X server, a local MCP server, a local proxynothing (SR-08)a mount namespace that hides them; a uid that cannot open themsaid on stderr every run, in policy check's advice, and in hello
processesfork bombsRLIMIT_NPROC for a non-root uid; a pids cgroup where one can be made; for root with no cgroup the policy is refused (SR-04)per-component uids; RLIMIT_NPROC=0 where a component never forksRLIMIT_NPROC counts a uid's processes machine-wide
memoryexhaustionRLIMIT_AS always; a memory cgroup where one can be madethe cgroup from the launcheraddress space is not resident memory (SD-05)
privilegesetuid, capabilitiesno_new_privs before the rulesetan empty bounding set; a throwaway uida child that is root stays root inside its policy
descriptorswhat was open before the policy stays open after itevery descriptor above 2 closed before the policy applies (SR-10)the launcher opens nothing for a componentstdin, stdout, stderr are the caller's
environmentsecrets in the environment reach the childnothing; declared on stderr every run (SR-11)a process.env row; the KMS and sharness.llm holding the secrets so no process needs themuntil then every child sees every variable
signals and IPCthe child signals or traces the harnessLandlock scoping of signals and abstract sockets (ABI 6+); ptrace outside the domain refuseda pid namespaceSystem V IPC; shared memory within one uid
the policy texttext that reads as one thing and binds anotherrefused (SR-12 to SR-16): normalised printable paths, one value per key, integers JavaScript keeps exact, no lone surrogatesthe same in the launcher; signing by the root keywhat the policy means: the hash names it; advice remarks on it; nobody judges it
the applying processit is the agent, and could skip the stepnothing: the Setting caveat, printed every hello (SD-01)the launcher above the agentthis is the design's argument, not a gap it can close from inside
the evidencea refusal counted when the environment, not the policy, refuseda control run without the policy for every ambiguous refusal; inconclusive reported (SR-09)the launcher's acceptance testone machine, one day; every figure on this site carries its date

5. The project itself

The product is a claim made by two repositories and a package index, so the project's own surfaces are in the model.

SurfaceThreatWhere it stands, as of 25 Oct 2026
the release pipelinecode that is not the project's runs in the job holding the PyPI tokenfixed (SR-05): GitHub's and pypa's actions only, pinned to commits; build and publish in separate jobs; PEP 740 attestations (Supply chain §3)
the branchesa push to main publishes; main and dev are unprotectedopen (SR-24): a setting (Hardening §3)
the browser benchthe guest's packages downloaded unverified; the chroot form runs them as rootfixed (SR-07): every download pinned by sha256 and refused on mismatch
this sitea third-party script changes what a page showsthe storyboards kit loads with its integrity string (SR-20); Pyodide from jsDelivr cannot carry one (SR-25, accepted, with what a compromise could and could not do)
the code published on this sitea file at a published path changes after a reader pinned itfixed by construction: immutable release paths with a manifest of hashes; the gate fails if a byte changes (Supply chain §5)
the agents that write herean agent following injected text in something it reads, or a leaked session tokenthe same rules as any contributor: a pull request, the gate, the register; the lock-down list asks for required reviews on main

6. What the model leaves out, on purpose

Sources: Why: threat model; Policies §2 and §8; Containment; What a process can do; the review on Reviews; the kernel's Landlock documentation for what Landlock does and does not hook.