Coding agents keep deleting production because they're handed credentials. Taper is a locked box that holds the keys and only carries out the specific, narrow operations an agent is allowed to ask for — and a helper can always be given less, never more. As of v0.2 the token also says who it acts for, the database gets a say before a write, and Tower mints a sixty-second credential per operation instead of keeping one. This page runs the real library in your browser.
A signed list of typed operations with constraints — ssh.exec on this host running this program, pg.query of this statement kind on these tables. It only ever narrows: a subagent gets a strictly smaller one, signed offline, and a widening block is rejected structurally rather than by policy.
A separate process under a different Unix user holds the real credentials. It checks proof of possession, validates the typed request, evaluates policy, and performs the operation itself. The credential is never in the agent's memory, environment, or shell.
Taper is never the only thing saying no. The database role can't ALTER; sshd runs a force-command. And before a write, the broker asks the database for its own invariants — no backup since Tuesday, three views depend on this — and proceeds only if the grant named each one. A wildcard never does.
The next track. Instead of holding a database password, the broker holds a CA and mints a sixty-second certificate per operation, with the human's name in it, only after re-verifying the chain itself. The database has no password to steal. Stage 1 shipped in v0.2; the design removes the vault entirely.
Today's agents get the hotel's master key: it opens every door, and if the agent is confused or tricked, the wrong door opens. Taper is the front desk. The agent never touches a key. It says "open room 412," the desk checks its list, and a staff member opens that one door. The agent can hand a helper a shorter list — just 412, just for five minutes — but can never write a longer one. Every request, allowed or refused, goes in a ledger where tearing out a page leaves a visible gap.
Not a sandbox (it mediates the paths that go through it, nothing else), not a model guardrail (no LLM in the decision path), and not audited — about two thousand lines of Python by one person. The measured result so far is deliberately modest: in a rebuild of the PocketOS incident, an agent with a credential and an agent with a token both did the job, ten out of ten. Taper doesn't stop an agent working; it bounds what else it could have done.
Nine steps, one property each. Every button drives the unmodified taper-broker package — nothing leaves this tab. Two steps need a database to answer, and a stand-in plays that one part; the page says so where it happens.
A policy is an operation plus a constraint per field. This is the widest this token will ever be — everything downstream is a subset of it.
any is called out: that is policy pressure, made visible at the moment it is written.The helper gets one host, one program, one argument, five minutes — and pg.query disappears entirely. No server is contacted: the parent signs a new block with an ephemeral key.
Order matters: proof of possession first, then the typed schema, then policy. A refusal quotes the constraint that refused it. An allowed request shows the exact argv the broker would execute.
validate/redteam.py, including the four that got through the first time. For step 5, untick with proof of possession and send again: that's someone who copied the token text out of a log.Revocation matches any id in the chain, so revoking the root kills the subagent too. TTLs only shrink down the chain. And the audit log on the right is hash-chained — delete a record and it tells you where.
A token can be exactly right and the write still wrong, because only the database knows whether this table has a backup from this morning. Before a write, the broker calls a function the database owns and proceeds only if the grant names every invariant it raises. The agent has no say; a wildcard never counts.
pg.migrate grant names production in its invariants. Click Migrate with the database raising only production: the write proceeds and the override is on the tape. Then tick stale backup so it also raises no_recent_backup, which the grant does not name, and click again. The database here is a stand-in that answers the invariants probe; the broker, executor, refusal and audit record are the real code.taper audit --refusals sorts every denial in the log by kind. The policy bucket — well-formed requests the grant did not cover — is the policy-pressure metric from the design document. Attacks are counted apart, so the report can never argue for widening a grant to admit one; refusals by the target are apart again, because the answer to those is "read what it said".
A credential that exists because a decision caused it to. The tower re-verifies the chain and the proof with its own state, refuses a decision about any other chain or subject, and mints a client certificate valid for sixty seconds: CN the database role, OU the human, serial derived from the clearance. The executor connects with it — and with no password, because there is none — and it is gone.
clearance record sits between the decision and the result, and the tower holds nothing afterwards. Try it again after step 6's revoke. The certificate and its issuance are real; the database it connects to is the stand-in.