> ## Documentation Index
> Fetch the complete documentation index at: https://docs.parmanasystems.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Install to your first authorized execution in under 10 minutes, using the in-memory server.

<Info>
  **\[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.
</Info>

<Warning>
  **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](/concepts/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](/security/overview)
  for what it was.
</Warning>

## 1. Install dependencies

```bash theme={null}
npm install
```

## 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:

```bash theme={null}
npm run generate:gateway-keys
```

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`](https://github.com/pavancharak/parmana-exp/blob/main/packages/api/src/bootstrap/createGatewayKeyPair.ts)
for why this key is deliberately separate from the authorization key, and
[Deploy patterns](/guides/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:

```bash theme={null}
NODE_ENV=test \
  PARMANA_STORAGE=memory \
  PARMANA_POLICY_DIR=/absolute/path/to/policies \
  npm run dev
```

Confirm it's up:

```bash theme={null}
curl http://localhost:3000/health
# {"status":"UP"}
```

<Note>
  **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](/concepts/the-gateway).
</Note>

## 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:

```bash theme={null}
curl http://localhost:3000/version
# {"error":"authentication required"}
```

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](/api-reference/authentication)). Send it as a bearer token and the same
route succeeds:

```bash theme={null}
curl http://localhost:3000/version \
  -H "Authorization: Bearer my-secret-api-key"
# {"name":"Parmana","version":"0.4.0","api":"v1"}
```

This is real, fail-closed authentication, not a placeholder, see
[Authentication](/api-reference/authentication) for how keys are minted and rotated outside
this demo key.

## 5. Install the Python SDK

```bash theme={null}
pip install -e ./python
```

<Warning>
  **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](/api-reference/authentication):

  ```bash theme={null}
  NODE_ENV=test \
    PARMANA_STORAGE=memory \
    PARMANA_POLICY_DIR=/absolute/path/to/policies \
    PARMANA_AUTH_DISABLED=true \
    npm run dev
  ```

  This gap is tracked on [Python SDK](/sdks/python), it's a real, current limitation, not
  something this walkthrough is working around cosmetically.
</Warning>

## 6. Execute a Business Transaction

```python theme={null}
from datetime import UTC, datetime
from uuid import uuid4

from parmana import (
    Authority, Authorization, BusinessTransaction,
    BusinessTransactionMetadata, Intent, ParmanaClient, PolicyReference,
)

client = ParmanaClient(endpoint="http://localhost:3000")

transaction_id = str(uuid4())
now = datetime.now(UTC)

transaction = BusinessTransaction(
    business_transaction_id=transaction_id,
    metadata=BusinessTransactionMetadata(
        business_transaction_id=transaction_id,
        correlation_id="quickstart",
        tenant_id=None,
        source_system="python-sdk-quickstart",
        submitted_by="sdk-demo",
        submitted_at=now,
    ),
    authority=Authority(
        authority_id="authority-001", authority_type="SERVICE",
        principal_id="python-sdk", display_name="Python SDK Quickstart", issued_at=now,
    ),
    authorization=Authorization(
        authorization_id="authorization-001", authority_id="authority-001",
        purpose="Quickstart demo", issued_at=now,
    ),
    intent=Intent(
        intent_id="intent-001", authorization_id="authorization-001",
        # test:fixture-execute, not payments:execute: payments:execute
        # (vendor-payment) was removed from the repository entirely, not
        # renamed. This capability is a generic, test-only connector
        # (createTestFixtureConnector.ts) that plays the same "zero
        # external dependency" role vendor-payment used to for this
        # walkthrough — it is not a product feature to build against.
        action="test:fixture-execute", target="vendor://payments",
        parameters={"amount": 1000, "currency": "USD"}, created_at=now,
    ),
    # Still governed by the vendor-payment/2.0.0 policy, kept unchanged as
    # generic example content even though the capability name above
    # changed — policy content and capability identity are independent
    # concepts in this architecture (see [Policies and the decision](/concepts/policies-and-the-decision)).
    policy=PolicyReference(name="vendor-payment", version="2.0.0", schema_version="1.0.0"),
    signals={
        "vendorVerified": True, "invoiceVerified": True, "paymentApproved": True,
        "sufficientFunds": True, "paymentAmount": 1000, "riskScore": 5,
        # vendor-payment@2.0.0 declares boundSignals: { "vendorId": "target" } —
        # this must exactly equal intent.target, checked before policy evaluation
        # ever runs. See [Policies and the decision](/concepts/policies-and-the-decision).
        "vendorId": "vendor://payments",
    },
    status="RECEIVED",
    created_at=now,
)

trust_record = client.execution.execute(transaction)
print(trust_record.trust_record_hash)
```

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):

```
Trust Record Hash: 1a3130a6874c81cb9c178c2d14a96aa8c4104c19f71f63ca9d272196c819eb5a
```

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](/concepts/execution-trust-records) for the
complete shape.

## Next

<CardGroup cols={2}>
  <Card title="How Parmana thinks" icon="brain" href="/how-parmana-thinks">
    The concepts behind what just happened: policy, authorization, the gateway, trust records.
  </Card>

  <Card title="Verify & replay" icon="check-double" href="/verification/overview">
    Read back or re-run verification against the record you just created.
  </Card>
</CardGroup>
