SIGN IN SIGN UP

Give each agent its own token, and stop trusting the body (#34)

* Give each agent its own token, and stop trusting the body

Calling a tool back used to need one deployment-wide secret, with the Bot
and the person read out of the request body. Anything holding that secret
could spend any Bot's grants and write any name into the audit trail. The
trail is the product, and a forgeable trail is worse than none because it
is believed.

Two credentials now, because they answer different questions and one cannot
answer both.

The token says which agent is calling. Issued per agent, held by whoever
runs it, and stored here only as a hash, so a database dump is not a set of
working credentials. Issuing again rotates; revoking leaves the agent able
to talk and unable to reach anything outside a conversation. Null is the
default, which is what a URL somebody pasted should get.

The run assertion says which Bot and which person. Signed by this
deployment, ten minutes long, sent in forwardedProps and handed straight
back. An agent is in no position to assert who it acts for, so it carries
our note instead of making a claim.

They are checked against each other: an agent may only act as the Bot its
token was issued for, so an assertion seen once cannot be replayed by
another credentialled agent. The deployment-wide token still authenticates
the Bots that ship in the box, but it no longer asserts anything.

Driven end to end, not just unit tested. A registered outside agent with its
own token called a granted tool and the audit row named the visitor rather
than "agent". The same assertion presented with a second agent's token was
refused 403, no assertion at all 401, an unknown token 401.

Also fixes a latent migration bug this work uncovered. drizzle.config.ts
still named computer.ts, deleted when computer use went, and did not name
the policy.ts that replaced it, so action_policy looked absent and the next
generated migration opened with DROP TABLE "action_policy" CASCADE. It
destroys the deployment's boundary. The config now names the right file, the
new columns are folded into the single baseline this repo keeps rather than a
second migration, and the baseline was verified by rebuilding the database
from nothing. The demo seed also refuses with an explanation when the roster
has not been written yet, instead of a foreign-key error naming a constraint.

* Keep the surface's own tools out of the agent's loop

Two things the merge dropped, both found by driving it rather than by any gate.

The set naming which tools this deployment runs was not built, so the filter that reads it threw
`ours is not defined` and the run died before it said anything. Green typecheck, green suite, dead
Bot: the variable is only reached once a model actually asks for a tool.

With it back, the reason it exists matters more than the crash. A run is offered two kinds of tool
that no naming rule separates: the Bot's grants, which execute through the policy and the audit
trail, and the components the surface draws. A call the surface owns now ends the run, which is how
a browser tool is supposed to work, the surface draws it or puts the question to a person and starts
the next run with the answer. Running the loop through it instead invents a result: the Bot
apologises for a chart the person is looking at, and an approval card answered on its behalf waits
for a click that can never land.

* Stop the agent credential announcing the wrong product

The token this deployment issues an agent was minted as `okai_agt_...`, which is the other product's
prefix. The prefix exists so a leaked credential can be recognised on sight, in a log or by a secret
scanner, and one naming a product this is not defeats the only job it has. It is `obot_agt_` now.

The panel that issues it, and the code around it, still called a Bot a teammate. That rename
happened everywhere else already.

Found by opening the screen. The security work these sit on was proven through the API, which is the
right place to prove a refusal, and it meant nobody had looked at the thing a person actually uses to
mint one.
D
David McKay committed
3ccd16fb49c74f11c1a10c5e8f616421b0dcad06
Parent: afde20f
Committed by GitHub <noreply@github.com> on 8/20/2026, 4:19:19 PM