Security model
The gate enforces; the human decides; the agent can neither reach the console nor change the policy.
Pre-release. lockhttp v0.2.0 is out as a pre-release and runs in Docker only (one lockhttp launcher, a cosign-signed image): command names are fixed, flags may still move. TLS decryption ships in it and is optional, wired by lea: without a local CA and the signer (--ca-cert, --signer), HTTPS is decided at the domain. Design decisions: docs/decisions.md, contract with lea: docs/lea.md.
Non-negotiable requirements
Console in its own container
Under its own account (
lockhttp), the only container with the policy volume. The agent gets neither its socket nor the Docker socket.NeverAuto by default
Only an approved
autorule, scoped to a container, can be answered by auto mode, and the console re-checks the rule against its own approved policy first.Identical fingerprint
One human decision covers only requests with the same container, method, URL, significant headers and normalised body.
Claude API allowlist
Once TLS is decrypted,
mcp_servers, server tools other thanweb_search, files and foreign credentials never pass without a human.Fail closed
No console, an unknown source, an unreadable source map, a policy that does not decode: ASK is refused, errors deny.
Threats and answers
Assets: the project’s and the host’s data, credentials, and actions taken online in your name. Attackers: the agent itself (prompt injection, drift) and hostile web content. Out of scope: a compromised host, Anthropic, a network attacker upstream.
| Threat | Answer | What remains |
|---|---|---|
| Going around the proxy | Agents sit on internal networks; squid listens on loopback; the gate refuses its egress interface. | Rootless Podman without an isolated gateway: covered by a blocking isolation test in lea. |
| DNS, DoH, IP literals | Non-forwarding DNS in lea; the network floor; no UDP. | IP literals of declared internal hosts. |
| Writing to a trusted host | A write asks, even on a trusted host. | Without decryption the gate sees only the host; the screen says “reads and writes”. |
| Exfiltration in a GET | Decryption, and the exfiltration score (logged in shadow by default; heuristics = "enforce" asks). | Slow, low-bandwidth channels; spliced hosts. |
| The Claude API as a relay | Path and parameter allowlist (with decryption). | Without decryption, the exact host only. |
| Approval fatigue | Auto rules you approved; identical requests grouped; shadow mode to tune first. | The human factor. |
| The agent edits the policy | The policy lives with the console; loosening needs your review; project files are ignored. | — |
| A compromised gate | Own uid, never root; no decision authority over ASK; the CA key in a separate signer. | FREE traffic. |
| Agent impersonation | Rules apply to containers, never to agents; the agent name is audit only. | Attribution between agents of one container. |
| Prompt injection through the screen | Terminal sanitising; the agent’s stated intent is marked untrusted. | — |
| No console | ASK refused at once; FREE on the last approved policy. | Reduced availability, on purpose. |
Audit
The gate writes FREE and DENY to a daily JSONL file, kept 30 days; the console writes every ASK to its audit log. Both carry the same request_id, the container, the declared agent and how it was attributed. The audit never holds a body or a secret.
The container is known for sure (one internal network per container). The agent is an attribution, not an authentication: agents of one container share a uid.
Report a vulnerability
Please do not open a public issue. Report it privately as described in SECURITY.md (a private GitHub security advisory).