ConceptsApprovals
Approvals#
Who approves what, and how?
On this page
An approval is a request held for a human: it is shown on a page (/w/<slug>/a/<id>) and decided by an eligible human with a passkey. Approvals are created by approve policy rules, by runs in protected environments, and by any item-mode use of a protected credential. An undecided approval expires after an hour, and a machine may have at most 20 open asks of every kind.
Who decides#
Your agent asks you. Whoever decides, the vault computes it when the page is viewed and again inside the decision:
- Your own machine's asks go to you, within your own current rights: you must yourself be able to use the credential in that context, and hold the protected right for a protected one.
- A private credential's ask is decided by its owner only, a guest's as much as a member's. Its full request is readable by its owner only; an owner or admin may deny it from a minimal stop view.
- A guest's or a team's machine asking for a shared credential is decided by a manager of the project, or an owner or admin, never by the guest.
- Four-eyes: where a protected environment has it, the deciders are the managers of every project whose four-eyes applies, other than the machine's owner.
- Denying is open to more people: owners, admins, the machine's owner, for a shared credential the project's managers, and for a team's machine the team's members.
Approving and reading are separate rights. An active member who can see the relevant credential or project may be able to read the full request even when they cannot approve it. Someone with only a right to deny, and no right to read the detail, sees a minimal stop view (workspace, machine, credential slug, status). Private-credential detail stays with its owner.
The eligible deciders' names travel with the refusal, so the agent can say who to send the link to. A decider's identity gate, cooling-off after an account recovery or pending account change suspends their right to approve. Every decision records who decided and on what basis; when that basis goes (a share expires, a role changes, a protected reference is added), the decisions, grants and runs that rested on it are stale.
What the agent sees#
- The call returns HTTP 409 with
error: "approval_required", the approval id, a binding code (6 characters from an alphabet without 0, O, 1, I, L or U),expiresAt, andnextwith the link,mehen wait apr_...and a sentence for the human.mehenexits 3 and prints the ask. - The agent relays the ask: the link, what it is, the code and when it expires. The same code shows on the approval page, so with several approvals pending the human can match them. The approval itself never travels through the chat, which an agent could forge.
- The human decides on the page with a passkey.
mehen wait <approval>returns the status:pending,approved,denied,expiredorused.- The agent repeats exactly the same request. Any change to the item, method, URL, content type, content encoding, forwarded headers or body is a different request and needs a new approval. When a request needs approval is the agent's procedure.
What the human sees#
The page leads with a decision card: the service's tile, "An agent on my-laptop wants to" (the machine's name its owner accepted at enrolment, never the agent's session label), the title, and the lines a person decides on (for an email: To with an outside <domain> mark on each external recipient, Cc, Bcc, Subject, From, attachments), then a countdown, the terms in words, the binding-code tick and a bar with Deny and Approve with passkey.
A protected run. The approval page shows the project, environment, command arguments, variable names and which variables receive real values or placeholders. The request digest also binds the submitted remote, worktree and commit, but this page does not display those fields. Check the CLI's resolved context before requesting approval.
Under it is the request. The approval is bound to hashes of the exact request. The page shows its method, URL, content type and encoding, forwarded headers and a presentation of its body: valid JSON is formatted, forms are decoded, decoded email content is drawn out where it is recognised, and a body that is not UTF-8 text is represented only by a byte count and cannot be inspected there. It is not a lossless view of the original bytes. Do not approve a request whose contents you cannot verify. Requests that need approval must be uncompressed, with a body no larger than 64 KiB and forwarded headers no larger than 16 KiB; otherwise the vault refuses with mode_refused.
Deceptive characters are marked. Hidden and text-direction characters become markers that name them where they sit, such as [RLO U+202E], and control characters are shown as \u00XX; either adds a red warning, and Deny becomes the primary button. Only markers the vault generated are styled; a marker an attacker typed stays plain text. The page checks what it shows against the approval before Approve appears.
Grants#
Approving creates a grant for that machine and that exact request. You may shorten the requested window or reduce its uses, never increase them: a relayed request asks for one use within ten minutes, and a protected run for one use within an hour. A matching request from the same machine spends one use; the grant is found by the request itself, so the approval id a request may carry is optional. A grant can end earlier when the approval expires or the authority behind the decision changes, and when the machine's owner loses the project the grant is dead at its next use.
Not built: grants scoped to a session, grants for subagents, pattern approvals ("Gmail sends to *@example.com, at most 5, for an hour"), budgets per binding, and receipts attached after execution.
What needs a passkey#
A step-up is a fresh passkey assertion over one exact action. The server rebuilds the action from what it is about to do, never from what the page claims, and binds it to the workspace, so what the human saw is what they signed. Everything that widens access needs one:
- storing or rotating a credential from an intake, and starting an OAuth connection;
- accepting your own machine, admitting a guest's or a team's machine, and accepting a wider scope for your machine;
- narrowing or revoking a machine;
- deciding an approval;
- changing a credential's binding, policy, ceiling, tags or description, re-homing it or deleting it;
- setting a variable, protection, four-eyes, fences or a bound repository from the browser, and choosing a personal override;
- creating, sharing, configuring or deleting a project;
- inviting, confirming, changing the role of, suspending, reinstating or removing a member, and leaving;
- changing teams;
- the workspace itself: renaming, the identity rule, deleting, unfreezing;
- your account: passkeys, recovery codes, linked identities, deleting it.
Pulling the brake (freezing) needs no passkey. While an account is cooling off after a recovery, or has a security change pending, only tightening actions are allowed: narrow, deny, revoke, freeze, and cancel the recovery. Keys, proofs and signing has the exact challenge and the action table.