true only with
a signed approval from a trusted approver, for this action and this resource (and amount, where the policy names
one), not expired, used once. This chapter is about the people and the signatures.
Three kinds of people, three kinds of keys
Private keys never leave their owner’s machine. Only public keys are sent to anyone.
Trust an approver
An approver is trusted when their public key is in the server’s approver list. Add one through maker checker: no code change, no deploy.-
The approver makes a key pair on their own machine:
They keep
manager-priya__manager-priya-key-1.private.pemand send the.public.pemfile. -
A maker proposes adding it:
POST /approval-issuers/changeswithaction: "add", the ids, the public key and a reason; orproposeApproverChange()in TypeScript,approvers.propose_add()in Python. -
A checker compares the public key with the approver over another channel, signs a step up authorization for the
change, and approves:
POST /approval-issuers/changes/<changeId>/approve. -
From the next request on, approvals signed with that key verify.
GET /approval-issuerslists every trusted key and where it came from (governed, orcodefor keys listed in the server’s source).
Sign an approval
The approver signs on their own machine, for one action, one resource and, where the policy names a value, a maximum.--ttl-seconds (ttlSeconds, ttl_seconds). The signing tools refuse
more than one day; the server checks only that it has not expired, so a short lifetime is the approver’s choice to
keep.
What the server checks
The agent sends a new request with the approval signaltrue and the approval in signals.approvalArtifact (or the
signal the policy names as the declaration’s artifact). Before deciding, and again at the gateway just before
release, the server checks:
A refused approval is a
403 POLICY_DENIED, because the declared signal does not match what the server verified:
How a person learns an approval is needed
A request refused for want of an approval is a normal, expected answer. Three ways to reach the approver:- Refusal Records. Every refusal is stored, signed, with its reason and the request. Query
refusal_recordsfor the policy andmatchedRuleId(Human approval). - A webhook. With
APPROVAL_WEBHOOK_URLandAPPROVAL_WEBHOOK_SECRETset, Parmana posts anapproval.neededevent, signed with HMAC SHA256, only when the same request with the approval signalstruewould have been approved. Itsapprovalslist is exactly what to sign. - Email. With Resend configured and
APPROVAL_EMAIL_TOset, the same event is emailed, at most once per refusal.
What an approval does not fix
- A refused request is never approved later. Nothing waits. After the approver signs, the agent sends a new request.
- An approval covers the action and the resource, and the amount where the policy names one. It does not fix other parameters: an approval for a Slack channel does not fix the message text. The approver trusts the agent with those details for that one use.
- Facts the agent declares stay its word. A policy should use them only to refuse.
- There is no automatic path. No policy can approve without a person; the validator refuses to load one that tries (Chapter 4).
Approving changes: the step up authorization
Approving a policy, an approver or an external connector is a different signature: a step up authorization from the checker, for one change and one decision (approve or reject), valid for at most 120 seconds and once. The
server also refuses it when the checker is the maker (403 SAME_ACTOR_CANNOT_APPROVE_OWN_CHANGE) or not registered as
a human (403 NON_HUMAN_CALLER_DENIED).
scripts/local-review-action.ts signs and submits in one process, so the 120 seconds cannot run out between the two.
Keep it honest
Parmana proves that a key signed. It cannot prove whose hand held the key. Two keys held by one person make every record claim a second person who does not exist. Keep each key on its owner’s machine, and never paste a key into a chat or a ticket.See it run
- Tutorial 119: an approval accepted, then refused when reused, stretched and moved.
- Tutorials 120 to 122 in
examples/tutorials/: approvals for the rule itself, a read, a Slack post and a HubSpot update.