Trust
An agent does not just answer — it runs real actions for you, and a web page or a message can try to trick it into misusing the access it already has. Polychrome's answer is one check on every action, and reading anything from outside your conversation takes away the powers that could send your data back out.
You could try to catch these tricks by scanning the text for attacks. That does not work well. Reword the attack and it slips through. So Polychrome takes a different route: it takes the risky power away, and the action simply is not available. The moment your conversation reads outside content, the agent loses the ability to reach out or act beyond the chat. An action that would have run a second ago now stops and asks you. The rest of this page shows how. For the full decision and the research behind it, see the capability-gate record.
Capabilities
Every action needs a set of capabilities — permissions for the specific things it does. Reading a file in your workspace needs permission to read local files. Fetching a web page needs permission to reach an address the agent picked. Sending a message or opening an issue needs permission to change something outside the chat. Your conversation is granted its own set of permissions. Polychrome compares the two, and one of three things happens:
- the conversation has everything the action needs, so it runs
- the action needs a permission the conversation no longer has, so it stops and asks you — and says what it needs and why
- a firm rule says no, so it is turned down with no prompt
There is one path for every action, not a pile of special cases. If Polychrome cannot tell what an action needs, it assumes the most and asks. A check when the code is built makes sure every built-in action is labeled, so a missing label is caught by a developer, not by you in the middle of a chat.
Outside content withdraws capabilities
As soon as your conversation reads something from outside — a web page it fetched, or a message from someone else — it loses two permissions: reaching an outside address, and changing anything beyond the chat. Both are ways the agent could send data somewhere an attacker can see it, so Polychrome takes them away together. Everything else keeps working. Reading your own data still runs with no interruption. But fetching an outside link, or sending a message, now asks you first. Polychrome re-checks this on every action, so content that arrives in the middle of a turn takes the permission away for the very next step.
How does it know? It tracks where each piece of content came from, kept in a log it can trust. The things that decide the check — the rules, the agent's setup, the label on each action — live outside your conversation, where the agent cannot touch them. So the agent cannot hand itself more power by writing clever text. The derivation-inputs record lists every input and where it lives.
Asking a person is the exception
Asking a person is the weakest link. A system that asks all the time trains people to click "yes" without looking, which defeats the point. So the real protection is taking the power away, and Polychrome is built to ask rarely. It tracks how often it asks, right next to its other numbers. If it starts asking a lot, that counts as a bug in the rules or the labels — not as safety doing its job. When you approve an action, you can choose not to be asked again. That choice is tied to you and to that one action, and covers no more than what you approved.
Granting and removing access
Three admin actions change who can use Polychrome at all: inviting a person, removing a person's access, and removing a person's admin role. Each one always asks a person first. There is no state in which any of them runs on its own — the power they need is one Polychrome never grants, so the check always lands on "stops and asks you." The approval card names the exact person. The agent can propose the action in plain language, but it never reports it as done until a person approves it and it actually runs. Polychrome owns that status, not the agent's words.
| You say | What happens | Guard rails | Undo |
|---|---|---|---|
| "invite @person" | Polychrome mints a single-use code and delivers it to the person privately — never to a channel. | Admins only. The code works once and expires. | An unused code expires on its own. |
| "remove @person's access" | Polychrome severs every way the person is linked and shuts them out entirely. | Admins only. Never the last admin, and never yourself. | Invite the person again. |
| "remove @person's admin role" | Polychrome takes the admin power away and nothing else. The person keeps their access. | Admins only. Never the last admin. | Another admin grants the role again. |
Removal is forward-looking: it stops the person from here on and erases nothing. The record of what they did stays exactly as it was. Polychrome reads the admin count from the log itself, so "never the last admin" holds even for someone who became an admin before this check existed. A deployment never ends up with no one in charge.
A second check where data leaves
The built-in fetchers cannot open a connection on their own. They send the request to the control plane, which is also the part that knows where your content came from. So the fetcher itself refuses to make an outside request for a conversation that has read outside content. It is the same rule as the check, enforced a second time — right where data would actually leave. If the first check ever has a bug, this one still holds, because the agent's own text cannot reach it.
Recovery
Reading outside content is not permanent. The log remembers where each piece of content came from, so an admin can clean a conversation up. This is called excision: remove the outside content, and the permissions come back. They come back because the facts changed, not because the rule got weaker. The log only grows — nothing is erased — so cleaning up deletes nothing. It adds a signed note of what was removed, who removed it, and why. The agent can never do this itself. Excision also removes what the agent wrote from the outside content, so the attack cannot sneak back in. And starting a fresh conversation is always the simple option.
# Clear the outside content a conversation took in; its web and
# outside access recover.
polychrome conversation excise <conversation-id> --all \
--actor slack:T…:U… --reason "reviewed, benign"The signed note and how excision is triggered are worked out in the excision design record. To see how spending from a wallet uses the same approval step, read Payments. For the log that carries all of this, read Architecture.