DocsGovern AI agentsApprovals

Approvals

The concept page covers fingerprints, quorum and single-use grants. This page is the loop as you actually run it.

approval.loopsingle use, claimed by fingerprint
Agent actscheck returns require_approval
pauses
Approval raisedbound to the exact fingerprint
waits for
A named humanCLI, console, or chat
on grant
Agent retriesgrant is claimed, then spent
the agent retries the same action; there is no approval id to carry back

What the agent gets

An agent handed require_approval receives an approvalId and the reason. It does not receive an error, a refused action and a paused action are different things, and an agent that cannot tell them apart will retry the wrong one.

json
{
  "effect": "require_approval",
  "approvalId": "a_7f31c2",
  "reason": "Auth and session code changes need a second pair of eyes.",
  "approvers": ["security-team"]
}

What the human does

bash
memnox approvals                 # what is waiting, with the id for each
memnox approve <id> --by dana    # --by defaults to $USER
memnox deny <id>
memnox approvals status <id>     # pending · granted 1/2 (dana) · approved

The <id> is printed by memnox approvals; it is never something you construct. --by is the name the audit trail keeps, so it should be a person rather than a shared account.

From the console it is the Approvals screen; from chat, a Slack-compatible webhook announces new requests (--approval-webhook).

Console → Approvals, one request selected showing action, target, environment, agent and quorum progress

Console → Approvals, one request selected showing action, target, environment, agent and quorum progress

screenshot slot, save as public/screens/…png and set  src

Four fields decide whether to grant: which agent, which action, which target, which environment. Everything else is context.

What the agent does next

It retries the same action. That is the entire client-side protocol.

The gateway claims the grant by fingerprint, so the agent does not have to carry the approval id back, which is what makes this work from an editor hook that has nowhere to put one. A caller that does have the id may still send it as approvalId on the check.

While it waits, an agent may poll its own approval:

bash
curl http://127.0.0.1:7466/v1/approvals/a_7f31c2 \
  -H "Authorization: Bearer $MEMNOX_AGENT_TOKEN"

This is the only route an agent token may read beyond the decision endpoints. It returns the approval that agent raised, and answers 403 on anyone else's.

Timing

An approval that nobody answers should not hold an agent forever, and a grant that nobody uses should not sit around indefinitely. Both are bounded by TTL, and a grant is spent the moment it authorizes an action regardless.

What a grant never overrides

  • an agent's declared capabilities;
  • a suspended agent;
  • a non-overridable taint block (project.delete, database.drop).

Each refuses the action and leaves the grant unspent, so the approval is still there afterwards, and the audit shows it was not the approval that failed.

Break-glass

bash
memnox approvals override <id> --reason "incident 4471, on-call escalation"

The reason is required. The override is audited as critical and opens a break_glass_override incident. Irreversible actions refuse it with a 403 and audit the attempt.

Break-glass is not a faster approval. It is an event somebody will read.

Approval health

bash
memnox approvals health

Reports how long requests are waiting and how many are still unanswered. Rising latency here is the earliest sign that a rule is routed to the wrong group. The rule is not wrong; the audience is.

Check it weekly. A rule nobody answers is a rule somebody will eventually delete instead of fixing, and the deletion is much harder to notice.