Skip to main content

ExecutionId design

executionId is the most important field in every execute request. It is your business transaction identifier and the key for replay protection. Rules:
  • Unique per business transaction each claim, payment, refund, or authorization must have its own executionId
  • Stable the same business event must always use the same executionId. Do not regenerate it on retry
  • Namespaced prefix with a domain to avoid collisions across policy types: claim-CLM-2024-00441, payment-TXN-20240115-0001
  • Derived from your business ID generate it from your business event’s canonical ID, not from crypto.randomUUID() at the call site
// Good — derived from the canonical business event ID
const executionId = `claim-${claim.id}`;
const executionId = `payment-${transaction.id}`;
const executionId = `refund-${refundRequest.id}`;

// Bad — new UUID every call means replay protection has no effect
const executionId = crypto.randomUUID();  // different on each call
What happens if executionId is reused? The second execute call throws [INV-013@replay]. This is correct behavior. If a system retries with the same ID, the replay store blocks it — which is the intended protection.

Signal hygiene

Signals are the inputs to the authority verification process. They are hashed (the execution_fingerprint) and included in the signed attestation, making them part of the proof. Rules:
  • No personally identifiable information (PII) names, email addresses, SSNs, and similar data should not be in signals. Use member IDs, account IDs, and clinical codes
  • No large payloads the request body limit is 64 KB. Signals should be structured scalar values, not raw documents or images
  • Normalize before sending floating point values should be rounded consistently. String values should be lowercased or normalized to a canonical form before sending
  • Be consistent the same business scenario must produce the same signals every time. If signals vary (due to upstream rounding or formatting), the execution_fingerprint will differ
// Good — normalized, no PII
const signals = {
  claimAmount: Math.round(rawClaim.amount * 100) / 100,  // 2 decimal places
  customerTier: rawClaim.tier.toLowerCase(),
  priorFraudSignals: Boolean(rawClaim.fraudFlags?.length),
};

// Bad — PII in signals, inconsistent types
const signals = {
  customerName: "John Smith",      // PII — do not include
  claimAmount: rawClaim.amount,    // may vary due to float precision
  tier: rawClaim.tier,             // "Gold" vs "gold" changes the fingerprint
};

Always verify after execute

Verification is lightweight (cryptographic only no database call) and proves the attestation is intact. Always verify before authorizing the real-world action.
const attestation = await client.execute({ ... });
const verification = await client.verify(attestation);

if (!verification.valid) {
  // Do not proceed. Log, alert, and investigate.
  throw new Error("Governance attestation verification failed");
}

// Only now proceed with the authorized action
if (attestation.decision.action === "approve") {
  await executePayment(...);
}

Check execution_state, not just decision.action

A decision can be action: "approve" but still be in execution_state: "pending_override" if the policy rule sets requires_override: true. Check both:
const isAuthorized =
  attestation.execution_state === "completed" &&
  attestation.decision.action === "approve";

const needsReview =
  attestation.execution_state === "pending_override";

Use RedisReplayStore in production

MemoryReplayStore does not persist across process restarts and does not work with multiple server instances. In any production environment, use RedisReplayStore:
import { RedisReplayStore } from "@parmanasystems/core";

const store = new RedisReplayStore(process.env.REDIS_URL!, {
  reservationTtlSeconds: 300,
  failedTtlSeconds: 30,
});
MemoryReplayStore is only appropriate for local development, single-process integration tests, and ephemeral test environments. If you are using the Parmana server via Docker, Redis is configured automatically.

Store attestations

The signed attestation is your proof of governance. Store it alongside your business record:
await db.claims.update({
  where: { id: claimId },
  data: {
    governanceAttestation: JSON.stringify(attestation),
    governanceStatus: attestation.decision.action,
    governedAt: new Date(),
  },
});
Attestations are self-contained JSON. They can be verified at any future point by any party with the public key no server access needed.

Handle pending override asynchronously

When execution_state === "pending_override", the execution is blocked. Design your review workflow to be event-driven rather than polling-based:
  1. Store the executionId and mark the record as pending
  2. Route it to your review queue (email, Slack, JIRA, etc.)
  3. When the reviewer acts, call POST /override
  4. Update your record with the resolution
Do not block a synchronous request waiting for override resolution overrides are human-loop processes that may take minutes to hours.

Graceful degradation

Decide upfront what your system does if the governance runtime is unreachable:
async function governedExecute(request: ExecuteRequest) {
  try {
    return await client.execute(request);
  } catch (err) {
    if (err instanceof TypeError) {
      // Network unreachable — fail closed for critical paths
      throw new Error("Governance runtime unreachable. Action blocked.");
    }
    throw err;
  }
}
For any path where the governed action has significant financial or safety consequences, fail closed. Do not proceed without a valid attestation.

Production checklist

  • executionId is derived from a stable, canonical business event ID
  • Signals are normalized and contain no PII
  • Verification (client.verify()) is called after every execute()
  • Both execution_state and decision.action are checked before authorizing
  • RedisReplayStore is configured (not MemoryReplayStore)
  • Attestations are stored in your database alongside business records
  • Pending override flow is implemented (do not ignore pending_override state)
  • Error handling distinguishes replay errors (no retry) from transient errors (retry with backoff)
  • Network failure handling fails closed for critical paths
  • PARMANA_API_KEY is loaded from a secrets manager, not hardcoded