Skip to content
DOCUMENT / STATEMIRROR

Diagrams

These diagrams explain the decision evidence boundary: applications read state, make decisions, emit evidence, and StateMirror preserves that evidence for later integrity verification.

SYSTEM
StateMirror Core
SCOPE
Decision evidence

The application owns the decision.

#

StateMirror preserves decision-time evidence. The application reads state, applies business logic, owns the decision and action and submits an evidence snapshot for later inspection.

FIG 01 / SCHEMATICDECISION_BOUNDARY
Application-owned action and separate evidence preservationThe application reads external or application state, decides and owns the action. A separate branch submits evidence to StateMirror, which preserves it in PostgreSQL. StateMirror does not decide or enforce the action. Branches show ownership, not required execution order.APPLICATION-OWNEDREADSUBMITSTOREExternal / application statechosen inputsApplicationreads + decidesActionperform / denyEvidencesubmitted JSONStateMirrorpreserves evidencePostgreSQLsnapshot storage
Application-owned action and separate evidence preservationThe application reads external or application state, decides and owns the action. A separate branch submits evidence to StateMirror, which preserves it in PostgreSQL. StateMirror does not decide or enforce the action. Branches show ownership, not required execution order.APPLICATION-OWNEDREADSUBMITSTOREExternal / app statechosen inputsApplicationreads + decidesActionperform / denyEvidencesubmitted JSONStateMirrorpreserves evidencePostgreSQLsnapshot storage
State is queried. Decisions are made. Actions remain application-owned. Branches show separate responsibilities, not a required order of execution.

Submit once. Retrieve and verify later.

#
FIG 02 / SCHEMATICEVIDENCE_LIFECYCLE
Submission, receipt, retrieval and verification over HTTPOn submission the application sends evidence to StateMirror. StateMirror stores the snapshot in PostgreSQL and returns a receipt to the application. Later, an application or operator makes separate retrieval or verification requests. StateMirror reads stored records and runs integrity verification in Core, returning a snapshot or verification result. PostgreSQL stores records; it does not perform verification. The HTTP path shown does not independently anchor history.SUBMITRETURNSTORERETRIEVEVERIFYRETURNApplicationsubmitted evidenceStateMirrorhash + preservePostgreSQLstored snapshotsApplication / operatorHTTP read credentialStateMirrorread records + verifySnapshot / verify resultreturned by CoreON SUBMISSIONLATER / SEPARATE REQUESTS
Submission, receipt, retrieval and verification over HTTPOn submission the application sends evidence to StateMirror. StateMirror stores the snapshot in PostgreSQL and returns a receipt to the application. Later, an application or operator makes separate retrieval or verification requests. StateMirror reads stored records and runs integrity verification in Core, returning a snapshot or verification result. PostgreSQL stores records; it does not perform verification. The HTTP path shown does not independently anchor history.SUBMITRETURNSTORERETRIEVEVERIFYRETURNApplicationsubmitted evidenceStateMirrorhash + preservePostgreSQLstored snapshotsApplication / operatorHTTP read credentialStateMirrorread records + verifySnapshot / verify resultreturned by CoreON SUBMISSIONLATER / SEPARATE REQUESTS
POST /v1/snapshots stores submitted evidence and returns a receipt. Later, GET /v1/snapshots/{snapshot_id} retrieves it; GET /v1/snapshots/{snapshot_id}/verify checks integrity. These are separate requests. Verification runs in Core against stored records, not in PostgreSQL or independently in the client.

Each record carries the chain forward.

#

The previous record’s chain_hash becomes the next record’s prev_chain_hash. Matching values below show that continuity directly.

FIG 03 / STORED RECORDS / BASE64CHAIN_CONTINUITY
verify range / 1–3EXAMPLE RANGE /PASS
  1. sequence_num001
    prev_chain_hash
    nullFirst record: no predecessor
    payload_hash
    PbQTKH7/sGHUg+GjB0acqsNluk4DuU42HAZaHUwQ5r4=SHA-256 of canonical state_payload
    chain_hash
    NZpNkbVJ5z7OzkbmqpiGM89RqzAA9A0+eUVvAyUPkOg=Becomes prev_chain_hash in sequence 2
  2. sequence_num002
    prev_chain_hash
    NZpNkbVJ5z7OzkbmqpiGM89RqzAA9A0+eUVvAyUPkOg=chain_hash from sequence 1
    payload_hash
    H/po/j1xS7YqfbxVFftdw2ckMjmuLJJu92iXrFZajrk=SHA-256 of canonical state_payload
    chain_hash
    ID8uVcrrOft4c61G6lWdCQxsZwf6jrvN5atJp1agJiQ=Becomes prev_chain_hash in sequence 3
  3. sequence_num003
    prev_chain_hash
    ID8uVcrrOft4c61G6lWdCQxsZwf6jrvN5atJp1agJiQ=chain_hash from sequence 2
    payload_hash
    ihcP+YghhVDHPTWs6eOeoET2gTLJfNj9AV2OGjw82IY=SHA-256 of canonical state_payload
    chain_hash
    lsyChJa9nPGo8U0txI2tvpJ/QI7qDwB65CAWOqnbVW0=Continues the same chain
payload_hash = SHA-256(canonical UTF-8 JSON(state_payload))chain_hash = SHA-256(sequence_num bytes || payload_hash bytes || prev_chain_hash bytes)

|| means byte concatenation: 8-byte signed big-endian sequence, then two 32-byte hashes. The first record stores prev_chain_hash: null; hashing substitutes 32 zero bytes. Hashes above are Base64 encodings, not strings concatenated into the formula.

Inspect the example payloads and computed result
{
  "records": [
    {
      "sequence_num": 1,
      "state_payload": {
        "subject": "customer_123",
        "inputs": {
          "plan_status": "active",
          "seats_used": 10,
          "seat_limit": 10,
          "read_at": "2026-06-15T18:22:30.000Z"
        },
        "rule_version": "access-rules@2026.06.12",
        "decision": "deny",
        "reason": "seat_limit_reached",
        "correlation_id": "request_001"
      }
    },
    {
      "sequence_num": 2,
      "state_payload": {
        "subject": "customer_123",
        "inputs": {
          "plan_status": "active",
          "seats_used": 10,
          "seat_limit": 10,
          "read_at": "2026-06-15T18:22:30.000Z"
        },
        "rule_version": "access-rules@2026.06.12",
        "decision": "deny",
        "reason": "seat_limit_reached",
        "correlation_id": "request_002"
      }
    },
    {
      "sequence_num": 3,
      "state_payload": {
        "subject": "customer_123",
        "inputs": {
          "plan_status": "active",
          "seats_used": 10,
          "seat_limit": 10,
          "read_at": "2026-06-15T18:22:30.000Z"
        },
        "rule_version": "access-rules@2026.06.12",
        "decision": "deny",
        "reason": "seat_limit_reached",
        "correlation_id": "request_003"
      }
    }
  ],
  "verification": {
    "valid": true,
    "message": "Preserved evidence chain integrity verified.",
    "checked_count": 3,
    "first_sequence": 1,
    "last_sequence": 3
  }
}
Each record commits to its own payload and the previous chain state. Illustrative records computed with Core’s hash and verification functions; sequence 1 matches the homepage example. Sequence numbers are instance-local. The hash covers state_payload, not every envelope field.

A changed payload breaks verification.

#
TEST 04 / MODIFIED PAYLOAD / UNCHANGED HASHESINTEGRITY_FAILURE
verify range / 1–3EXAMPLE RANGE /FAIL
  1. sequence_num001INTEGRITY /PASS
  2. sequence_num002INTEGRITY /FAILPAYLOAD_HASH_MISMATCH
  3. sequence_num003NOT CHECKEDRange stopped at 2

Sequence 2: the stored payload changed.

Change state_payload.decision from "deny" to "allow", leaving the stored hashes unchanged. Verification recomputes the payload hash and finds a mismatch.

expected / recomputed from modified payload
5HTyjnnLhUfCQHr11oXSBh1dg8EjT2ClBG+/J5qaT7I=
actual / stored payload_hash
H/po/j1xS7YqfbxVFftdw2ckMjmuLJJu92iXrFZajrk=
Inspect Core’s computed failure result
{
  "valid": false,
  "failure_code": "PAYLOAD_HASH_MISMATCH",
  "message": "Stored payload hash does not match recomputed payload hash.",
  "sequence_num": 2,
  "break_at_sequence": 2,
  "expected": "5HTyjnnLhUfCQHr11oXSBh1dg8EjT2ClBG+/J5qaT7I=",
  "actual": "H/po/j1xS7YqfbxVFftdw2ckMjmuLJJu92iXrFZajrk=",
  "checked_count": 2,
  "first_sequence": 1,
  "last_sequence": 2
}
Computed in memory using Core’s range verifier, not a live API response. The range fails at sequence 2; this does not assert that each later record’s individual check fails. A privileged rewrite with recomputed hashes is a different case, shown below.

Where verification’s trust boundary ends.

#
FIG 05 / SCHEMATICTRUST_BOUNDARY
Verification inside the operator's database trust boundaryThe application submits evidence to StateMirror. StateMirror verifies stored payload hashes and chain links against records in PostgreSQL. StateMirror, PostgreSQL and a privileged database administrator are within the operator's operational trust boundary. The administrator can rewrite records and recompute hashes, so a consistent rewrite can pass these checks. Core provides no independent external history anchor. Upstream truth and decision correctness are not verified.OPERATIONAL TRUST BOUNDARY / OPERATOR-OWNEDSUBMITREADSTOREREWRITEApplicationowns payload meaningStateMirrorpayload + chain checksPostgreSQLrecords + hashesPrivileged DB administratorcan rewrite + recompute
Verification inside the operator's database trust boundaryThe application submits evidence to StateMirror. StateMirror verifies stored payload hashes and chain links against records in PostgreSQL. StateMirror, PostgreSQL and a privileged database administrator are within the operator's operational trust boundary. The administrator can rewrite records and recompute hashes, so a consistent rewrite can pass these checks. Core provides no independent external history anchor. Upstream truth and decision correctness are not verified.OPERATIONAL TRUST BOUNDARYSUBMITREADSTOREREWRITEApplicationowns payload meaningStateMirrorpayload + chain checksPostgreSQLrecords + hashesPrivileged DB adminrewrite + recompute
Independent external anchor: not provided by Core. Independent evidence of expected ledger progress remains an operator responsibility where required. Integrity checks do not establish upstream truth or decision correctness.
A privileged database administrator can rewrite records and recompute the chain. Consistent rewrites can pass verification; stored hashes alone cannot establish independent history or detect every chain-suffix deletion. This is the same operational trust boundary documented in Security & Operations.

Supporting facts remain optional.

#
FIG 06 / SCHEMATICOPTIONAL_INPUTS
Optional supporting facts composed by the applicationPlanSignal, DenySignal and ExpirySignal are optional read-time inputs to the application. The application selects and composes its own state_payload and submits it to StateMirror. StateMirror does not require these primitives or their schemas. Dashed paths indicate optional input, not required dependencies.OPTIONAL SUPPORTING PRIMITIVESREADSUBMITPlanSignalplan / entitlementDenySignaldenial factExpirySignaltemporal validityApplicationselects payload fieldsStateMirrorapplication-defined JSON
Optional supporting facts composed by the applicationPlanSignal, DenySignal and ExpirySignal are optional read-time inputs to the application. The application selects and composes its own state_payload and submits it to StateMirror. StateMirror does not require these primitives or their schemas. Dashed paths indicate optional input, not required dependencies.OPTIONAL INPUTSREADSUBMITPlanSignalplan / entitlementDenySignaldenial factExpirySignaltemporal validityApplicationselects payload fieldsStateMirrorapplication-defined JSON
Dashed inputs are optional. The application decides what to include in state_payload; StateMirror accepts application-defined JSON and does not require these primitives or schemas.