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 determinism guarantee

Same governed inputs always produce the same governed decision. This is not a promise. It is enforced — statically by the CI invariant gate and at runtime by the sealed execution scope.
signals: { monthly_income: 82000, employed: true }
policy:  personal-loan v1.0.0

→ decision: { action: "approve", requires_override: false, reason: "income_verified" }

On any machine.
At any time.
By any runtime that implements the governance protocol.
The execution_fingerprint is the SHA-256 hash of the canonical signal set plus the policy identity. If the decision changes for the same fingerprint, the attestation is wrong.

What breaks determinism

Each of the following would make it impossible to reproduce a decision from the same inputs:
OperationWhy it breaks determinism
Date.now()Returns a different millisecond timestamp on every call
new Date()Same — wall-clock time is non-deterministic
Math.random()Returns a different value on every call
crypto.randomUUID() inside evaluationGenerates a new UUID on every call
Locale-sensitive sort()Different default collation on different operating systems
Environment variable reads inside evaluationValue differs across environments and deployments
Network calls during evaluationExternal state changes between calls

The sealed execution scope

Parmana enforces determinism by confining the evaluation engine to a sealed scope — a set of source files that are statically prohibited from accessing non-deterministic globals. The sealed files are declared in packages/execution/src/sealed-vm.ts:
export const SEALED_SCOPE_FILES = [
  "packages/execution/src/execute.ts",
  "packages/execution/src/pipeline.ts",
  "packages/execution/src/canonical-signing.ts",
  "packages/bundle/src/canonicalize.ts",
  "packages/bundle/src/hash.ts",
] as const;
The forbidden globals are:
export const FORBIDDEN_GLOBALS = [
  "Date.now",
  "Math.random",
] as const;
The CI invariant gate (scripts/invariants/ci-invariant-gate.ts) scans all sealed-scope files at build time and fails the build if any forbidden global appears. This check runs on every commit via the pre-commit hook.

Governing time

When a timestamp is needed inside execution (e.g. for audit records), it is injected deterministically via governingTime() rather than read from the wall clock:
// sealed-vm.ts
export function governingTime(provided?: string): string {
  if (provided && provided.length > 0) {
    return provided;   // use the injected deterministic value
  }
  return new Date().toISOString(); // fallback — only valid outside sealed scope
}
governingTime() may only be called from outside the sealed scope (e.g. audit.ts, dry-run paths, tests). Inside execute.ts and pipeline.ts, time must be injected through ExecutionContext.

Determinism invariants

CodeInvariantBoundary
INV-001Canonical serialization produces identical bytes for identical inputscanonicalize
INV-004Execution time is injected deterministically — no wall-clock reads inside the execution scopecanonicalize, execute
INV-047Canonical serialization uses explicit UTF-8 encodingcanonicalize
INV-048Unicode normalization is stable across canonicalization callscanonicalize
INV-049Canonical JSON sorts object keys recursively and preserves array ordercanonicalize
INV-051Numeric values canonicalize identically regardless of trailing zeroscanonicalize
INV-052Object insertion order does not affect canonical form or content-address hashcanonicalize
INV-053Array element order is preserved through canonicalizationcanonicalize
INV-054NaN and Infinity serialize to null; undefined fields are omittedcanonicalize
INV-057SHA-256 content-address is stable for identical content across callscanonicalize
INV-073Canonical evaluation source files contain no network callsexecute
INV-077All failure modes are deterministic: same invalid input always produces the same errorverify, replay, execute

Why this matters for governance

If a decision is non-deterministic:
  1. Two identical inputs produce different decisions — the system is lying to at least one caller
  2. An auditor cannot reproduce the decision from the stored attestation
  3. The execution_fingerprint cannot be independently recomputed to check for tampering
  4. The cryptographic attestation becomes unverifiable in practice
  5. Regulatory proof-of-decision requirements cannot be met
Determinism is the foundation of verifiability. Without it, the cryptographic guarantees are hollow.

Verification: proving determinism after the fact

The execution_fingerprint field in every attestation is the SHA-256 of the canonical signal set:
const execution_fingerprint = sha256(canonicalize({
  policyId,
  policyVersion,
  signals,
}));
An independent auditor can recompute this hash from the stored signals and compare it to the attestation. If they match, the decision was computed from the inputs claimed.

Signals and determinism

Signal types are constrained to integer, boolean, string, and enum specifically to preserve determinism. float and number are rejected because IEEE 754 floating-point arithmetic produces different bit representations on different CPUs, runtimes, and JavaScript engines. For values that require fractions, use scaled integers: rate_bps: 375 instead of rate: 3.75. See Signal Types for the complete reference.

Use Cases

Reproducing a credit decision one year later

A borrower challenges a loan rejection 11 months after the fact. The NBFC retrieves the stored attestation and the signal set from its audit database. The auditor independently recomputes sha256(canonicalize({ policyId, policyVersion, signals })) and compares it to the attestation’s execution_fingerprint. The values match — proving the stored signals are exactly what was evaluated, with no post-hoc modification. This works because INV-001 guarantees canonical serialization is byte-identical for the same inputs on any machine.

Verifying determinism across runtime upgrades

Before deploying a new Parmana runtime version, the team runs the existing test suite against a snapshot of production signal sets. Every test case is verified to produce the same execution_fingerprint and the same decision.action as the previous runtime. Because all non-deterministic operations are excluded from the sealed scope (Date.now, Math.random), the evaluation output changes only when the policy rules or the signals change — never due to timing or environment.

Detecting a forbidden global in a policy evaluator patch

A developer patches packages/execution/src/execute.ts and accidentally adds const ts = Date.now() inside the evaluation scope. The CI invariant gate (scripts/invariants/ci-invariant-gate.ts) scans the sealed files, detects Date.now in execute.ts, and fails the build before the change is merged. This enforces INV-004 structurally — determinism cannot be broken by a well-intentioned but incorrect change.

See also