Governed Runtime Standards · Specification 2 of 3
Action Approval Attestation
A verifiable statement that an identified approver permitted, or refused, a specific proposed action by an AI agent. It binds the decision to the exact proposal the approver saw, gives the permission a defined scope and bounds, and can be sealed so that the permission itself is evidence. It is the answer to the question the Action Provenance Record raises and the IETF Agent Action Capsule deliberately leaves open: on whose authority, and for exactly what.
The three failures it closes
Human-in-the-loop is the safety mechanism every agent vendor promises and almost none specify. In practice "the user approved it" is a boolean in a database, disconnected from what was shown on screen, what was executed, and how long the permission lasted.
Approve X, execute Y
The agent proposes one thing, the human approves, and the arguments change before execution. Without binding the approval to a digest of the arguments, nothing detects it.
Approval creep
A single yes silently becomes a standing permission with no expiry, no use limit, and no record of what it matches.
Unverifiable consent
After the fact, nobody can prove the approval existed, who gave it, or that it has not been edited.
What the attestation contains
| Member | Meaning |
|---|---|
| spec (R) | Always action-approval/1.0. |
| id, correlationId (R) | Unique id; the action or run the attestation belongs to, matching the resulting provenance record. |
| proposal (R) | kind, tool, target, effect (read or write), argsDigest of the exact arguments the approver saw (required), a human-readable summary, and presentedDigest when the presentation held more than the arguments. |
| principal (R) | Who requested the action. |
| approver (R) | Who decided. For an auto decision, the policy engine as a service party. |
| decision (R) | approved, rejected, or auto. |
| scope (R) | single, session, or standing. |
| constraints | expiresAt, maxUses, matchOn (which proposal members must match for a broad grant to apply), conditions. |
| decidedAt (R), method | When, and how: ui, api, delegated, policy. |
| policy | The policy under which approval was sought or granted. |
| revoked | Present once a grant has been withdrawn: at, by, reason. |
| integrity | Content digest and MAC or signature, computed as in the Action Provenance Record. |
argsDigest is required at Level 1. An approval that does not bind the arguments is not an approval of anything in particular.Scope
- single. One execution of exactly this proposal in exactly this correlation. The default, and what anything with a material effect should use.
- session. Proposals within one session that match on the members named in
constraints.matchOn. Ends with the session. - standing. Matching proposals until the grant expires, is exhausted or is revoked. A standing grant must carry an expiry or a use limit. This is the "always allow" pattern, made safe.
For session and standing scope, arguments are deliberately not matched; matchOn names what is, for example tool, target and effect. That is what makes them broader than single.
Coverage: does this grant authorise this action now?
Given a grant G and a proposal P to execute now with correlation C, G covers P if and only if all of the following hold. A runtime evaluates this immediately before execution, not at the time of approval, and records the covering attestation in the resulting provenance record.
- G.decision is approved or auto.
- G is not revoked.
- G has not expired.
- G has uses remaining.
- G.proposal.effect equals P.effect.
- If G.scope is single: G.correlationId equals C and G.proposal.argsDigest equals P.argsDigest.
- If G.scope is session or standing: every member named in matchOn is equal between G.proposal and P. For session scope, G.correlationId must also equal C or the current session.
Conformance levels
Level 1: Bound
The attestation validates against the schema, and argsDigest binds the decision to the proposal.
Level 2: Scoped
An approved or rejected decision is made by a human party; an auto decision cites a policy; a session or standing grant names what it matches on; a standing grant carries an expiry or a use limit.
Level 3: Verifiable
The attestation is sealed with a content digest and a MAC or a signature.
Example: a single approval
{
"spec": "action-approval/1.0",
"id": "att_01J9X3PZ9C4D6E8F0G2H4J6K8M",
"correlationId": "corr_01J9X3Q0Z8K7M2N5P4R6S7T8V9",
"proposal": { "kind": "tool_call", "tool": "outlook.send_email", "target": "microsoft-outlook",
"effect": "write", "argsDigest": "sha256:5a6b...",
"summary": "Send the August aged-receivables reminder to accounts@northwind.example" },
"principal": { "id": "user_2f9a", "type": "human", "tenant": "org_acme" },
"approver": { "id": "user_2f9a", "type": "human", "display": "Sarah Chen" },
"decision": "approved",
"scope": "single",
"decidedAt": "2026-09-02T03:12:38.640Z",
"method": "ui",
"policy": { "ref": "pol_finance_outbound_email", "version": "3" }
}
A standing "always allow" grant looks the same with scope: "standing", constraints.matchOn: ["tool", "target", "effect"], and an expiresAt or maxUses.
Closing the loop with the provenance record
The attestation is created before execution; the record is created after. The record's approval.attestation carries the attestation's id or content digest. A verifier holding both confirms that the executed effects[].argsDigest equals the approved proposal.argsDigest for a single grant. That is the check that closes the approve-X-execute-Y gap.
Security considerations
- Binding. Present the same bytes you digest. If the approver sees a summary, record presentedDigest too, and make sure the summary cannot mislead about the arguments.
- Replay. A single grant is consumed on first execution. Runtimes should store consumed attestation ids.
- Revocation. A revoked grant stops covering immediately. Revocation should itself be sealed.
- Auto approvals. An auto decision is only as good as the policy it cites. Runtimes should expose an organisation-wide autonomy cap so no agent can be granted more than the organisation allows.
Conformance statement for Renly
Renly presents every side-effecting action as an approval card bound to the proposed tool call, signs granted approvals with a dedicated evidence key, supports single approvals and time- and scope-bounded "always allow" grants, records the covering approval in the resulting evidence record, and enforces an organisation-wide autonomy cap. Renly implements this specification at Level 3 using keyed MACs.