Skip to main content
This page is written to be followed literally by an AI agent or a person. It contains no options to weigh. If your situation is not covered, stop and ask the operator. Do not guess. The human readable version, with more explanation, is the Production deployment runbook. Every variable is defined in the Environment variable reference. If the two disagree with this page, the environment reference wins, and you must tell the operator.

Rules that apply to every step

  1. Do the steps in order. Do not skip one.
  2. After each step compare what you saw with Expect. If it differs, follow If not and stop.
  3. Never print, log, commit or paste a secret. Secrets are: raw API keys, bearer keys, private key files, DATABASE_URL, PARMANA_KEY_MATERIAL_JSON, connector secrets and tokens.
  4. Never set PARMANA_AUTH_DISABLED, and never set NODE_ENV to test or development on a production server.
  5. Never deploy to production, write production environment variables or publish anything unless the operator has told you to in this session. Preparing the values and the commands is always allowed.
  6. The maker and the checker in step 8 must be two different human identities, and both keys must have credentialHolderType USER. You act as the maker only if the operator gave you the maker’s key and said so. You must never approve your own proposal. The checker is a human.

Inputs you need before you start

If any input is missing, stop and ask for it.

Step 1: Generate the signing keys

Run from REPO:
Expect: the directory keys holds default.private.pem, default.public.pem, gateway.private.pem and gateway.public.pem. If not: a command reports that a file already exists. Do not add --force. Stop and ask the operator whether those keys are the ones in use.

Step 2: Create the database schema

Ask the operator to apply the schema, or apply it if you have Supabase CLI access. There is exactly one accepted method for an agent: run supabase db push in a directory linked to the project. Otherwise give the operator the file scripts/apply-all-migrations.sql and ask them to run it in the Supabase SQL Editor. Expect: the query select count(*) from consumed_nonces; returns a number. If not: the query says the table does not exist. The schema is not applied. Stop and report it. If a later request says PGRST205, run NOTIFY pgrst, 'reload schema'; and try once more.

Step 3: Generate the caller keys

Replace the capitalised words with the input values. Each command prints a raw key, and a JSON entry. The third also prints a step up private key. Expect: three JSON entries, each with callerId and a keyHash of 64 lowercase hexadecimal characters. The second and third have credentialHolderType set to USER, and the third has a stepUpPublicKey. If not: stop and report the output, with any key values removed. Do the following with the printed values, and nothing else:
  1. Hand the first raw key to the caller through the channel the operator names.
  2. Hand the maker bearer key to the maker, and the checker bearer key and the step up private key to the checker, and to nobody else.
  3. Keep the three JSON entries. Join them into one array. That array is PARMANA_API_KEYS.

Step 4: Set the environment on the host

Set exactly these variables on the host, in the Production environment: If the host cannot mount the four key files, also set PARMANA_KEY_MATERIAL_JSON to {"default":{"privateKeyPem":"<contents>","publicKeyPem":"<contents>"},"gateway":{"privateKeyPem":"<contents>","publicKeyPem":"<contents>"}}, with the file contents from step 1. If the operator wants a connector, also set its variables from the connector section. For the refund connector set both PAYTM_CONNECTOR_URL (it must start with https://) and PAYTM_CONNECTOR_SHARED_SECRET, or neither. Expect: the host lists all six variables, and does not list PARMANA_AUTH_DISABLED. If not: stop and report which variable is wrong. Never repeat a secret value in your report. Rule 5 applies. If the operator has not told you to write to the host, print the variable names and stop, and let the operator set the values.

Step 5: Deploy

Environment variables are read once at startup, so a change to any of them needs a new deploy. Expect: the deploy finishes and the host shows the new version live. If not: read the host’s log. A startup failure names the variable it dislikes. Look the message up in the startup errors table, fix that one thing and deploy again. Do not change anything else.

Step 6: Check the server

Replace URL with the input value.
Expect: If not:

Step 7: Copy the policies into the database

Run from REPO, with DATABASE_URL and PARMANA_POLICY_DIR=./policies set in your shell:
Expect: one line per policy, then Backfilled N policies into the 'policies' table. If not: stop and report the error message.

Step 8: Approve each policy

For every policy the operator lists, in the form POLICY_NAME and POLICY_VERSION:
  1. You never hold the checker’s keys. You act as the maker only if the operator gave you a maker’s bearer key and said so. Otherwise ask the operator to propose.
  2. Propose the change as the maker, following the Policy governance guide, step 1. The response has status 201 and a pendingPolicyChangeId.
  3. Tell the checker the pendingPolicyChangeId and ask them to review it and approve it with their own key, as in steps 2 to 4 of that guide, or with scripts/local-review-action.ts.
  4. Wait until the checker confirms.
Expect: GET URL/policies/pending-changes?status=PENDING_APPROVAL no longer lists your pendingPolicyChangeId. If not: Do not run scripts/backfill-legacy-policy-approvals.ts. It records a system approval that is not a human decision. Only the operator may choose that, in words. Then run the check. Set SUPABASE_URL and SUPABASE_ANON_KEY in your shell first. They are for this script only:
Expect: the script exits with code 0. If not: it lists each live policy without a matching approval. Approve those, and run the check again.

Step 9: Send one real request

Follow Integrate Parmana: specification for AI agents with a small, harmless action that the policy allows. The action needs a configured connector from step 4. Expect: the response is a signed Execution Trust Record. Sending the same businessTransactionId again returns 409. If not: a 500 with no code and the server log line No connector registered for capability means the connector is not configured. Nothing was executed. Configure it as in step 4 and deploy again. For anything else use the table in that page. Never send the same action again under a new id after a 500 with the code EXECUTION_RECORD_INCOMPLETE. That action was released. Tell the operator.

Step 10: Report

Give the operator one message with:
  1. The base URL and the answers from step 6.
  2. The number of policies approved and the result of the check in step 8.
  3. The result of step 9.
  4. Anything you skipped, and why.
Do not include any secret in the message.

When to stop and ask

Stop and ask the operator when:
  1. An input is missing, or two inputs conflict.
  2. A command tells you a file already exists, and you were not told to overwrite it.
  3. You are asked to deploy, write production variables or publish, and you were not told to.
  4. Anything in Expect differs and its If not row says to stop.
  5. You would have to guess a value, a name or a person.