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.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:| Operation | Why 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 evaluation | Generates a new UUID on every call |
Locale-sensitive sort() | Different default collation on different operating systems |
| Environment variable reads inside evaluation | Value differs across environments and deployments |
| Network calls during evaluation | External 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 inpackages/execution/src/sealed-vm.ts:
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 viagoverningTime() rather than read from the wall clock:
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
| Code | Invariant | Boundary |
|---|---|---|
INV-001 | Canonical serialization produces identical bytes for identical inputs | canonicalize |
INV-004 | Execution time is injected deterministically — no wall-clock reads inside the execution scope | canonicalize, execute |
INV-047 | Canonical serialization uses explicit UTF-8 encoding | canonicalize |
INV-048 | Unicode normalization is stable across canonicalization calls | canonicalize |
INV-049 | Canonical JSON sorts object keys recursively and preserves array order | canonicalize |
INV-051 | Numeric values canonicalize identically regardless of trailing zeros | canonicalize |
INV-052 | Object insertion order does not affect canonical form or content-address hash | canonicalize |
INV-053 | Array element order is preserved through canonicalization | canonicalize |
INV-054 | NaN and Infinity serialize to null; undefined fields are omitted | canonicalize |
INV-057 | SHA-256 content-address is stable for identical content across calls | canonicalize |
INV-073 | Canonical evaluation source files contain no network calls | execute |
INV-077 | All failure modes are deterministic: same invalid input always produces the same error | verify, replay, execute |
Why this matters for governance
If a decision is non-deterministic:- Two identical inputs produce different decisions — the system is lying to at least one caller
- An auditor cannot reproduce the decision from the stored attestation
- The
execution_fingerprintcannot be independently recomputed to check for tampering - The cryptographic attestation becomes unverifiable in practice
- Regulatory proof-of-decision requirements cannot be met
Verification: proving determinism after the fact
Theexecution_fingerprint field in every attestation is the SHA-256 of the canonical signal set:
Signals and determinism
Signal types are constrained tointeger, 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 recomputessha256(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 sameexecution_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 patchespackages/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
- Cryptographic Invariants — what is signed and how
- Signal Types — why float is not a signal type
- Invariants Overview — complete invariant registry