Skip to main content
A ready-to-deploy Fastify HTTP server that exposes the governance runtime as an HTTP API. Use this when you want to run governance as a standalone service rather than embedding it in your application.

Install

npm install @parmanasystems/server

Starting the server

The server is started by running the package entry point. Configure it via environment variables:
VariableDescription
PORTPort to listen on (default: 3000)
HOSTHost to bind to (default: 0.0.0.0)
PARMANA_API_KEYBearer token for authentication (omit for dev mode)
PARMANA_SIGNING_KEYPEM-encoded Ed25519 private key (env mode)
PARMANA_SIGNING_KEY_PATHPath to PEM key file (disk mode)
AUDIT_DATABASE_URLPostgreSQL connection URL (enables audit persistence and /audit/* routes)
CORS_ORIGINComma-separated allowed origins, or * (default: localhost:5173,localhost:8080)
LOG_LEVELPino log level (default: info in production, debug otherwise)
If no signing key is configured the server generates an ephemeral Ed25519 keypair on startup (development only).

API endpoints

MethodPathDescription
POST/executeVerify authority before execution — returns signed ExecutionAttestation
POST/verifyVerify an ExecutionAttestation signature and provenance
POST/evaluateDry-run policy evaluation — no attestation, no replay slot
POST/simulateFull pipeline simulation — no side effects
POST/confirm-executionConfirm a real-world action against an authorization
GET/healthHealth check
GET/runtime/manifestRuntime version, hash, and supported schema versions
GET/runtime/capabilitiesCapability declarations for this runtime instance
GET/audit/decisionsDecision timeline (requires AUDIT_DATABASE_URL)
GET/audit/decisions/:executionIdDecision detail by execution ID (requires AUDIT_DATABASE_URL)
GET/audit/statsAggregate audit statistics (requires AUDIT_DATABASE_URL)
GET/audit/securitySecurity event summary (requires AUDIT_DATABASE_URL)
GET/audit/verifications/:executionIdVerification history for an execution (requires AUDIT_DATABASE_URL)

POST /execute

Request:
{
  "policyId":      "loan-approval",
  "policyVersion": "1.0.0",
  "signals": {
    "credit_score": 720,
    "loan_amount":  15000
  }
}
Response (ExecutionAttestation):
{
  "executionId":           "88a9db95-5f31-4f0d-84de-28a3ec9e3c83",
  "execution_fingerprint": "43258cff...",
  "policyId":              "loan-approval",
  "policyVersion":         "1.0.0",
  "schemaVersion":         "1.0.0",
  "runtimeVersion":        "1.0.0",
  "runtimeHash":           "4ece5616...",
  "signalsHash":           "43258cff...",
  "decision": {
    "action":            "approve",
    "requires_override": false,
    "reason":            "Credit score meets standard threshold."
  },
  "execution_state": "completed",
  "signature":       "TU6LMv5N..."
}
Rate limit: 100 requests/minute.

POST /verify

Request: the full ExecutionAttestation object returned by /execute. Response:
{
  "valid": true,
  "checks": {
    "signature_verified":  true,
    "runtime_verified":    true,
    "schema_compatible":   true,
    "bundle_verified":     true,
    "release_verified":    true,
    "trust_root_verified": true,
    "evaluator_verified":  true,
    "governed":            true
  },
  "warnings": [],
  "errors": []
}
Rate limit: 200 requests/minute.

POST /evaluate

Dry-run evaluation — computes what decision would be made without issuing an attestation or consuming a replay slot. Request:
{
  "policyId":      "loan-approval",
  "policyVersion": "1.0.0",
  "signals": { "credit_score": 720, "loan_amount": 15000 }
}
Response:
{
  "wouldDecide":      "approve",
  "ruleMatched":      "approve-standard",
  "reason":           "Credit score meets standard threshold.",
  "signalsValidated": true
}

POST /simulate

Full pipeline simulation. No attestation produced, no replay slot consumed. Request: same shape as /execute. Response:
{
  "simulation":       true,
  "decision": {
    "action":      "approve",
    "reason":      "Credit score meets standard threshold.",
    "ruleMatched": "approve-standard"
  },
  "signalsValidated":   true,
  "policyLoaded":       true,
  "wouldSign":          true,
  "attestationPreview": {
    "policyId":      "loan-approval",
    "policyVersion": "1.0.0",
    "schemaVersion": "1.0.0",
    "execution_state": "completed",
    "dry_run": true
  }
}

POST /confirm-execution

Verifies that a real-world action matches a prior governance authorization and returns a signed ExecutionIntegrityProof. Request:
{
  "attestation": { ... },
  "executedAction": {
    "type":       "LOAN_DISBURSED",
    "payload":    { "amount": 15000, "policyId": "loan-approval" },
    "executedAt": "2024-01-15T10:30:00.000Z",
    "executedBy": "disbursement-service"
  },
  "timeWindowSeconds": 300
}
Response (ExecutionIntegrityProof):
{
  "executionId":     "...",
  "authorizationId": "88a9db95-...",
  "integrityHash":   "...",
  "authorized": {
    "action":        "approve",
    "reason":        "Credit score meets standard threshold.",
    "policyId":      "loan-approval",
    "policyVersion": "1.0.0"
  },
  "executed":    { ... },
  "match":       true,
  "matchDetails": {
    "actionTypeMatch":   true,
    "payloadConsistent": true,
    "withinTimeWindow":  true,
    "timeWindowSeconds": 300
  },
  "signature":   "...",
  "confirmedAt": "2024-01-15T10:30:05.000Z",
  "verified":    true
}
Rate limit: 100 requests/minute.

GET /health

{
  "status":           "healthy",
  "runtimeVersion":   "1.0.0",
  "runtimeHash":      "4ece5616...",
  "verification":     "PASS",
  "audit_db":         false,
  "signing_mode":     "env",
  "capabilities":     ["execute", "verify", "replay-protection"],
  "supported_schemaVersions": ["1.0.0"]
}
signing_mode is one of "env" (key from environment variable), "disk" (key from file), or "ephemeral" (generated at startup).

GET /runtime/manifest

Returns the active governance runtime manifest.
{
  "version":                   "1.0.0",
  "runtimeHash":               "4ece5616...",
  "capabilities":              ["execute", "verify", "audit", "replay-protection", "ed25519-signing"],
  "supported_schemaVersions":  ["1.0.0"],
  "startedAt":                 "2024-01-15T10:00:00.000Z"
}

GET /runtime/capabilities

{
  "capabilities": [
    "execute",
    "verify",
    "audit",
    "replay-protection",
    "ed25519-signing",
    "memory-replay-store",
    "redis-replay-store"
  ]
}

Audit routes

All /audit/* routes are registered only when AUDIT_DATABASE_URL is set. They are rate-limited to 60 requests/minute and require bearer auth if PARMANA_API_KEY is set.
RouteQuery params
GET /audit/decisionslimit, offset, policyId, decision (approve/deny/any), from, to
GET /audit/decisions/:executionId
GET /audit/stats
GET /audit/securityfrom, to, limit
GET /audit/verifications/:executionId

Using with the SDK client

Use @parmanasystems/sdk-client as the companion HTTP client for this server. It provides typed methods for all endpoints.

Use Cases

Deploying a governance sidecar for a loan origination service

An NBFC runs the governance server as a sidecar next to its loan origination service. The origination service sends POST /execute with each loan application’s signals and receives a signed ExecutionAttestation. The sidecar is configured with a persistent signing key via PARMANA_SIGNING_KEY and uses AUDIT_DATABASE_URL to persist every decision to PostgreSQL for regulatory inspection:
PORT=4000
PARMANA_SIGNING_KEY="$(cat /secrets/parmana-private.pem)"
AUDIT_DATABASE_URL="postgresql://parmana:secret@db:5432/governance"
The origination service never touches the attestation — it passes it to the disbursement service which calls POST /confirm-execution after funds are transferred.

Using /evaluate for pre-flight policy checks

Before collecting full KYC documentation, a loan app uses POST /evaluate to check whether the applicant’s initial signals would qualify under the current policy. No attestation is issued and no replay slot is consumed — the response is a dry-run decision:
curl -X POST http://governance:4000/evaluate \
  -H "Content-Type: application/json" \
  -d '{"policyId":"personal-loan","policyVersion":"1.0.0","signals":{"credit_score":680,"monthly_income":65000,"employed":true,"employment_type":"salaried","blacklisted":false,"loan_amount":300000,"emi_obligations":12000}}'
If wouldDecide is "reject", the app surfaces a rejection message before the borrower completes the full application — saving time for both parties.

Audit query for SEBI reporting

A fintech running stock margin governance uses the /audit/* routes to pull decision timelines for a SEBI inspection. All routes require AUDIT_DATABASE_URL. A bearer token is set via PARMANA_API_KEY to restrict access:
# Pull all decisions for the personal-loan policy in January 2024
curl -H "Authorization: Bearer $PARMANA_API_KEY" \
  "http://governance:4000/audit/decisions?policyId=personal-loan&from=2024-01-01&to=2024-02-01&limit=1000"
Each record in the response is the original ExecutionAttestation — independently verifiable using the public key, without requiring access to the live runtime.

See also