Skip to main content
By the end of this page you will have:
  1. Parmana and its own Postgres running on your machine.
  2. The shipped customer-refund policy approved by two people, the way production requires.
  3. A refund request refused by that policy, with a signed Refusal Record you can verify.
Every command below was run exactly as written on 2026-09-25.

Before you start

Run every command from the root of the repository. Each docker compose run command first prints two lines such as Container parmana-setup-run-4031dfc35ccf Creating and ... Created. They go to standard error and are not part of the output shown on this page.
Git Bash on Windows only: run export MSYS_NO_PATHCONV=1 first, in the same terminal. Without it, Git Bash rewrites paths such as /app/parmana-local/api-key.txt into Windows paths before Docker sees them, and the commands fail.

Step 1: Start Parmana

The first run builds the image, which takes a few minutes. Later runs take seconds. On the first run this command also creates your signing keys and an API key in the directory ./parmana-local. What each part does is in Configuration reference. Check:
Expected output:
If you see anything else, go to Troubleshooting.

Step 2: Read your API key

The first start made one API key, for the caller ID local-operator, allowed to use the capability paytm:refund. Print it:
The output ends with a line that starts with pk_local_. That line is the key. Keep it in a variable for the rest of this page:
Check:
Expected output:

Step 3: Add the two people who approve policies

In production a policy decides nothing until two different people have approved it: a proposer, and an approver who confirms with a signature from a key only they hold. Until then every request under that policy is refused with 403 POLICY_DENIED and the reason “has no PolicyChangeApprovalRecord”. This page calls them alice (proposer) and bob (approver). Use your own names. 3a. The approver makes a step up key pair, on their own machine. The private key never leaves that machine.
The approver sends only step-up.public.pem to whoever runs Parmana. In this quickstart both are you, on one machine. 3b. Add both API keys. Each command prints the new key once, on the line that starts with pk_local_. It cannot be shown again. Give each person their own key.
Bob’s key also registers his step up public key, which is read from standard input. The -T is required for that:
Keep them in variables for the next step:
3c. Restart the API. It reads the keys only when it starts.
Check:
Expected output:

Step 4: Approve the customer-refund policy

4a. Alice proposes the policy exactly as it ships in policies/customer-refund/1.0.0/policy.json:
Check: the last command prints an ID such as 99527ea9-16dd-468d-bcc7-88d51daa1779. If it prints nothing, proposed.json holds the error. 4b. Bob signs his approval, on his own machine. This needs a clone of the repository with npm install run once. The signature is valid for 120 seconds and for this one change only, so do 4c right after.
4c. Bob sends the approval:
Check: the last command prints "status":"APPROVED". Parmana refuses the approval if Alice sends it (403 SAME_ACTOR_CANNOT_APPROVE_OWN_CHANGE), if the signature is reused, expired or made with another key (STEP_UP_AUTHORIZATION_INVALID), or if the key is not a verified human (NON_HUMAN_CALLER_DENIED).

Step 5: Get your first decision

Build a request for a refund of 50,000, which is over the policy’s limit of 10,000:
Send it:
Expected output:
Nothing was executed, and Parmana stored a signed Refusal Record of the decision.

Step 6: Verify the Refusal Record

Fetch it by the request’s businessTransactionId:
Check its signature. This endpoint needs no API key, so anyone you give the record to can run it:
Expected output:
Change one value and check again:
Expected output:

What you have now

  • Parmana running on your machine with its own Postgres, signing keys and API keys. Nothing is sent to Parmana or anywhere else.
  • A policy approved by two people, recorded and signed.
  • A refused request with a signed Refusal Record.
A request the policy authorizes is released to a connector, the service that performs the action. Until one is configured, an authorized request returns 503 CONNECTOR_NOT_REGISTERED and nothing is executed. To see the full authorized path, including a Trust Record verified with only the public keys, run the offline check. To connect your own system, see Connectors.

Clean up

The files this page created on your machine: approver/, proposal.json, proposed.json, signed.txt, approved.json, refund.json, refusal.json and tampered.json. Delete them when you are done. Keep approver/step-up.private.pem if Bob will approve again. To stop Parmana and keep everything: docker compose down. To delete the database too: docker compose down -v. Deleting ./parmana-local deletes your signing keys and API keys.