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

API reference

KernelPort v1

KernelPort v1 is the only interface into the elan1 Govern kernel. It has six calls: define registers a declaration, assert submits a write intent, seal and verify work with proof-record digests, scope limits what a caller may touch, and refuse records a stop. This page gives each call's parameters and envelopes.

The six calls into elan1 Govern. Work and Decide use them, and so does every third-party agent: in-process through the SDK, or over the MCP server and HTTP API. No other door into the kernel exists, and CI fails the build if one appears.

Docs v1.0Examples are illustrative specimens, not customer data

Docs menu

Overview

KernelPort is the only door into the kernel: one write function behind six calls. A model may produce a write intent. It may never produce an allow. Every allow, refusal and hold comes from elan1 Govern climbing the Right Path from rung 0b STOP to rung 9 METERED, and every outcome writes a proof record.

The six calls at a glance
CallPurposeWho may callWrites a proof record
defineRegisters a declaration version and the write types it allows; returns a content-addressed digest.Control-plane tokenYes. Registering is itself a governed write, under dual control.
assertSends a write intent up the Right Path and answers committed, refused or escalated, synchronously.Tenant token, act scopeYes, for every outcome.
sealWrites the record to the tenant's chain under Seal v6. At L1 it returns a hash-chain receipt.The kernel onlyIt is the writer.
verifyRecomputes preimage and digest octet for octet and walks the chain.Read-only tokenNo. It reads.
scopeResolves blast radius and autonomy so a surface can show them. Advisory only.Read-only tokenNo. It reads.
refuseEmits a typed refusal from a closed register: an approver's no, or a caller withdrawing an intent.Tenant token, act scopeYes. Metered at 0.4 of a governed action.

Three ways in

Transport and what it lets elan1 say
TransportModeWhat elan1 may say
In-process SDKMode A · EnforcedEnforced. Govern holds the write credential; no write reaches the system of record without a gate decision and a proof record.
MCP server · 34 toolsMode B · Attested advisoryEvaluated against your rules and recorded. Attested advisory, never blocked.
HTTP API · /v1Mode B · Attested advisoryEvaluated against your rules and recorded. Attested advisory, never blocked.

The calls, their meaning and their outcomes are the same on every transport. Only the mode changes, and with it the sentence elan1 may use. A Mode B integration can move to Mode A where the credential can move: after 30 days of recorded traffic and a review.

Conventions on this page

  • Shape, not schema. Requests and responses are shown as HTTP and JSON. The SDK and the MCP server carry the same fields. Call names, arguments and response shapes are fixed in KernelPort v1 and the record shape; the exact schemas are in the customer reference.
  • Specimens. Every identifier containing specimen, every digest and every amount is invented. Digests are shortened with an ellipsis for reading. Illustrative specimen. Not customer data.
  • Frozen identifiers. kernelport/v1, elan1.proof.seal.v6 and action-receipt/1 are the names this reference is written against.
  • Refusal codes. Codes on this page are illustrative and marked so. Real codes come from the frozen refusal register in KernelPort v1. See Errors and refusals.
  • Fields. F01–F16 refer to the sixteen Seal v6 fields of the proof record.

Try a call

Six calls, one door. Pick a call, edit its request and send it. The outcome envelope comes back from a mock of the kernel that runs in this page: no key, no network call, nothing recorded. Try sending an assert that carries "approval": "granted".

Call
Scenario · edits the request
HTTPPOST /v1/assert
Authorization: Bearer <tenant token, act scope>
X-Tenant: tnt_specimen_01
Idempotency-Key: wr_specimen_7c2

Valid JSON

Or press ⌘+Enter / Ctrl+Enter in the editor

Committed at rung 9 · seq 1042 · 1 governed action

httpResponse · committed
HTTP/1.1 200 OK

{
  "outcome": "committed",
  "rung": 9,
  "record": { "id": "je_specimen_5531", "version": 2 },
  "proof": {
    "seal": "elan1.proof.seal.v6",
    "seq": 1042,
    "digest": "sha256:f2423aa3…5aca2c18",
    "prev_digest": "sha256:8e37839e…0ffdeac0"
  },
  "meter": { "governed_actions": 1 }
}

Mocked in your browser. Nothing was sent. Illustrative specimen. Real codes come from the frozen refusal register.

Call 1 of 6

define

define(declaration, version, actor) → escalated | committed | refused, with a declaration digest

Registers a version of a declaration — a process’s rules in seven blocks: identity, shape, permissions, write_types, gates, proof_binding and channels — and the write types it allows. It returns a content-addressed digest of that version. Later records name the declaration and the version they were checked against.

Who may call
A control-plane token. A control-plane token cannot read any record.
Rungs
All of them. Registering a declaration version is itself a governed write, under dual control, so it climbs the Right Path and waits at rung 6 for a second named person.
Fields it governs
F06 declaration_id and F07 declaration_version on every later record; the write types it lists are what F05 write_type may hold.
Record
The registration writes its own proof record.
Versioning
A version is never edited. Change the rules and the version moves; old records still point at the rules that applied to them.
httpRequest · defineHTTP code: Request · define
POST /v1/define
Authorization: Bearer <control-plane token>
Idempotency-Key: wr_specimen_d08

{
  "kernelport": "v1",
  "call": "define",
  "write_id": "wr_specimen_d08",
  "declaration": {
    "id": "decl_month_end_close",
    "version": 8,
    "supersedes": 7,
    "blocks": {
      "identity":      { "...": "who owns this process" },
      "shape":         { "...": "the records it may touch" },
      "permissions":   { "...": "who may propose and who may approve" },
      "write_types":   ["journal_entry.post", "journal_entry.reverse"],
      "gates":         { "...": "pre-gate settings and thresholds" },
      "proof_binding": { "...": "what the record binds" },
      "channels":      { "...": "how requests may arrive" }
    }
  },
  "actor": { "id": "usr_specimen_controller", "kind": "human" }
}
Illustrative specimen. Not customer data.
httpResponse · held for dual controlHTTP code: Response · held for dual control
HTTP/1.1 200 OK

{
  "outcome": "escalated",
  "rung": 6,
  "reason": "Registering a declaration version is a governed write under dual control. A second named person decides.",
  "obligation": "OB-specimen-0419",
  "declaration": {
    "id": "decl_month_end_close",
    "version": 8,
    "digest": "sha256:7a1d0c55…f3b2c94e"
  },
  "proof": { "seq": 1044, "digest": "sha256:0d6e2a91…5c7b18aa" }
}
Illustrative specimen. Not customer data.

Errors and refusals

When define does not register
SituationWhat comes backRecord
The token is not a control-plane tokenA contract error: the request is rejected before the Right Path.None
The version already exists, or skips a numberA contract error naming the expected version.None
A required block is missing or malformedA contract error naming the block.None
The second person says no, or the hold expiresRefused with a typed code, for example APPROVER_REFUSED or APPROVAL_EXPIRED (illustrative).Written · 0.4 governed action

Call 2 of 6

assert

assert(write_intent) → committed | refused | escalated, with seq and digest

The central call. It sends one typed write intent up the whole Right Path, in order, and answers synchronously with one of three outcomes: committed, refused, or escalated to a named person. The answer always carries the proof sequence and digest, because every outcome writes a record.

Who may call
A tenant token with act scope: an agent, the elan1 Decide router, elan1 Work, or a person.
Rungs
0b STOP · 1 IDENTITY · 2 WRITE DOOR · 3 PRE-GATES (G1–G9) · 4 evaluate() · 5 GUARDRAIL · 6 AUTONOMY · 7 ACT · 8 PROOF · 9 METERED. Any rung can refuse. Rungs 3 to 8 cannot reach a model client.
Fields
All sixteen. write_id → F04, write_type → F05, declaration → F06/F07, actor → F09/F10, record → F12/F13, payload → F14 as a hash, never the payload itself.
Idempotency
The write_id is the idempotency key. Replaying it returns the original outcome, sequence and digest, and no second meter event.
Meter
One governed action when committed. A refusal is metered at 0.4 of a governed action. An escalation is not metered until it resolves.
Mode
In Mode A · Enforced the single writer makes the change at rung 7 with a 10-minute single-use credential, sandbox first. In Mode B · Attested advisory your system writes; elan1 evaluates and records.

Request

httpRequest · assertHTTP code: Request · assert
POST /v1/assert
Authorization: Bearer <tenant token, act scope>
X-Tenant: tnt_specimen_01
Idempotency-Key: wr_specimen_7c2

{
  "kernelport": "v1",
  "call": "assert",
  "write_id": "wr_specimen_7c2",
  "write_type": "journal_entry.post",
  "declaration": { "id": "decl_month_end_close", "version": 7 },
  "actor": { "id": "agt_specimen_gl02", "kind": "agent" },
  "record": { "id": "je_specimen_5531", "expected_version": 1 },
  "payload": {
    "date": "2027-07-14",
    "lines": [
      { "account": "5100 Freight inward", "debit": "18400.00" },
      { "account": "2100 Accrued expenses", "credit": "18400.00" }
    ]
  }
}
Illustrative specimen. Not customer data.

Payload fields are examples, not the published schema. The payload shape is whatever the declared write type says it is.

Three responses

httpResponse · escalated at rung 6HTTP code: Response · escalated at rung 6
HTTP/1.1 200 OK

{
  "outcome": "escalated",
  "rung": 6,
  "reason": "Above the autonomy ceiling. A named person, not the asker, decides.",
  "obligation": "OB-specimen-0412",
  "proof": { "seq": 1039, "digest": "sha256:3b9a51c2…7e04d1f8" },
  "meter": { "governed_actions": 0 }
}
Illustrative specimen. Not customer data.
httpResponse · committedHTTP code: Response · committed
HTTP/1.1 200 OK

{
  "outcome": "committed",
  "rung": 9,
  "record": { "id": "je_specimen_5531", "version": 2 },
  "proof": {
    "seal": "elan1.proof.seal.v6",
    "seq": 1042,
    "digest": "sha256:f2423aa3…5aca2c18",
    "prev_digest": "sha256:8e37839e…0ffdeac0"
  },
  "meter": { "governed_actions": 1 }
}
Illustrative specimen. Not customer data.
httpResponse · refused at rung 3HTTP code: Response · refused at rung 3
HTTP/1.1 200 OK

{
  "outcome": "refused",
  "rung": 3,
  "refusal": {
    "code": "GATE.PRECONDITION_FAILED",
    "class": "rule",
    "reason": "The precondition gate (G9) failed against your data."
  },
  "record": { "id": "00000000-0000-0000-0000-000000000000", "version": 0 },
  "proof": { "seq": 1041, "digest": "sha256:8e37839e…0ffdeac0" },
  "meter": { "governed_actions": 0.4, "inr": 0.64 }
}

# 0.4 of a governed action: ₹0.64 at L1, beyond the allowance.
Illustrative specimen. Not customer data.

An escalation names an obligation. A named approver, who is not the asker and never an agent token, answers it. If they approve, the single writer commits and the record is sealed; if they say no, the refusal is recorded through refuse. If nobody answers before the hold expires, expiry is a refusal. Your caller learns the final outcome from a webhook or by reading the obligation; see HTTP API · webhooks.

Errors and refusals

What can go wrong, and where
SituationOutcomeRecord · meter
Writes are stopped for this tenant (rung 0b)RefusedWritten · 0.4 governed action
The write type is not on the declaration's list (rung 2)RefusedWritten · 0.4 governed action
A pre-gate fails, for example GST rate or precondition (rung 3)Refused G4_GST_RATE_MISMATCH, GATE.PRECONDITION_FAILED (illustrative)Written · 0.4 governed action
The versioned rules say no (rung 4)RefusedWritten · 0.4 governed action
The blast radius is over the declared limit (rung 5)RefusedWritten · 0.4 governed action
Above the autonomy ceiling (rung 6)EscalatedWritten · not metered while held
The record changed since you read it (expected_version)RefusedWritten · 0.4 governed action
A dependency is downRefused — a platform refusal, never an allowWritten · never billed
Malformed request, wrong token, “latest” as a version on a production keyContract error (4xx)None

Which rung raises which code is fixed by the frozen register. Codes shown are illustrative.

Call 3 of 6

seal

seal(record) → receipt

The chain writer. seal takes the sixteen fields of a record, builds a length-prefixed preimage under the domain elan1.proof.seal.v6, hashes it with SHA-256, and appends the record to the tenant’s chain. The digest becomes the next record’s prev_digest. No caller outside the kernel can invoke it; you see its result in every response’s proof block and in the records you read.

Who may call
The kernel only.
Rung
8 PROOF, for commits, refusals and escalations alike.
Fields
All sixteen, F01 tenant_id to F16 recorded_at. F02 seq only ever goes up by one; F03 prev_digest links the chain. Chains never cross tenants.
At L1
A hash-chain receipt. Tamper-evident to you: a changed record no longer matches its digest, and the next record no longer points at it.
From L2
The same call also signs, with Ed25519 per residency domain; Merkle roots are anchored to two RFC 3161 timestamp authorities. Signed records are checkable offline from L2.
jsonReceipt · elan1.proof.seal.v6JSON code: Receipt · elan1.proof.seal.v6
seal  →  receipt (L1)

{
  "seal": "elan1.proof.seal.v6",
  "tenant_id": "tnt_specimen_01",
  "seq": 1042,
  "prev_digest": "sha256:8e37839e…0ffdeac0",
  "digest": "sha256:f2423aa3…5aca2c18",
  "recorded_at": "2027-07-14T10:42:07+05:30"
}

# From L2, the same call also signs. A signature
# block is added; the digest and the chain do not change.
Illustrative specimen. Not customer data.

Errors

If the chain writer cannot seal, nothing is committed: an allow without a record is not an allow. The intent ends as a platform refusal and is never billed. A refusal is final for its write_id, so a later attempt is a new intent with a new one. There are no UPDATE or DELETE operations on evidence rows. The full field list and the preimage layout are in Proof record.

Call 4 of 6

verify

verify(record | seq range) → intact | findings

Checks one record or a stretch of chain. It recomputes each record’s preimage and digest octet for octet, compares every prev_digest with the digest before it, and checks that seq has no gaps, duplicates or reorderings.

Who may call
A read-only token.
Rungs
None. verify reads; it writes no record and is not a governed action.
Fields
F02 seq and F03 prev_digest, plus the digest recomputed over all sixteen.
At L1
Verification needs elan1. It tells you whether the chain you hold is the chain elan1 wrote, and that nothing in it was edited afterwards.
From L2
Signed records and bundles are checkable offline, by anyone with the bundle, using elan1-verify.
httpRequest · verify a rangeHTTP code: Request · verify a range
POST /v1/verify
Authorization: Bearer <read-only token>
X-Tenant: tnt_specimen_01

{
  "kernelport": "v1",
  "call": "verify",
  "range": { "from_seq": 1041, "to_seq": 1042 }
}
Illustrative specimen. Not customer data.
httpResponseHTTP code: Response
HTTP/1.1 200 OK

{
  "range": { "from_seq": 1041, "to_seq": 1042 },
  "checked": 2,
  "result": "intact",
  "findings": []
}

# A broken chain answers with findings instead, for example:
#   { "seq": 1042, "finding": "prev_digest_mismatch" }
#   { "seq": 1043, "finding": "seq_gap", "expected": 1043, "found": 1044 }
Illustrative specimen. Not customer data.

What it finds

A flipped octet, a reordered pair, a duplicate and a gap are each found; conformance test C9 proves it. Finding names above are illustrative; the exact names are in the record-shape reference.

Call 5 of 6

scope

scope(actor, write_type, record) → advisory scope

Sets out what a caller may do, so a surface can show it before anything is proposed: the tenant and residency domain, whether the write type is allowed, the blast radius, and the autonomy level against its ceiling. It is advisory only. assert resolves both again at rungs 5 and 6, so a scope answer can never be used as an allow.

Who may call
A read-only token.
Rungs it previews
1 IDENTITY, 2 WRITE DOOR, 5 GUARDRAIL and 6 AUTONOMY. It does not run the pre-gates or evaluate().
Fields it previews
F01 tenant_id, F05 write_type, F11 autonomy_level.
Missing grant
An actor without a grant resolves to A0 Observe.
Isolation
Scope never crosses tenants or residency domains. A cross-domain query is not slow; it is impossible.
httpRequest · scopeHTTP code: Request · scope
GET /v1/scope?write_type=journal_entry.post&record_id=je_specimen_5531
Authorization: Bearer <read-only token>
X-Tenant: tnt_specimen_01
Illustrative specimen. Not customer data.
httpResponse · advisoryHTTP code: Response · advisory
HTTP/1.1 200 OK

{
  "advisory": true,
  "actor": { "id": "agt_specimen_gl02", "kind": "agent" },
  "residency_domain": "in",
  "write_type_allowed": true,
  "autonomy": { "level": "A2", "ceiling": "A2", "person_must_approve": true },
  "blast_radius": { "records": 1, "ledgers": 1, "inside_declared_limit": true }
}
Illustrative specimen. Not customer data.

Call 6 of 6

refuse

refuse(obligation | write_id, actor, code) → refused

Records a typed “no”. A named approver uses it to refuse a held write; a caller uses it to withdraw an intent it no longer wants. The kernel uses the same path when a hold expires. Codes come from a closed register, so a refusal is always one of a known set.

Who may call
A tenant token with act scope. Refusing a held write needs the named approver; an agent token cannot answer an obligation.
Rung
6 AUTONOMY for an approver's no or an expired hold; the rung where the intent stood for a withdrawal.
Fields
F09 actor_id and F10 actor_kind name who refused. F12 record_id is zeroed and F13 record_version is 0, because nothing changed.
Record and meter
A refusal still writes a proof record. It is metered at 0.4 of a governed action, and a database check holds it there.
httpRequest · refuse a held writeHTTP code: Request · refuse a held write
POST /v1/refuse
Authorization: Bearer <tenant token, act scope>
X-Tenant: tnt_specimen_01

{
  "kernelport": "v1",
  "call": "refuse",
  "obligation": "OB-specimen-0412",
  "actor": { "id": "usr_specimen_approver", "kind": "human" },
  "code": "APPROVER_REFUSED",
  "note": "Accrual belongs in August, not July."
}
Illustrative specimen. Not customer data.
httpResponseHTTP code: Response
HTTP/1.1 200 OK

{
  "outcome": "refused",
  "rung": 6,
  "refusal": { "code": "APPROVER_REFUSED", "class": "rule" },
  "record": { "id": "00000000-0000-0000-0000-000000000000", "version": 0 },
  "proof": { "seq": 1040, "digest": "sha256:91c4e7b0…2d3f6a05" },
  "meter": { "governed_actions": 0.4, "inr": 0.64 }
}

# 0.4 of a governed action: ₹0.64 at L1, beyond the allowance.
Illustrative specimen. Not customer data.

Errors

When refuse is rejected
SituationWhat comes back
The obligation is already resolvedThe original outcome, sequence and digest. Nothing new is written.
The caller is the asker, or an agent tokenA contract error. Approval separation (C11) applies to a no as much as a yes.
The code is not in the registerA contract error. Free-text reasons go in the note; the code must come from the register.

Outcomes, at a glance

Every call that writes ends in exactly one of three outcomes. A platform failure is ours, not a rule, and is never billed. A contract error means the request never reached the Right Path.

What a caller can receive
OutcomeMeansProof recordMeter
CommittedThe write climbed every rung. In Mode A the single writer made it; in Mode B your system makes it and elan1 has recorded the evaluation.Written1 governed action
RefusedA rung said no, or a person did, or the hold expired. Typed code from the register.Written0.4 governed action
EscalatedHeld for a named person who is not the asker. Resolves to committed or refused.WrittenNot metered while held
Platform refusalA dependency failed. elan1 fails closed: never an allow.Written where the chain writer is reachableNever billed
Contract errorThe request did not meet the contract: shape, token, version pin.NoneNone

What the customer reference covers

This page is the public quick reference. Customers receive the full KernelPort v1 reference with their workspace, which sets out:

  • Exact argument names, nesting and response shapes beyond the call names and the three outcomes.
  • Exact HTTP paths. /v1/<call> here is illustrative.
  • The refusal register: every code, its class and the rung that raises it.
  • Finding names returned by verify.
  • The L2 signature block added by seal.
Do not hard-code a refusal code from this page. Branch on the outcome and the refusal class, and show the code to a person.

// BUILD WITH US

Get the full reference for your integration.

Customers receive the full KernelPort v1 reference with their workspace. Ten early-adopter design partners also help shape what comes next.