The four credentials
The server never holds a raw API key, a step-up private key, or an approver private key. A key that a script prints once cannot be recovered. If it is lost, create a new one.
Before you start
You need Node.js 24 or later and Git. Build the packages the scripts import:ERR_MODULE_NOT_FOUND. Each key script prints its secret once. Save the printed Key : value in a password manager. Keep the files the scripts write outside the repository folder.
Quick path with parmana-ops
This route uses theparmana-ops CLI for everything it can do. The raw API steps further down are the same operations, written out by hand. Use this route first.
Each person does their own steps, on their own machine. Nobody generates a credential for someone else.
Set up the CLI
Build and link the CLI from the repository, once per machine:npm install -g from a folder. That pulls the published @parmana/crypto, which may not have the key commands.
Set your own caller key for each command that calls the server. Use the raw key from your own step 1, never someone else’s:
1. Generate credentials
Create trip and reset keys only when someone needs them. A reset needs a second, different operator to complete it.
Each command writes owner-only files and prints paths, never secret values:
2. Put the entries on the server
The operator merges every*.entry.json into one JSON array and sets it as PARMANA_API_KEYS in the host’s secret store, then redeploys. The entries hold only hashes, so the server cannot recover a raw key. A new value takes effect only after a redeploy.
3. Validate before you use a key
Check each credential once it is live:200 response whose callerId matches. A 401 means the key is not in the deployed list, or the deploy did not pick it up. Repeat with a retired key, which must return 401.
For the kill-switch probe key, check the status route, then send one probe:
200 for status and 201 for the probe. The probe writes one record to the database, so send it once. A probe key without kill-switch:probe gets 403 with CAPABILITY_NOT_ALLOWED.
For an approver’s public key, the first line must be the public header:
-----BEGIN PUBLIC KEY-----. A PRIVATE KEY header means the wrong file was sent. Stop, and treat that private key as exposed.
4. Register an approver: maker and checker
The maker proposes the public key. A different checker approves it with a step-up signature. The change takes effect for the next request. Maker, dry run first, then the real command:- The maker and the checker must be different callers. The server refuses an approval from the proposer.
- The checker signs with the step-up key, not the approver key.
- The envelope is valid for 120 seconds and works once. If it expires, sign again.
parmana-ops approver propose-revoke --approver <id> --key <key-id> --reason "<why>", and the checker approves it as above.
Raw API steps
The steps below do the same work by hand with PowerShell and the API. Use them when the CLI is not available.1. Maker key
--credential-holder-type USER is required. Policy governance endpoints accept only callers provisioned as human. 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:.private.pem file on the approver’s machine. Send the .public.pem file to the operator, who registers it in step 8.
4. Agent key
Grant only the capabilities the agent needs. For a refund agent:--allowed-principal-ids, the agent may act only as itself: the principalId in its requests must equal its caller id.
5. Deploy the key list
PARMANA_API_KEYS is a JSON array with one entry per caller. Build it from the entry files, then set it in your host’s secret store and redeploy. Environment changes take effect only in a new deployment.
PARMANA_API_KEYS, marked as a secret. Then redeploy. A caller that is not in the list loses access at that redeploy. That is how a key is revoked.
Keep the entry files. A secret value cannot be read back from most hosts, so these files are the only copy of the list.
If the server does not start, check the value first. PARMANA_API_KEYS is not valid JSON or must be a JSON array means the value was not saved as one array.
6. Test a key
GET /callers/me returns the caller id of a key. It never returns the key itself.
7. Change a policy: maker and checker
Propose (maker)
The body holds areason and the whole policy.json. Its policyId must match the name in the path. Propose a new version folder. An approved version is never edited.
pendingPolicyChangeId. Give it to the checker.
Review (checker)
Approve (checker)
The step-up signature is valid for 120 seconds, so run this whole block at once:status : APPROVED. The server verifies the signature against the step-up public key in the approving caller’s own entry. The approved version is then in effect for its action.
8. Register an approver
The recommended route is maker checker. The maker proposes the approver’s public key, and the checker approves it with a step-up signature. The change applies to the next request. See Manage approvers for the commands in curl, TypeScript and Python.TRUSTED_APPROVAL_ISSUERS in packages/api/src/bootstrap/codeApprovalIssuers.ts through a pull request. Merging deploys it. Until an approver is listed, every signed approval from that approver is refused.
9. Sign one approval
An approver signs one action on one resource, up to an amount. The default validity is 15 minutes, and the approval can be used once:managerApproved: true and the contents of approval.json in signals.approvalArtifact. See Human approval.
10. Rotate or revoke
Rotate a key at once if it was pasted into a chat, a ticket or a log.
When a service holds several values from Parmana, change them together, or requests fail between the changes. Set all of them, then redeploy both sides. A new value takes effect only in a new deployment. Afterwards,
GET /callers/me with the new key should return its caller id.