Running Parmana on your own infrastructure? Use the self hosted
deployment: one Docker Compose command starts the
server and its own Postgres, with keys, migrations and policies handled, and
the quickstart takes you to a first signed
decision. This page is for running the server directly from source, mainly for
development.
What runs today
One Express process (packages/api/src/server.ts), single instance, no load balancer or
orchestration assumed. Signing keys are local PEM files read by FileKeyProvider. Storage
is either fully in-process (memory, non-persistent) or Postgres (postgres, or its older
name supabase; both use DATABASE_URL).
Configuration
Real.env keys (from this repo, values redacted):
PARMANA_KEY_DIR is a single directory containing every keypair FileKeyProvider reads:
default.*.pem (authorization signing/verification) and gateway.*.pem (gateway
attestation, see Gateway attestation). They are
deliberately separate keys in the same directory, not one key used for two purposes.
PARMANA_GATEWAY_KEY_ID lets you point the gateway at a differently-named key pair (for
example during rotation, run both gateway and gateway-v2 side by side, then flip the
env var) without touching default.*.pem at all.
Running it
NODE_ENV=test matters even though PARMANA_STORAGE=memory is set: two other components,
the Execution Gateway’s replay-nonce store and the caller-authentication audit trail, default
to Supabase-backed implementations outside NODE_ENV=test (createNonceStore.ts,
createCallerAuditSink.ts) and will fail closed without a real, reachable Supabase project.
PARMANA_API_KEYS above is a demo key for local development only, raw key
my-secret-api-key, allowed to use test:fixture-execute and to act as demo. Its raw key is
public: make real keys with npm run generate:api-key, see Authentication.
Generate the default and gateway keypairs first if you haven’t, see
Quickstart step 2. Without it, the server refuses to start: the gateway is
wired unconditionally (see The gateway), there is no code path that
skips needing this key.
What this deployment does not give you
- No self-service key management. Caller authentication is real and fails closed (see
Authentication), but issuing and rotating keys is an
operator action (
scripts/generate-api-key.ts), not an API call. - No KMS in this mode. With
KEY_PROVIDER=localyour private keys, bothdefault.private.pemandgateway.private.pem, are files on disk, exactly the exposure that produced the incident noted on Security. SetKEY_PROVIDER=aws-kmsto keep thedefaultkey in AWS KMS, see AWS KMS signing. - Single process, single instance. No HA, no load balancer assumed. Whether replicas
share replay-nonce state depends on
NODE_ENV:MemoryNonceStore(NODE_ENV=test, as above) is process-local, each process would accept the same authorization once, not once fleet-wide. OutsideNODE_ENV=test,SupabaseNonceStoreis durable and shared across replicas pointed at the same Supabase project, but this deployment shape is still one process behind no load balancer. - No connectors registered unless their credentials are configured. HubSpot, GitHub,
Paytm, and Slack each register only when their own environment variable is set
(
HUBSPOT_PRIVATE_APP_TOKENand similar). See The gateway for what adding a new connector requires.
Node version
The Parmana server requires Node 24 or later. The rootpackage.json sets engines.node to >=24, the
Dockerfile builds from node:24, and CI runs Node 24. Use Node 24 or later everywhere, including for
ML-DSA-65 (post quantum signing), which needs Node 24 or later for native node:crypto support. The TypeScript SDK
(@parmana/sdk) is separate and only needs Node 20 or later.