docs
Bounds

Trust

An agent that runs unattended will eventually read something a stranger wrote. So every event carries a trust level, and only an operator's is rendered as an instruction.

Three levels

Only one of them may tell an agent what to do

operatormay instruct — an operator wrote it: the control socket, or a schedule an operator set
participantdata, attributed — a known human in a channel — someone else in the Telegram chat
publicdata — a webhook payload; anyone on the internet

Everything that is not from the operator arrives fenced and introduced as data, in one place, so a new channel adapter cannot forget to do it. The fence nonce is random and chosen after the content is written, so nothing inside can close it.

Enforced more than once

Because there is more than one way to launder authority

a webhookMay not carry operator trust, however well signed. The secret proves which system sent the request, never that a human meant what is inside it.
TelegramMay, and only from the one account that pressed the pairing link. Everyone else in the chat is a participant, however the message is worded and whoever it claims to be from.
mailMay, and only from an address that paired, and only when the receiving provider's own Authentication-Results says DKIM and DMARC passed aligned with the domain it claims.
a wakeupAn agent may schedule itself, but not with operator trust. Otherwise one successful injection is permanent.
the socketThe one door that carries operator trust. It is 0600, and reaching it already means holding a file the operator owns.

The wakeup rule is the least obvious and the most load-bearing. If an agent could schedule itself with operator trust, one successful injection would be permanent: the injected turn schedules a wakeup that instructs, and the agent goes on instructing itself with no attacker present to notice or revoke. Schedules is the rest of that.

And it is visible

Everything not typed at the console wears a mark

> how is the queue looking?
four issues open, none of them blocked.
‹wake› check the queue again
still the same.
‹email› and the build?
‹→ email› green since last night.
‹webhook:github› the nightly build failed on main

The pane gets read back to work out who asked for what, and a line from a stranger drawn the same way as the operator's is the one bug in a chat window that matters. Only what arrives on the control socket is drawn as the operator; everything else is named for the channel it came in on, your own mail included.

What it does not claim

The boundary is the sandbox, and the secrets

The control plane holds the Docker socket, so it is root-equivalent on the machine: the trust boundary is the sandbox around the agent, not the process managing it. And an agent that can run code in a sandbox and reach the internet can send what it read to somewhere you did not choose — that is true of any grant broad enough to be useful, and it is why the boundary that carries weight is the one around the credentials rather than the one around the addresses.

One container per agent, not a microVM — because if self-hosting needed microVMs nobody would run it. It is not a boundary to put hostile code inside.