The patterns on this page — the audit-log schema, the metric names, the rate
limiter — are illustrative starting points for your own integration, not
claims about what this repository’s own infrastructure runs or has test
coverage for. The SDK behavior itself (the
Configuration/RetryPolicy
shape, the error classes, the retry semantics) is real and verified — see
TypeScript SDK for what’s actually tested.Deployment architecture
Environment variables
A production-safe client wrapper
transport. From 1.1.6 the client builds its own HttpTransport from the configuration you pass, so timeout, retryPolicy, apiKey and userAgent all apply. If you supply your own transport (1.1.5 and earlier require one), build it from the same configuration object. HttpTransport reads timeout and retryPolicy from the configuration it is constructed with. If you write transport: new HttpTransport({ endpoint, apiKey }) next to a retryPolicy on the client, the transport never sees the retry policy and nothing is retried, with no error. Tested against a server that returns 503 twice: with the transport built from a partial configuration there is 1 request and an InternalServerError, and with the transport built from the full configuration there are 3 requests and success.
Retries are off unless retryPolicy.enabled is true and maxAttempts is above zero. maxAttempts counts retries after the first attempt, so maxAttempts: 3 makes up to 4 requests. Only GET requests are ever retried, on a network failure, a timeout, a 429 or a 5xx. A POST, including execute(), is never retried, and a policy rejection is a final answer that must never be retried.
See TypeScript SDK configuration and behavior for every option and default.
Error handling: fail closed
Every non-2xx response the Runtime returns maps to a specific, typed error — there is no “unknown error, assume success” path:
Two kinds of
InternalServerError need different handling (1.1.3). Check error.serverCode. On
SIGNING_UNAVAILABLE or EXECUTION_INTENT_UNAVAILABLE (503) nothing was executed, so retry later with a new
businessTransactionId. On EXECUTION_RECORD_INCOMPLETE (500) the action was released and only its signed record
failed, so never resubmit it under a new id: tell the operator, who can rebuild the record without repeating the action
(see Execution Intents). The Execution Intent methods (executionIntent, unfinalizedExecutionIntents, finalizeExecutionIntent, resolveExecutionIntent) are in the SDK source and are not in the published 1.1.6, so until the next release the operator uses the HTTP API.
POST /execute is not itself retried by the SDK’s retry policy — see
TypeScript SDK for exactly which
operations are retry-eligible. If you need your own retry around execute() (for a
NetworkError/TimeoutError only, never for ExecutionRejectedError), reuse
businessTransactionId on the retry — it’s the server’s idempotency key, so a retried
execute() against a transaction that actually succeeded returns the original result
instead of executing twice.
Audit logging pattern
EveryExecutionTrustRecord is already Parmana’s own durable, signed audit record — but
most teams also want a local, queryable copy alongside their own application logs:
ExecutionTrustRecord’s
signature (see Verify independently), which doesn’t depend
on this table being correct.
Monitoring pattern
Track, at minimum, the shape of decisions your service is producing — a spike in denials or a rising Parmana-call latency is usually the first signal something is wrong upstream of Parmana (a bad policy deploy, a misconfigured signal), not inside the SDK itself:error outcomes (network/timeout/internal, as opposed to denied) above
zero for more than a few minutes, and p99 latency on execute() growing past your
PARMANA_TIMEOUT_MS.
Rate limiting pattern
The Runtime itself does not currently return a429/rate-limit response the SDK maps to a
dedicated error (see TypeScript SDK for
error classes that are defined but not yet thrown by any real condition) — if your own
call volume needs shaping, do it client-side:
Testing in production
A smoke test to run right after every deploy, against the real endpoint you just deployed:Deployment checklist
-
PARMANA_ENDPOINTandPARMANA_API_KEYset from your secrets manager, not committed to source (see Deploy patterns). -
retryPolicyconfigured deliberately — confirm what’s idempotent-safe to retry versus what isn’t (see the table above). - Every
client.execute()/client.createTransaction()call site fails closed: a caught, unrecognized error is treated as not-approved, never as approved. -
ExecutionRejectedErroris never silently swallowed — it’s a real policy decision your caller needs to see. -
businessTransactionIdreuse on retry is intentional (idempotency), not accidental. - A signature-verification path exists independent of this SDK (see Verify independently) for anything you’ll need to prove later without trusting your own logs.
- Smoke test wired into your deploy pipeline, hitting
health()/version()at minimum. - Monitoring on denial rate and error rate, not just uptime.
Next
TypeScript SDK reference
The full, verified error taxonomy, model types, and test suite this page
builds on.
Deploy patterns
Running the Parmana Runtime itself in production, not just the SDK client.
Verify independently
Re-verify a trust record’s signature without trusting this SDK or your own
logs.
Limitations
What Parmana does and does not guarantee — read before writing an incident
runbook.