The word permission smuggles in a shape. It suggests a switch: the agent may, or it may not. We found out the switch is a fiction one feature at a time — a shared agent here, a scheduled run there, a folder somebody wanted fenced — until the permission code was a set of answers to questions nobody had written down in one place. OWASP's 2025 list of risks for language-model applications names the failure this produces Excessive Agency, and decomposes it into three root causes: too much functionality, too many permissions, too much autonomy. That decomposition is right, and it is still coarse. When we drew our own model on one page, it had eleven axes.
Earlier entries in this log each built a piece of it: why the guarantee against prompt injection sits at the exit rather than the entrance, what happens to consent when nobody is present to give it, which of an agent's memory stores another agent may see. This entry is the regathering. It states what the system is now, whole — including the one place where it is not deny-by-default, and why that is a decision rather than a gap.
Every agent can call every tool
The first decision runs against the instinct the word permission gives you. There is no per-agent list of allowed tools. Every agent is offered every tool the platform has — just under five hundred at the last count — and what separates an agent that can email strangers from one that cannot is not which tools it sees but what happens when it calls one.
We built the allow-list first. Tools were derived from the skills an agent had been granted, connector tools filtered out unless a connector grant existed, the email send withheld from unattended runs by a mode flag. It was tidy and it failed in a way that decided the matter: a workflow stage whose agent needed to send mail reported, fluently, that no email connector was available in this stage — because the tool had been filtered from its list — instead of pausing to ask. An agent that cannot name a tool cannot ask for permission to use it. The list had converted a consent question into a silent incapacity.
So availability became universal and enforcement moved to the moment of use. A platform-owned table names every tool that requires a grant and what shape of grant it needs; a hundred and fifteen tools are in it, and membership is the gate — a tool absent from the table is free. When an agent calls a tool in the table without the grant it needs, it pauses and asks. When it holds the grant, the call's arguments are checked against the grant's value before anything runs. Least privilege is still the property; it is simply enforced at the point where the privilege is exercised rather than at the point where the tool is registered — trust on first use, remembered per agent, asked exactly once.
A grant is not a yes
The second decision is the one this entry is named for. When the system asks whether an agent may write to your vault, yes is not an answer — the question was which folders. When it asks whether an agent may send email, the answer is a policy: only to you, to an allow-list of addresses and domains, or to anyone. A grant is a typed value, and every kind of grant has its own schema.
There are twelve shapes. A vault scope is two lists of folder prefixes, read and write — and an empty pair is a real decision, meaning workspace-only, not an absence. A memory posture is one of four values, below. Recipients are a three-way policy. Channels are an allow-list scoped to one workspace, because a second Slack workspace is a second decision. Repositories, Drive folders and Notion targets are each an allow-list or any. Calendar invitees is a two-way choice — events for yourself, or events that invite other people, which is egress wearing a calendar. A public post has no allow-list at all, because its audience is the public; it is a stronger remembered confirmation. Web hosts are exact lowercase hostnames, a subdomain counting as a different host, because encoding data into a subdomain is a classic way out. Conversation access is own or all, with a list of sealed agents the user has explicitly let in. And the plainest shape, a simple remembered allow, covers the tools whose only question is whether.
Each shape renders as its own panel. The vault question opens the same folder tree the create-agent flow uses, pre-seeded with the folder the paused call was reaching for and with any access the agent already holds, so granting is one tap and prior choices stay visible. The recipient question shows three options and a place to type an address. The generic allow shows once, always, and not now. A value is validated against its schema at every write boundary, and at runtime a call whose arguments fall outside the grant's value — an address not on the list, a folder not in the scope — does not execute and does not fail silently: it becomes a review item the user can see and approve.
The eleven axes
Drawn on one page, the questions the runtime answers before a tool call proceeds are these. Each is resolved per turn, server-side, from the database; nothing in a request can assert any of them.
- Resource. Vault, memory, conversations, connectors, tools, web hosts. Six, each with its own grant schema, each fenced by different machinery.
- Attendance. Is a human present? An interactive turn and an unattended run answer differently — the axis that decides whether reads are free.
- Tenancy. Personal, shared within an organisation, installed from a Kit, or shared by another user. Which store the agent runs against, and under whose governance.
- Acting party. The owner, an invoking member, or — for a scheduled run — nobody. The third case is the one that reshapes everything.
- Agent standing. The default assistant, or a custom agent. The default is treated differently, deliberately and in writing.
- Direction. Read, write, or egress. These are not a ramp of increasing severity; egress is a different kind of act.
- Destination trust delta. Yourself, a destination already approved for this agent, or somewhere novel. This is the axis that lets the gate stay quiet when it should.
- Grant lifetime and scope. Once or remembered; and remembered per agent, per connector, per workspace, or per host.
- Runtime. The hosted pod, a Slack channel, a tool call arriving over MCP. A fence that differs by transport is not a fence.
- Decided or undecided. The absence of a grant is itself a state, and it does not mean the same thing on every axis.
- Recommended or enforced. What the agent's author suggested, and what its owner chose. Only the second is policy.
Two of these do most of the work, and neither is the one you would name first. Destination trust delta is what makes the model livable — without it every send would ask, and a gate that asks about everything is a gate people click through. Decided-or-undecided is what makes it honest — because there is exactly one axis on which an undecided agent fails open, and the reasoning has to be a decision, not a default.
Reads are free while someone is watching
The attendance axis and the direction axis meet at the model's most counter-intuitive rule, and an earlier entry on prompt injection carries its full argument: the guarantee lives at the exit, not the entrance. Injection enters through reads, but by the time a system could ask "may the agent read this email?" the hostile content is already fetched, and the person asked cannot see inside it. Harm leaves through the write that follows — the send, the post, the file pushed somewhere — and that is the boundary a compromised agent must pass through code to cross. So reads stay free, writes ask on first use, and egress is keyed on the destination's trust delta: sending to yourself never asks, sending to an approved recipient never asks, sending to a stranger asks once.
That is the interactive rule. Take the human away and it changes. A scheduled run reading a connected service for the first time does ask — once per connector, remembered — because the risk equation has three factors and autonomy is one of them: what the agent has read, how novel the exit, and whether anyone is watching. Interactive chat has a human in the loop who sees the turn and can intervene. A run at three in the morning has nobody, so the careful question that chat does not need is reinstated exactly where the watching stopped.
Presence buys one thing, and the argument is easy to stretch past it. In interactive chat the agent acts with the user's own ambient authority: it can reach what the person driving it could reach, and never more. Presence establishes authority. It does not establish attention — a person who types "summarise my inbox" cannot see that an injected line in one of those messages caused a read of a sensitive folder. The answer to that is not a read prompt, which would ask the person to approve something they cannot inspect. It is the egress gate, which stands whether or not anyone is watching, and the audit mark every reach into sensitive data leaves behind — content-free, in a retention-locked log — so that what has it looked at? has a complete answer that depends on nobody's memory.
Whose reach an agent acts with
The acting-party axis is where the identity a single-user assistant starts with — the agent acts as you — stops being one thing. Add agents shared across a team and scheduled runs that fire with nobody present, and acting as you has three answers.
For a person chatting with a shared agent, the effective scope is computed per turn as the intersection of what the agent was authored to reach and what the acting member's team standing allows — never widened by either side. A powerful agent shared with a junior runs clamped to the junior; a member with broad access running a narrowly authored agent gets the agent's narrowness. Sharing changes who may run a thing; it never changes what the thing may reach. A member on no team resolves to nothing. And when reading the member's standing fails — an infrastructure hiccup — the composition fails closed rather than falling back to the agent's full authored scope, because a fence that widens under error is not a fence.
For a scheduled run there is no acting member to intersect with, so the run executes the agent's own authored scope — and the execution context, not a per-agent setting, is what decides which rule applies. This asymmetry is deliberate and load-bearing. An owner authored a fixed task; a member who triggers it can invoke but not steer, and sees only the designed output. That is exactly why a member may trigger a workflow whose agent reads a team they are not in, yet cannot extract that team's data by opening the same agent in chat and asking for it — in chat, the agent has no such access for that runner. Reconcile the two paths in either direction and you reopen the confused deputy: a program with legitimate authority, tricked into spending it on behalf of someone who was never given it — Hardy's 1988 compiler overwriting the billing file because it trusted the path a user handed it. The two paths exist so that neither party's authority can be laundered through the other's.
One more detail of the org case matters more than it looks. When an agent posts to a connector on a member's behalf, it does so with that member's own credential, scoped to the organisation — it posts as the person, fenced to what that person connected. The agent is not a shared service account with everyone's reach; it is a runner of the member's authority, for that turn.
Four postures, three modes, and what undecided means
The resource axis has a different vocabulary for each store, and the decided-or-undecided axis behaves differently on each. Memory first. An agent's memory posture is two independent switches — are reads shared, are writes shared — which yields four postures: open (both shared, the default), private writes (reads the common pool, keeps its own notes to itself), contribute (works from its own focused context, publishes curated writes up to the pool — the publisher of record), and isolated (its own separate memory, nothing crossing in or out). The single rule underneath all four is that a private wing is readable only by its owner, whatever posture the reader holds; an open agent reading the pool never sees an isolated agent's notes. The vault has three modes: unrestricted, workspace-only, or scoped to named folders. Conversations have two: an agent searches its own by default, and widening to all conversations is a grant like any other, with sealed agents excluded from all unless named — derived live from their current posture, so sealing an agent later pulls its history out retroactively.
Then the axis nobody designs deliberately: what happens when no decision was made. On writes and egress, absence is a question — the agent asks at first need. On conversation access, absence is the fence: no grant means own conversations only, deny-by-default by construction, and even the default assistant does not widen without asking. On unattended reads, absence asks. And on interactive reads of vault and memory, for a custom agent whose owner skipped the scope questions, absence resolves open: the agent reads what its owner could read, in chat, until a decision is made.
That last case is the one place the model is not deny-by-default, and it is a decision we would make again. The runtime cannot tell never decided from deliberately open — absence is the only signal, and it means both — so the fallback was chosen to make a future tightening safe for every agent minted before scopes existed. More than that: it is the same rule as the attendance axis. With a person present, the agent acts with that person's authority over that person's own data, and a prompt asking them to approve a read they just requested would train them to click through the prompts that carry weight. A decided restriction is enforced everywhere, chat included, so an owner who wants a folder-scoped agent has one today. What undecided buys is that scoping, in the interactive case, is mostly a relevance control rather than a security one — people fence an agent so it answers better, not because it might betray them — and off-by-default is the right default for a relevance control.
The eleventh axis sits inside this one. A Kit's author can recommend a memory posture for the agent it installs; the install screen seeds the picker with it, and only the owner's confirmed answer is enforced. Recommended and enforced are two separate fields, and only one of them is read by anything that decides. An installed agent otherwise arrives with no grants at all — every other decision is asked at first need — which is why the interactive rule above has to hold: an agent someone installed from a shelf is an undecided agent on most axes, and the model has to be right about undecided.
The question waits for you
Everything above assumes the agent can ask. The attendance axis is where that assumption breaks, and the mechanism that repairs it is the one an earlier entry called the other half of autonomy: the question is parked where a human will find it. A run that reaches a tool it has no grant for pauses, writes the question as a card in the user's inbox, and finishes with a first-class needs your permission status rendered amber rather than failure-red — nothing was lost, and the run knows exactly what it is waiting for. Grant it and the work re-runs with the grant in place; decline and the card closes and nothing executes. The same panel that would have appeared inline in chat appears on the run's screen while it is live and in the inbox when it is not — one panel, three surfaces.
Two properties keep the asking meaningful. Allow once is genuinely once: on an unattended resume the grant is threaded ephemerally and never persisted, so the next run asks again; in chat it is persisted for the turn and swept at its end. And the panel that asks about a destination states the read surface beside it — this agent can currently read: Gmail, Drive — so the person granting an exit knows what could leave through it. Reads inform; egress gates; and the moment of granting is where the two are shown together.
One door
The runtime axis is the last, and it is where the model is either real or decorative. An agent's turn can execute as interactive chat, a workflow stage, a routine, a dashboard refresh, a message in a Slack channel, or a tool call arriving over MCP from a client outside the product. Every one of those paths installs the same three ambient contexts before the loop runs — the agent's grants and posture, the vault mode and scopes, and the memory-write attribution — and the enforcement layers read from those contexts and from nothing the request carried.
The lesson that fixed the shape of this was cheap to state and expensive to learn: fences are only as good as their installers. In July, a review of the shipped code found that every fence existed and that two unattended paths — routines and dashboard refreshes — had never installed the contexts they read from, so a folder-scoped agent's routine read the whole vault and a sealed agent's routine read the shared pool. Nothing was wrong with any fence. The assumption that every caller would remember to build one was wrong. So the final check moved into the dispatcher itself — the single function every tool call passes through — where it re-derives consent from ambient context and fails closed when a gated tool arrives with no context at all. A future execution path that forgets the ceremony gets a blocked call, not a silent bypass. A choke point callers are relied on to invoke is a convention; a choke point inside the only door is an invariant.
The runtimes differ where they must, and only there. A call over MCP runs as the user's own assistant, carries the same contexts — including the org member clamp when the person connecting is a member — and receives a registry-derived subset of tools with the email sends removed — over MCP an agent drafts, and the person clicks Send in the product: defence in depth over the gate, not a replacement for it. It is also stricter in the one place it sees less. An MCP client sends a tool call, not a conversation, so the pod cannot know which links the person pasted; every data-carrying fetch to a host not already approved is refused, and the refusal names where the approval lives. A Slack channel binds to a team, and the channel supplies the scope when no member is in the loop.
What transfers
For anyone building agents that touch real accounts, the shape of the model travels better than its details. Make availability universal and enforce at the moment of use — an agent that cannot name a tool cannot ask for it. Make a grant a typed value with its own schema and its own panel, remembered under a key whose shape says per what, and derive "decided" from the entry's existence rather than a flag. Key egress on destination novelty so that the gate can stay quiet, and let reads be free while a person is present — then reinstate the question where the person is not. Resolve every agent's reach per turn as an intersection of the parties involved, and let sharing change who, never what. Decide what undecided means on every axis, state the one place it fails open, and be able to say why. Park the question where a human will find it, and make the answer resume the work. And put the last check inside the single door, because the review that finds your fences uninstalled will otherwise be the one your users run.
The stated exception carries a trigger. Interactive reads on a self-authored agent fail open because the person present is the authority being exercised. The day an agent authored by a stranger can be installed at scale, that reasoning no longer transfers — the installing user's presence would be spent on a third party's instructions — and the model will need the installing user's grant to bound the agent's reach at install, not a question they may not answer at mint. That is a fork on the tenancy axis we can see from here, and have not yet had to take.
This entry is written on the day the view was taken, not backdated: the pieces it gathers landed between May and September, and the eleven axes are how they read once laid side by side. Two things it names are still open — bounding a third-party agent at install, and seeding an agent's scope from what its identity says it is for, so that "undecided" becomes "confirm this default" without a prompt.