Skip to main content
A Parmana deployment uses four kinds of credential. This page gives the commands for creating them, deploying them, testing them and rotating them. The examples use PowerShell and placeholder names. Replace each placeholder with your own value.

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.
Each person creates their own credential on their own machine. The server refuses an approval from the same caller that proposed the change (SameActorCannotApproveOwnChangeError). It cannot tell whether two callers are two people. If one person holds both the maker and the checker credentials, that is one person with two credentials, not two-person control.

Before you start

You need Node.js 24 or later and Git. Build the packages the scripts import:
Without the build, every script fails with 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 the parmana-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:
Do not install the CLI with 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:
Expected: a 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:
Expected: 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:
It must read -----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:
Checker finds the change, then signs and submits it within 120 seconds:
Check the result:
Rules that matter:
  • 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.
To revoke an approver, the maker proposes 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:
The script writes two files and refuses to overwrite existing ones. Keep the .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:
Put the key in the agent’s own configuration. Without --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.
Set the value in your host, as 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 a reason 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.
The response holds pendingPolicyChangeId. Give it to the checker.

Review (checker)

Read every rule, warning and conflict before approving. Rules are evaluated first match wins. An overlap between two rules with the same outcome is harmless. An overlap between an approve rule and a reject rule decides behavior by their order.

Approve (checker)

The step-up signature is valid for 120 seconds, so run this whole block at once:
A successful approval shows 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.
The checker then approves the change with the same step-up procedure as in step 7. Alternatively, a deployment can list approvers in code. Add the entry to 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:
The agent sends a new request with 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.

Troubleshooting