Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.manthan.systems/llms.txt

Use this file to discover all available pages before exploring further.

The replay problem

Without replay protection, the same governed execution can run more than once:
  • A loan approval is re-submitted and a second disbursement is triggered
  • A fraud block decision is replayed to authorize a transaction it was meant to block
  • An attacker captures a valid approval and replays it to authorize a different action
  • A bug in the calling system submits the same request twice under network timeout
Replay protection makes this impossible at the governance layer. The same execution_fingerprint — the SHA-256 hash of the canonical signal set — can only be consumed once.

The execution fingerprint

The execution_fingerprint is computed before any evaluation happens:
const execution_fingerprint = sha256(canonicalize({
  policyId,
  policyVersion,
  signals,
}));
It is deterministic: the same policy and signals always produce the same fingerprint. Two calls with identical inputs will produce identical fingerprints, and the replay store will block the second one. This means replay protection is signal-level — not call-level. Changing any signal value, even slightly, produces a different fingerprint and allows the execution.

Two-phase commit protocol

executeFromSignals uses a two-phase commit protocol when the replay store supports it:
Phase 1 — RESERVE (before evaluation)
  ├── Compute execution_fingerprint
  ├── Check: has this fingerprint been seen before?
  │     YES → throw [INV-013@replay] immediately. Nothing has happened yet.
  │     NO  → reserve the fingerprint in the store (status: RESERVED)
  └── Continue to evaluation

Evaluation + Signing
  ├── evaluate policy against signals
  ├── issue execution token
  ├── sign token
  └── executeDecision → ExecutionAttestation

Phase 2 — CONFIRM (after signing)
  ├── Mark fingerprint as confirmed (status: CONFIRMED)
  └── Return attestation

On failure between RESERVE and CONFIRM:
  ├── Mark fingerprint as FAILED
  └── TTL expiry allows retry
If anything throws between reserve and confirm (e.g. signing fails), the fail() path is called:
try {
  await replayStore.reserve(execution_fingerprint);
  reserved = true;
  // ... evaluation, signing ...
  await replayStore.confirm(execution_fingerprint);
  return attestation;
} catch (err) {
  if (reserved && replayStore.fail) {
    await replayStore.fail(execution_fingerprint);
  }
  throw err;
}

Replay store implementations

MemoryReplayStore — development only

import { MemoryReplayStore } from "@parmanasystems/core";
const store = new MemoryReplayStore();
  • In-process, not persistent
  • Lost on process restart
  • Never use in production — any restart clears all replay protection

RedisReplayStore — production

import { RedisReplayStore } from "@parmanasystems/core";

const store = new RedisReplayStore(
  process.env.REDIS_URL ?? "redis://localhost:6379"
);
  • Persistent across restarts
  • Atomic operations via Redis SET NX
  • Configurable TTL for RESERVED and FAILED states
  • Supports full two-phase commit: reserve, startExecution, confirm, fail, expire, override
  • Supports getReplayState() for observability

Custom — implement ReplayStore

import type { ReplayStore } from "@parmanasystems/execution";

class MyReplayStore implements ReplayStore {
  // Required
  async hasExecuted(fingerprint: string): Promise<boolean> { ... }
  async markExecuted(fingerprint: string): Promise<void> { ... }

  // Optional — enables two-phase commit
  async reserve?(fingerprint: string, ttlSeconds?: number): Promise<void> { ... }
  async confirm?(fingerprint: string): Promise<void> { ... }
  async fail?(fingerprint: string, ttlSeconds?: number): Promise<void> { ... }
}
The minimum interface is hasExecuted + markExecuted. The 2PC methods are optional and backward-compatible.

Replay states

The full lifecycle of a fingerprint in the replay store:
StateMeaning
(absent)Not yet seen — execution may proceed
RESERVEDExecution in progress — another call will be blocked
EXECUTINGEvaluation has started
CONFIRMEDExecution completed successfully — terminal, blocks forever
FAILEDExecution failed — retryable after TTL
EXPIREDReservation TTL elapsed without confirm or fail — retryable
OVERRIDDENManually overridden by governance authority — terminal
Only CONFIRMED and OVERRIDDEN are terminal states. FAILED and EXPIRED are retryable — the same fingerprint can be re-executed after they expire.

Replay invariants

CodeInvariant
INV-013Replay protection is always enforced — execution_fingerprint is single-use and non-configurable
INV-059Replay domain is explicit: every fingerprint in the store was consumed by a real execution
INV-013 is structurally enforced — there is no configuration option or flag to disable it. The replay store check runs unconditionally in executeFromSignals.

Replay vs double execution

These are different problems:
ReplayDouble execution
Same execution_fingerprint submitted twiceTwo different fingerprints for the same real-world event
Blocked by replay storeNot detected by Parmana
Attacker or bug resubmits same signalsBug or design issue generates two different signal sets
Prevented at governance layerPrevented by your system design
If two different signal sets can authorize the same real-world action, Parmana will produce two valid attestations. Preventing this requires application-level deduplication (e.g. idempotency keys on your disbursement API).

confirmExecution replay protection

confirmExecution has its own replay protection, independent of the execution fingerprint:
// Inside confirmExecution:
const confirmKey = "confirm:" + attestation.executionId;
const alreadyConfirmed = await store.hasExecuted(confirmKey);

if (alreadyConfirmed) {
  throw new Error(
    `INTEGRITY_ALREADY_CONFIRMED: executionId=${attestation.executionId} has already been confirmed`
  );
}
An authorization (ExecutionAttestation) can be confirmed at most once. A second call to confirmExecution with the same attestation throws INTEGRITY_ALREADY_CONFIRMED, regardless of what action is reported.

Use Cases

Blocking a duplicate loan disbursement

A lending system submits the same personal loan application twice due to a network timeout and retry logic. Both submissions carry identical signals: { monthly_income: 82000, loan_amount: 500000, credit_score: 710, employed: true }. The first call reserves and then confirms the execution_fingerprint in RedisReplayStore. The second call, arriving milliseconds later, computes the same fingerprint and hits INV-013 during the RESERVE phase — before evaluation runs, before any attestation is issued. The disbursement system catches the InvariantViolation, reads err.report.invariant_id === "INV-013", and returns the original attestation to the caller. No double disbursement occurs.

Fraud block bypass via signal replay

A fraudster captures a valid UPI transaction approval signal set ({ transaction_amount: 50000, is_foreign: false, risk_score: 12 }) and attempts to replay it to authorize a subsequent blocked transaction. The execution_fingerprint for the original approval was already marked CONFIRMED in the replay store. The replayed call throws INV-013 immediately — the same signals cannot produce a second governed decision. The attacker cannot bypass the fraud block by reusing a previously captured approval.

Retry-safe insurance claim processing

An NBFC-MFI processes motor insurance claims and must guarantee each approved claim is disbursed exactly once. The claims system uses confirmExecution with INTEGRITY_ALREADY_CONFIRMED detection: if the confirmation key for a given executionId already exists in the store, it means the claim was already confirmed and the disbursement service can safely return the cached proof rather than re-processing. The two-phase commit in executeFromSignals plus confirmExecution’s own replay check form a complete double-confirmation barrier.

See also