Skip to main content
[AVAILABLE], commands re-run against a live local server on 2026-07-29 (commit 0e69ed4), including python/examples/quickstart/run.py itself. Updated (2026-08-09, not re-run live as part of this specific update): the example capability below was changed from payments:execute to test:fixture-execute because payments:execute (vendor-payment) was removed from the repository entirely, not merely renamed — see the note in step 6. The code below was checked against current source for accuracy, but this specific pass did not itself start a live server and re-capture output; treat step 7’s captured hash as illustrative of the shape of a real response, not a value you’ll reproduce.
Step 6’s transaction declares a vendorId signal. vendor-payment@2.0.0 (the policy still governing this example, see the note in step 6) declares boundSignals: { "vendorId": "target" } (see Policies and the decision): SignalIntentBinder rejects any transaction where a declared signal doesn’t exactly match its bound intent field, checked before policy evaluation ever runs. This closed a real, previously live bypass — see Security for what it was.

1. Install dependencies

2. Generate a local Gateway keypair

The Execution Gateway signs its own attestations with a keypair separate from the authorization-verification key (keys/default.*.pem, already committed for local dev). No key is generated automatically, you have to create one:
This runs scripts/generate-keypair.ts --algorithm ed25519 --key-id gateway, writing keys/gateway.private.pem / keys/gateway.public.pem. keys/ is gitignored, this stays local. See createGatewayKeyPair.ts for why this key is deliberately separate from the authorization key, and Deploy patterns for how to manage it outside local dev.

3. Start the Runtime locally

The committed .env defaults to Supabase-backed storage. To run fully locally with no external dependency, override storage to memory and set NODE_ENV=test. Both are required: PARMANA_STORAGE=memory alone only affects where Business Transactions and Trust Records are stored, two other components, the Execution Gateway’s replay-nonce store (createNonceStore.ts) and the caller-authentication audit trail (createCallerAuditSink.ts), independently default to Supabase outside NODE_ENV=test and will fail closed against this repo’s demo Supabase credentials otherwise. With NODE_ENV=test, a generic, test-only connector (test-fixture, packages/api/src/bootstrap/createTestFixtureConnector.ts) registers automatically, no credential environment variable required — unlike the one real connector this server can also register (hubspot), which needs real API credentials and is not this walkthrough’s subject:
Confirm it’s up:
The Execution Gateway is wired into this server unconditionally (createExecutionSystem() always returns createExecutionGateway(), packages/api/src/bootstrap/createExecutionSystem.ts). Every POST /execute is independently re-verified and routed through a real Connector. An action with no registered connector fails closed with "No connector registered for action", it does not silently skip enforcement. See The gateway.

4. Every other route requires a bearer key

/health is the one route exempt from caller authentication (along with /ready, /openapi.yaml, and /documentation). Everything else, including /version, fails closed with a 401 before a Business Transaction is even constructed:
The committed .env ships one demo caller key for local development, callerId: "demo", raw key my-secret-api-key (only its SHA-256 hash is ever stored, see Authentication). Send it as a bearer token and the same route succeeds:
This is real, fail-closed authentication, not a placeholder, see Authentication for how keys are minted and rotated outside this demo key.

5. Install the Python SDK

The Python SDK cannot send the bearer key above yet. ParmanaClient.__init__ (python/parmana/client.py) has no api_key parameter, and HttpTransport never sets an Authorization header, checked directly against source. Submitting the transaction below against the authenticated server from step 4 gets a 401. To run this specific example, restart the server from step 3 with PARMANA_AUTH_DISABLED=true added, local development only, never in a real deployment, see Authentication:
This gap is tracked on Python SDK, it’s a real, current limitation, not something this walkthrough is working around cosmetically.

6. Execute a Business Transaction

Full runnable version: python/examples/quickstart/run.py, this is the same transaction it sends (updated to match this page; syntax-checked, not re-run against a live server as part of this pass).

7. Real output

Captured from an actual run, 2026-07-29, against the transaction as it existed then (action: "payments:execute", before that capability was removed — see step 6’s note; IDs and hashes differ on every run regardless):
The full ExecutionTrustRecord includes the signature block, executions[0].decision (outcome: APPROVED, evaluated by the vendor-payment policy, unchanged), executions[0].evidence (what the connector actually did, including a connectorEvidenceHash), and an initial verifications / receipts history. See Trust Record for the complete shape.

Next

How Parmana thinks

The concepts behind what just happened: policy, authorization, the gateway, trust records.

Verify & replay

Read back or re-run verification against the record you just created.