All four platforms available
Reference · all four platforms availableDocs v1.0 · KernelPort v1 call names and shapes are stable

Evidence

Proof record

This is the specification of elan1's proof record. It lists the 16 fields, defines the Seal v6 preimage, shows how the SHA-256 digest is computed and explains how each record carries the previous record's digest to form a chain. With it, anyone can recompute a digest and check the chain independently.

Every governed write, allowed or refused, leaves one record of sixteen fields under Seal v6. Each record is hashed with SHA-256 over a length-prefixed preimage and chained to the one before it. This page is the reference: every field, the exact preimage layout, how the chain links, and how to recompute it yourself at L1.

Docs v1.0Document date 10 Oct 2026Examples are illustrative specimens, not customer data

Docs menu

What a proof record is

A proof record is the evidence that one write intent took the governed path. elan1 Govern writes it at rung 8 PROOF, synchronously, before the caller receives its answer. A commit writes one. A refusal writes one. A hold that expires and becomes a refusal writes one. There is no outcome of assert that leaves no record.

Frozen identifier
elan1.proof.seal.v6
Fields
Sixteen, F01 to F16, in a fixed order. No optional fields, no seventeenth field, no v6.1. A refusal carries zeroes, not omissions.
Hash
SHA-256 over a length-prefixed preimage: the 19-octet domain string, then each field with its length in front.
Chain
One chain per tenant. F03 prev_digest holds the digest of the record before. Chains never cross tenants.
Storage rule
Evidence rows cannot be updated or deleted: there is no UPDATE or DELETE on them.
Personal data
None in evidence. F14 holds a hash of the payload, not the payload.
Assurance
At L1: tamper-evident to you; not signed, and checking needs elan1. Signed and checkable offline from L2.
Status
Available · L1 Guarded and L2 Attested

Every value on this page is an illustrative specimen, not customer data. The two specimen records are the same ones the inspector on the proof-record page hashes.

The sixteen fields

Field names and order come from Seal v6. The example column is record seq 1042 from the specimen chain: an approver committing a journal entry for Kolam Components*.

The sixteen Seal v6 fields
FieldNameWhat it holds
F01tenant_idThe tenant this record belongs to. Chains never cross tenants.
Specimen: tnt_specimen_01
F02seqThe record's position in this tenant's chain. It only ever goes up by one.
Specimen: 1042
F03prev_digestThe digest of the record before this one. It is what links the chain.
Specimen: sha256:8e37839e9548dfa9a4a8948b723c1517afb532a7eaceb617a7db6eff0ffdeac0
F04write_idThe identifier of the write intent. Every invoice line must carry one.
Specimen: wr_specimen_7c2
F05write_typeThe kind of write, from the declaration's list of allowed write types.
Specimen: journal_entry.post
F06declaration_idThe declaration — the process's rules in seven blocks — that was checked.
Specimen: decl_month_end_close
F07declaration_versionWhich version of that declaration was in force at the time.
Specimen: 7
F08policy_versionThe version of the policy rules evaluate() ran. No model, only rules.
Specimen: 2027.06.1
F09actor_idWho acted: the named approver, or the agent that proposed the write.
Specimen: usr_specimen_approver
F10actor_kindWhether the actor was a human or an agent.
Specimen: human
F11autonomy_levelThe autonomy level A0–A5 the write ran at; above the ceiling a person decides.
Specimen: A2
F12record_idThe record in your system of record that changed, or would have.
Specimen: je_specimen_5531
F13record_versionThe version of that record after the write.
Specimen: 2
F14payload_hashA hash of the payload, not the payload itself, so personal data stays out of evidence.
Specimen: sha256:e1eb0d8f0a7399695abf290e8fd2b6abc738ffd2fbdb8297f9ae44eb1a32ab6a
F15lexicon_hashA hash of the vocabulary the write was expressed in, so meanings cannot drift.
Specimen: sha256:fd8e4d0b12b24bf22a591e735f211d8d7bb2bdf480058fdf89bb9d44666b2633
F16recorded_atWhen the record was sealed, with the time-zone offset.
Specimen: 2027-07-14T10:42:07+05:30

*Fictional company; all data is illustrative. Illustrative specimen. Not customer data.

Read the fields in seven groups

F01–F03Position in the chain

Which tenant's chain, which position in it, and the digest of the record before.

F04–F05The write intent

The intent's identifier and the kind of write it asked for. write_id is also the idempotency key: replaying an intent returns the original outcome.

F06–F08The rules that applied

The declaration, the version of it in force, and the version of the policy rules evaluate() ran. No model sits on the allow path.

F09–F11Who acted

The named approver or the proposing agent, whether that actor was a human or an agent, and the autonomy level A0–A5 the write ran at.

F12–F13What changed

The record in your system of record that changed, or would have, and its version afterwards. On a refusal, record_id is zeroed and record_version is 0.

F14–F15What was said

A hash of the payload and a hash of the vocabulary it was expressed in, so content stays out of evidence and meanings cannot drift.

F16When

When the record was sealed, with its time-zone offset.

Commit and refusal, side by side

Fields that differ between the committed record seq 1042 and the refused record seq 1041
Fieldseq 1041 · refusedseq 1042 · committed
F05 write_typepurchase_invoice.postjournal_entry.post
F06 declaration_iddecl_ap_purchasedecl_month_end_close
F09 actor_idagt_specimen_ap01usr_specimen_approver
F10 actor_kindagenthuman
F11 autonomy_levelA1A2
F12 record_id00000000-0000-0000-0000-000000000000je_specimen_5531
F13 record_version02

Illustrative specimen. Not customer data. Both records share F01 tenant_id and F08 policy_version; the refusal still takes a place in the chain.

Beside the record, never in it

Three things a reader needs are not fields: the outcome (committed, refused or escalated), the approver’s display name, and the refusal code. Interfaces show them in a rendering strip next to the record. The record itself stays at sixteen fields, so the preimage never changes shape between outcomes.

Outcome
Refused at rung 3 · PRE-GATES
Approver
None. The write stopped before approval.
Refusal code
GATE.PRECONDITION_FAILED (illustrative)
Meter
0.4 governed action

Rendering strip for specimen seq 1041. Illustrative specimen. Not customer data. Refusal codes come from one closed register; real codes are in the KernelPort v1 reference customers receive. See Errors and refusals.

The preimage and the digest

The digest of a record is SHA-256 over its preimage. The preimage is the 19-octet domain string elan1.proof.seal.v6, followed by the sixteen fields in order, each written as its length and then its bytes. Because every field carries its own length, no two different records can produce the same bytes: "ab" then "c" can never be confused with "a" then "bc". The domain string keeps a Seal v6 digest from ever colliding with a hash computed for another purpose.

octets 0–18Domain string elan1.proof.seal.v6, 19 octets, no length prefix
4 octetsLength of F01 tenant_id, unsigned, big-endian
n octetsThe bytes of F01 tenant_id
4 + n octetsF02 seq, the same way
…F03 to F15, each length-prefixed, in order
4 + n octetsF16 recorded_at, last
  1. Start with the domain string.

    Write the 19 octets of elan1.proof.seal.v6.

  2. Append every field, in order.

    For F01 to F16, write a 4-octet big-endian length, then the field's bytes. Never skip a field; a refusal's zeroed record_id is still written.

  3. Hash once.

    Run SHA-256 over the whole preimage. The 32-octet result, written as sha256: and 64 hex characters, is the record's digest.

  4. Carry it forward.

    The next record in the tenant's chain stores that digest in F03 prev_digest.

Where this description is weak. The specimen encoding on this page writes every field as UTF-8 text. Seal v6 encodes identifiers and digests as fixed binary octets, and that canonical per-field encoding is not restated here. Use this page to learn the method and to recompute the specimens; do not treat it as a production verifier.

Chaining by prev_digest

Each tenant has exactly one chain. F02 seq goes up by one for every record, and F03 prev_digest on record n + 1 equals the digest of record n, octet for octet. That single rule is what makes the chain tamper-evident: the digest of every record is committed to by the record after it.

What each kind of tampering does to the chain, and how a walk finds it
If someone…What breaksHow a walk finds it
Changes one octet of a past recordThat record’s digestThe next record’s F03 no longer matches the recomputed digest.
Deletes a recordThe sequenceF02 seq jumps by two; the following F03 matches nothing you hold.
Swaps two recordsOrder and linksseq runs backwards and both links fail.
Inserts a recordA linkEither seq repeats or a prev_digest points at the wrong record.
Rewrites everything after a pointThe head digestThe head no longer matches a digest you noted at an earlier check.
Moves a record to another tenantF01 and the linkChains never cross tenants; the tenant changes mid-walk.

The last row is why a periodic check matters at L1: anyone able to rewrite a whole stretch of the chain can also recompute its digests. Note the head digest each time you check and compare it next time. From L2, signatures and outside timestamps close that gap, because a rewritten stretch would no longer carry valid signatures or anchors.

Recompute a chain at L1

At L1, the verify call recomputes digests and walks the chain for you. You can also do it yourself, with the records exported to you and a few lines of code. The script below uses only the Python standard library. Run against the two specimen records it prints the same head digest that the browser inspector computes.

  1. Export the stretch you want to check.

    One record per line, sixteen named fields each, in seq order. The export shape below is illustrative; at L1 the records come to you from elan1.

  2. Rebuild each preimage.

    The domain string, then each field as a 4-octet big-endian length and its bytes.

  3. Hash and compare.

    SHA-256 each preimage and compare it with F03 prev_digest on the next record. They must be identical.

  4. Walk the whole stretch.

    Check that F01 never changes and F02 always goes up by one. Note the head digest for next time.

jsonlchain.jsonl · two records, seq 1041–1042JSONL code: chain.jsonl · two records, seq 1041–1042
{"tenant_id":"tnt_specimen_01","seq":"1041","prev_digest":"sha256:058169bb8da3e9f6b1b8511193b43b0636ffe1ecc6e4c46ce8a8a905964023f8","write_id":"wr_specimen_7c1","write_type":"purchase_invoice.post","declaration_id":"decl_ap_purchase","declaration_version":"4","policy_version":"2027.06.1","actor_id":"agt_specimen_ap01","actor_kind":"agent","autonomy_level":"A1","record_id":"00000000-0000-0000-0000-000000000000","record_version":"0","payload_hash":"sha256:5f105d2d818bc7fe36a7c1b7e367196610df5ee6e8d4c5296c9d8cc4b28428d8","lexicon_hash":"sha256:1d967c9d1c583efd0a3135e57aa1035ebdd5ab4766f0572f10304feae7516d53","recorded_at":"2027-07-14T10:39:12+05:30"}
{"tenant_id":"tnt_specimen_01","seq":"1042","prev_digest":"sha256:8e37839e9548dfa9a4a8948b723c1517afb532a7eaceb617a7db6eff0ffdeac0","write_id":"wr_specimen_7c2","write_type":"journal_entry.post","declaration_id":"decl_month_end_close","declaration_version":"7","policy_version":"2027.06.1","actor_id":"usr_specimen_approver","actor_kind":"human","autonomy_level":"A2","record_id":"je_specimen_5531","record_version":"2","payload_hash":"sha256:e1eb0d8f0a7399695abf290e8fd2b6abc738ffd2fbdb8297f9ae44eb1a32ab6a","lexicon_hash":"sha256:fd8e4d0b12b24bf22a591e735f211d8d7bb2bdf480058fdf89bb9d44666b2633","recorded_at":"2027-07-14T10:42:07+05:30"}
Illustrative specimen. Not customer data.
pyverify_chain.pyPY code: verify_chain.py
# verify_chain.py — recompute a stretch of a tenant's chain (illustrative, L1 method)
import hashlib, json, struct, sys

DOMAIN = b"elan1.proof.seal.v6"          # 19 octets
FIELDS = ["tenant_id", "seq", "prev_digest", "write_id", "write_type",
          "declaration_id", "declaration_version", "policy_version", "actor_id", "actor_kind",
          "autonomy_level", "record_id", "record_version", "payload_hash", "lexicon_hash", "recorded_at"]

def preimage(rec):
    out = bytearray(DOMAIN)
    for name in FIELDS:                       # F01..F16, in order, none optional
        b = str(rec[name]).encode("utf-8")
        out += struct.pack(">I", len(b)) + b  # 4-octet big-endian length, then the bytes
    return bytes(out)

def digest(rec):
    return "sha256:" + hashlib.sha256(preimage(rec)).hexdigest()

def walk(records):
    for prev, cur in zip(records, records[1:]):
        if cur["tenant_id"] != prev["tenant_id"]:
            return f"FAIL tenant change at seq {cur['seq']}"
        if int(cur["seq"]) != int(prev["seq"]) + 1:
            return f"FAIL gap after seq {prev['seq']}"
        if cur["prev_digest"] != digest(prev):
            return f"FAIL link broken between seq {prev['seq']} and {cur['seq']}"
    return f"OK {len(records)} records, seq {records[0]['seq']}-{records[-1]['seq']}"

if __name__ == "__main__":
    records = [json.loads(line) for line in open(sys.argv[1], encoding="utf-8")]
    print(walk(records))
    print("head", digest(records[-1]))
Illustrative specimen. Not customer data.
shRun itSH code: Run it
$ python3 verify_chain.py chain.jsonl
OK 2 records, seq 1041-1042
head sha256:f2423aa3d4559b7fd54b390a724f2a989744c10db034caa4a0e047cb5aca2c18
Illustrative specimen. Not customer data. Output from the two specimen records above.
shChange one field and run it againSH code: Change one field and run it again
# Change one character of F13 record_version on seq 1041 ("0" → "1") and run again:
$ python3 verify_chain.py chain.jsonl
FAIL link broken between seq 1041 and 1042
head sha256:f2423aa3d4559b7fd54b390a724f2a989744c10db034caa4a0e047cb5aca2c18          # record 1042 is untouched; its F03 no longer matches
Illustrative specimen. Not customer data.

The script shows the method on the specimen encoding (every field as UTF-8 text). It does not check signatures, because at L1 there are none, and it cannot tell you who sealed a record. At L1 the records are tamper-evident to you; they are not signed.

What a check proves, and what it does not

What a proof record proves and does not prove
A passing walk showsIt does not show
The records have not changed since they were sealed, as far as the head you trustThat the business decision was right
Which declaration, which version and which policy version appliedThat the rules were good
Who acted, as human or agent, and at what autonomy levelThat nothing happened outside elan1 (in Mode B, the caller holds the credential)
That a refusal happened, at which rung, and that it still joined the chainThat the transaction itself was genuine

In Mode B a record shows that a write intent was evaluated against your rules and recorded. It is not evidence of enforcement, because elan1 did not hold the credential. The word enforced applies to Mode A only. See Modes and assurance levels.

What L2 adds

The record does not change between levels: the same sixteen fields, the same preimage, the same chain. L2 Attested adds four things on top, so that someone who does not trust our infrastructure can check a record without us.

What L2 adds to the proof record
AdditionWhat it doesStatus
SigningEach record's digest is signed with Ed25519, with a key per residency domain. India first: domain "in". The signature travels with the record, never as a seventeenth field; at L1 it is absent, not stubbed.Available
AnchoringRecords are batched into Merkle roots and anchored to two RFC 3161 timestamp authorities, one of them in India, within 15 minutes. An anchor shows a record existed, in that form, by that time. We do not run a blockchain.Available
Evidence bundleAn eight-member folder that needs nothing from us: hashes, not payloads, and never key material.Available
Offline verifierelan1-verify, free, with no dependencies and no network, built so anyone can reproduce the build.Available
textThe eight bundle members and the verifierTEXT code: The eight bundle members and the verifier
manifest.json
seals.jsonl
chain.json
anchors/
keys.json
declarations/
lexicon/
VERIFY.txt

$ elan1-verify --bundle <path> --profile <path>
PASS | FAIL_INTEGRITY | FAIL_PROFILE | INCOMPLETE | UNSUPPORTED | USAGE
Illustrative specimen. File names from the bundle specification. Not customer data.
  • Signing is switched on for a tenant at a recorded point in the chain. Records written before it stay L1 and are labelled L1 for good; we never say past records were signed.
  • Signing a Mode B record does not make it enforced. The record keeps the mark that the caller made the write, in every export.
  • L3 Proven is reached when an outside auditor accepts an L2 bundle in writing. It has no date.

What the customer reference covers

Field names and order are fixed by Seal v6. These parts of the surrounding reference are set out in full in the reference customers receive with their workspace.

  • Canonical per-field encoding

    How identifiers, integers, digests and timestamps are written as octets in the production preimage. The specimen encoding here writes everything as UTF-8 text.

    In the customer reference

  • Export format at L1

    The file shape in which records are handed to you for your own walk. The JSON Lines shape above is illustrative.

    In the customer reference

  • Refusal codes

    The closed register of typed refusal codes shown beside a record. Codes on this site are illustrative.

    In the customer reference

  • Where the record physically lives

    The record shape is fixed; its physical home follows your deployment: hosted in your residency domain, or in your own VPC or on premises.

    Per deployment

  • Signing-key custody

    L2 signing keys are held per residency domain. Custody details are in the customer reference and on the verification page.

    In the customer reference

  • Erasure and an append-only chain

    Payloads are hashed, not stored, and evidence rows cannot be changed. A lawful erasure request is honoured where the personal data lives; the record keeps only a hash. See the library note on DPDP and the append-only chain.

    Library note

// CHECK IT

Recompute a real chain in your browser.

The proof-record inspector hashes two specimen records with SHA-256 on your device. Change a field and watch the link break.