The four credentials
What the server never holds: a raw API key, a step up private key, or an approver private key. A key shown once by a script cannot be recovered; if it is lost, you make a new one.
Before you start (every machine, once)
You need Node.js 24 or later and Git.npx tsc -b builds the packages the scripts import. Without it every script fails with ERR_MODULE_NOT_FOUND ... @parmana\shared\dist\index.js.
Each key script prints its secret once. The blocks below save everything to files automatically, so only the printed Key : value needs to go into a password manager. Keep the files outside the repository folder: the repository is public.
parmana-entries holds one JSON line per caller: the caller id, the key hash and, for a checker, the step up public key. None of it is secret. It is what you deploy.
1. Maker key (the maker runs this)
Replacemaker-NAME with the maker’s caller id.
Key : value in a password manager now. --credential-holder-type USER is required: policy governance endpoints accept only callers provisioned as human (packages/api/src/auth/isHumanCaller.ts), and a key without it is refused with NON_HUMAN_CALLER_DENIED.
2. Checker key (the checker runs this, on their own machine)
3. Approver key pair (the approver runs this, on their own machine)
4. Agent key (the operator runs this)
Grant only the capabilities the agent needs. For a refund agent:--allowed-principal-ids, the agent may only act as itself: the principalId in its requests must equal its caller id.
5. Deploy the API keys (the operator runs this)
PARMANA_API_KEYS is a JSON array of the entries. This block builds it from every file in parmana-entries, replaces the variable on Vercel and redeploys, because an environment change only takes effect in a new deployment. The first time, it links a folder to the Vercel project (run vercel login before).
parmana-entries folder: a sensitive Vercel variable cannot be read back, so this folder is the only copy of the list.
Do not use scripts/rotate-caller-key.mjs for this. It rewrites an entry as only a caller id and a hash, dropping credentialHolderType and stepUpPublicKey, so a human caller would lose access to policy governance.
6. Test a key
GET /callers/me returns the caller id of a key, and never the key itself.
7. Maker and checker: change a policy
Propose (maker)
The body needsreason and proposedContent, the whole policy.json, whose policyId must match the name in the path.
pendingPolicyChangeId. Send it to the checker.
Review (checker)
Approve (checker)
The step up signature is valid for 120 seconds: run the whole block at once.status : APPROVED. The server verifies the envelope against the step up public key in the approving caller’s own entry, not against --key-id. From then on, the approved version is the one in effect for its action, with no deploy (docs/CLAIMS.md 2.43).
8. Register an approver (operator, with a pull request)
Approvers are listed in code. Add the entry with the public key file’s contents toTRUSTED_APPROVAL_ISSUERS in packages/api/src/bootstrap/createApprovalIssuerRegistry.ts:
revoked: true and deploy.
9. Approver: sign one approval
For one action on one resource, up to an amount, valid 15 minutes by default, usable once:managerApproved: true and the contents of approval.json in signals.approvalArtifact. See Human approval.
10. Rotate or revoke a key
Rotate at once if a key was pasted into a chat, a ticket or a log.
What was done on 2026-09-27
The production deployment was rebuilt with these steps, in this order:- The operator had no working human key, and two keys had been shown in a chat transcript. Every production API key was replaced from scratch with step 5; the old
policy-maker,policy-reviewer-1andpaytm-refund-agentkeys stopped working at the redeploy. - The operator decided to hold the maker, checker and approver roles for now. New callers:
charak1987(maker),reviewer-charak1987(checker, with a step up key) andpaytm-refund-agent(paytm:refundonly). Governance in production is therefore one person with two credentials until a second person replaces the checker. reviewer-charak1987reviewed and approvedcustomer-refund1.1.0 (change008f504d-0efd-4bec-b33a-2991bb84099f, proposed bypolicy-maker) at 18:53:40 UTC. Refunds now run under 1.1.0: automatic up to 10000, a verified manager approval above 10000 and up to 100000, refused above 100000.- The refund agent key was deployed after the approval, so no refund ran under 1.0.0 with the new key, and the refund agent’s Vercel project was given it.
- A refund manager key pair,
manager-charak1987, was created and, on 2026-09-28, added toTRUSTED_APPROVAL_ISSUERS(step 8). Before that, every production refund above 10000 was refused.
docs/progress/2026-09-27-HUMAN-APPROVAL.md.