Execution Proof and the Policy Binding Problem: The RANKIGI Architecture
Author: Wesley Snow Date: May 2026 Status: Working Paper
1.Abstract
RANKIGI is a passive sidecar that produces tamper-evident, externally verifiable proofs of what an autonomous AI agent did, when it did it, and under whose authority. The system is built around three independently verifiable claims: a SHA-256 hash chain that records every event in canonical form, an Ed25519 (and post-quantum ML-DSA-65) agent passport that binds those events to a named identity, and a public anchoring layer that timestamps the chain head against the Sigstore Rekor transparency log and an RFC 3161 timestamp authority. None of these proofs require contacting RANKIGI infrastructure to verify.
This paper describes the system as it is implemented in the codebase, not as it is specified. Each component is cited to a specific file and line range so the claims here can be checked against the source. The paper covers the hash chain (Section 3), the agent passport and the Know Your Agent standard (Section 4), the public anchoring path (Section 5), the Seal as both an artifact and an open architectural question (Sections 6 and 7), the relationship to existing regulatory frameworks (Section 9), and the open frontier of execution-time policy binding (Sections 7 and 10). Where a feature is partial, mocked, or unbuilt, it is labelled explicitly.
2.Introduction
The compliance posture of an autonomous AI system is usually expressed as a log: a stream of events written to a file, a database, or a SIEM. A log is observable. It is not, by itself, provable. Three properties separate observability from proof.
The first is integrity. A plain log can be edited after the fact by anyone with write access. The receiver of a compliance report has no way to know whether the rows they are reading are the same rows that were written.
The second is authenticity. A log row claims to come from agent A acting under authority B. A reader who cannot independently verify either claim is taking the publisher's word for it. In a regulated context, that is the entire problem.
The third is timeliness. An event that was logged "at 2026-04-12 14:32 UTC" is only useful if a third party can confirm the row existed at that time and not, for example, written retroactively the morning of an audit.
RANKIGI replaces the log with a structure that carries each property by construction. Events enter through a single ingest endpoint. Each event is hashed into a per-agent SHA-256 chain that uses a server-stamped timestamp. The agent passport is signed at issuance with Ed25519 and (optionally) ML-DSA-65, and individual events submitted by a passport-bound agent must carry a fresh Ed25519 signature over the canonical payload. The chain head is anchored daily to Sigstore Rekor and to the FreeTSA RFC 3161 timestamp authority, both of which produce evidence that does not depend on RANKIGI to verify. The result is that "what happened" can be proved without taking RANKIGI's word for it.
The remaining hard problem is what this paper calls the binding problem: connecting a pre-execution admissibility decision (what the agent was authorized to do at the moment of action) to the cryptographic proof of what the agent in fact did. Sections 6 and 7 describe what is built and where the open frontier lies.
3.The Hash Chain
The hash chain is the foundational evidentiary structure. It is a per-agent, append-only sequence of SHA-256 hashes where each new event commits to the previous event's hash, the canonical event payload, and a server-stamped timestamp.
3.1 Canonical JSON
Hashing is deterministic only if serialization is deterministic. RANKIGI uses a custom canonical JSON serializer rather than JSON.stringify, because platform serialization under-specifies key ordering, number precision, and undefined handling.
The serializer is implemented at src/lib/crypto/canonical-json.ts:1 and exported as canonicalJson(value: unknown): string at line 126. The hard rules, transcribed from the file's preamble, are:
- Object keys sorted lexicographically by Unicode code point.
- Whole-valued numbers serialize as integers;
1.0becomes"1",-0.0becomes"0". - Other finite numbers serialize using the shortest decimal that round-trips. Scientific notation is rejected; values that would otherwise serialize that way are converted to fixed notation.
- Non-finite numbers (
NaN,Infinity,-Infinity) are rejected at line 56. - Strings are escaped only as RFC 8259 requires.
- Maximum nesting depth is 10 levels (line 35).
The output of canonicalJson is the byte sequence fed into SHA-256. The same payload, serialized by a Node implementation and a Python port, must produce identical bytes. This property is what allows external auditors to recompute hashes independently.
3.2 The Hash Function
sha256Hex is at src/lib/crypto/hash.ts:3:
export function sha256Hex(input: string): string {
return crypto.createHash("sha256").update(input, "utf8").digest("hex");
}It returns a 64-character lowercase hex digest of the UTF-8 encoded input.
3.3 The Hash Input
The chain hash for each event is computed as the SHA-256 of a pipe-delimited concatenation:
prev_hash | server_received_at | org_id | agent_id | canonical_payload | [intent_hash]The construction is in the Postgres function ingest_event_with_hash, defined in the migration supabase/migrations/20260430000002_server_received_at.sql:81-88:
v_input := v_prev_hash
|| '|' || v_chain_ts_text
|| '|' || p_org_id::text
|| '|' || p_agent_id::text
|| '|' || p_canonical_payload
|| CASE WHEN p_intent_hash IS NOT NULL THEN '|' || p_intent_hash ELSE '' END;
v_hash := encode(sha256(v_input::bytea), 'hex');Two things are notable. First, the timestamp used in the hash is server_received_at, the moment Postgres saw the event, not occurred_at, the timestamp the agent claimed. This means an agent cannot backdate or forward-date a chain entry by manipulating occurred_at; the entry's position in the cryptographic record is fixed at server receipt. Second, when an agent supplies an encrypted intent (Pro tier and above), the intent hash is mixed into the chain input so the intent is committed alongside the event without exposing the plaintext to the server.
3.4 Genesis
For each new agent, the first chain entry uses a genesis hash of 64 ASCII 0 characters, computed by repeat('0', 64) in the migration at line 70. This is the standard convention for Merkle and chain structures and is recognized by the verifier as a special case.
3.5 Insertion
The migration inserts into event_hash_chain in the same database transaction that inserts into events. The schema for event_hash_chain (assembled from src/sql/001_schema.sql:106-122, src/sql/004_snapshots.sql:12, and supabase/migrations/20260313000021_chain_index.sql) is:
| Column | Type | Notes |
|---|---|---|
id | uuid | primary key, generated |
org_id | uuid | tenant isolation |
agent_id | uuid | per-agent chain |
event_id | uuid | unique, foreign key to events |
prev_hash | text | hex of prior chain head, or genesis |
hash | text | hex of sha256(input) |
chain_index | bigint | unique per (org_id, agent_id) |
payload_canonical | text | canonical JSON used in input |
intent_ciphertext | text | nullable, AES-256-GCM ciphertext |
intent_hash | text | nullable, mixed into hash input |
server_received_at | timestamptz | authoritative timestamp |
created_at | timestamptz | row creation |
The unique constraint on (org_id, agent_id, chain_index) plus the per-agent advisory lock acquired inside the RPC at line 55 prevent fork creation under concurrency. The atomicity of events and event_hash_chain insertion eliminates the class of bugs where an event is recorded but never sealed.
The ingest route at src/app/api/ingest/route.ts:297-309 is the single call site:
const { data: rpcData, error: rpcError } = await supabaseAdmin.rpc(
"ingest_event_with_hash",
{
p_org_id: org_id,
p_agent_id: agent_id,
p_occurred_at: occurredAt.toISOString(),
p_action: action.trim(),
p_tool: tool ?? null,
p_severity: severity ?? "info",
p_payload: payload,
p_canonical_payload: canonicalPayload,
p_intent_ciphertext: intentCiphertext,
p_intent_hash: intentHash,
p_server_received_at: serverReceivedAt.toISOString(),
},
);There are no asynchronous workers in the chain construction path. The chain row exists if and only if the corresponding event row exists, in the same transaction.
3.6 Verification
Verification is implemented as the Postgres function verify_agent_chain at supabase/migrations/20260430000002_server_received_at.sql:126-188, exposed via the API at src/app/api/agents/[id]/verify/route.ts:62. The function recomputes the SHA-256 input for every block in the agent's chain and emits, for each block, the stored hash, the recomputed hash, a boolean indicating whether prev_hash matches the prior block (or genesis), and a composite ok flag. Verification is independent of any RANKIGI application code; it is pure SQL over committed data.
Section 5 describes how the same property holds across the trust boundary: a third party can verify the chain head against the public Rekor log without contacting RANKIGI at all.
4.The Agent Passport and KYA Standard
Where the hash chain answers "what happened," the passport answers "who did it." A passport is a signed statement issued by an organization that binds a named agent identity to a public key, a list of authorized tools, and a current risk score, and which can be revoked.
4.1 The `agent_passports` Schema
The table is created at supabase/migrations/20260306000015_passports.sql:8-28:
create table if not exists agent_passports (
id uuid primary key default gen_random_uuid(),
org_id uuid not null references organizations(id) on delete cascade,
agent_id uuid not null,
name text not null,
version text,
purpose text,
status text not null default 'active'
check (status in ('active', 'suspended', 'revoked')),
public_key text,
current_risk_score int not null default 100
check (current_risk_score >= 0 and current_risk_score <= 100),
current_jwt text,
jwt_issued_at timestamptz,
jwt_expires_at timestamptz,
created_at timestamptz not null default now(),
revoked_at timestamptz,
revoked_by uuid references auth.users(id),
foreign key (agent_id, org_id) references agents(id, org_id),
unique (org_id, agent_id)
);Post-quantum columns are added on the parent agents table by supabase/migrations/20260427000001_post_quantum_crypto.sql:12-18: passport_signature_ml_dsa, passport_public_key_ml_dsa, signature_algorithm (default ed25519+ml-dsa-65), quantum_ready, and crypto_version (default v1-hybrid).
4.2 Issuance
A passport is created in two steps. The registration endpoint at src/app/api/passport/register/route.ts:82-93 inserts the row with no key material. The provisioning endpoint at src/app/api/passport/provision/route.ts:58-92 generates an Ed25519 keypair, stores the public key in SPKI DER base64 form, and issues a JWT signed with the org's signing key:
const { publicKey, privateKey } = generateKeyPairSync("ed25519");
const publicKeyB64 = publicKey
.export({ type: "spki", format: "der" })
.toString("base64");
const { jwt, issuedAt, expiresAt } = await signPassportJwt({
id: passport.id,
org_id: passport.org_id,
agent_id: passport.agent_id,
name: passport.name,
version: passport.version,
status: passport.status,
current_risk_score: passport.current_risk_score,
public_key: publicKeyB64,
authorized_tools: authorizedTools,
});JWT signing is at src/lib/passport/jwt.ts:1-62 using jose with EdDSA. The JWT carries the full passport claim set including risk score and authorized tools, so a downstream consumer can resolve "is this agent allowed to call this tool" without contacting RANKIGI.
4.3 Hybrid Ed25519 plus ML-DSA-65 Signing
The hash chain is signed by the agent at the event level, not the passport level. The signing function is at src/lib/crypto/index.ts:106-128:
export function signPassport(input: SignPassportInput): PassportSignature {
const message = hexToBytes(input.passportHash);
const sigEd = ed25519.sign(message, input.ed25519PrivateKey);
const result: PassportSignature = {
algorithm: "ed25519+ml-dsa-65",
signature_ed25519: bytesToHex(sigEd),
public_key_ed25519: bytesToHex(input.ed25519PublicKey),
crypto_version: "v1-hybrid",
quantum_ready: false,
};
if (input.mlDsaPrivateKey && input.mlDsaPublicKey) {
const sigPq = ml_dsa65.sign(message, input.mlDsaPrivateKey);
result.signature_ml_dsa = bytesToHex(sigPq);
result.public_key_ml_dsa = bytesToHex(input.mlDsaPublicKey);
result.quantum_ready = true;
}
return result;
}ML-DSA-65 is implemented through @noble/post-quantum. When both signatures are present, verification at src/lib/crypto/index.ts:136-150 prefers the ML-DSA-65 path. An on-demand upgrade endpoint at src/app/api/agents/[id]/upgrade-pq/route.ts:9-66 can mint and persist the post-quantum signature for an existing agent without re-issuing the passport itself. ML-DSA-65 support is fully implemented; the network has not yet rotated all production passports onto the hybrid scheme by default. The Ed25519 path is always present.
4.4 KYA: Know Your Agent
KYA is the standard the passport claims to satisfy. The active version is 1.1.0, declared at src/app/api/public/revocation/route.ts:8. The browser-side verification engine is at src/lib/kya/verify.ts:1-310 and performs, in order:
- JSON parsing.
- Schema conformance: required fields are
event_id,agent_id,action_type,chain_index,previous_event_hash,event_hash. - Hash recomputation: canonical JSON of the chain row, hashed with SHA-256, compared to the stored hash.
- Chain link integrity: each
previous_event_hashmatches the prior event'sevent_hash. - Chain index monotonicity.
- Passport validity check (light, browser-side).
- X.509 certificate chain validation (PEM format, per the FEDIS authority model in Section 9).
4.5 Public Revocation
Revocation status is queryable without authentication at src/app/api/public/revocation/route.ts:128-195. The endpoint accepts passport_id or agent_id, returns active, revoked, expired, or unknown, and ships with permissive CORS and a 60 request per minute rate limit. This is the resolution mechanism that lets a third party, including a regulator or counterparty, ask "is this passport still valid right now" without ever holding RANKIGI credentials.
4.6 What Is Not Yet Built
A documented HTTP route to revoke a passport does not exist; revocation today goes through the dashboard UI and the underlying agent_passports.status column. Owner identity, geographic location, and "who hired the agent" semantics are tracked separately in agent_ownership rather than embedded in the passport claim set. Both are fixable by extending the existing schema.
5.Sigstore and RFC 3161 Anchoring
The chain proves internal integrity. Anchoring extends the proof across the trust boundary so a third party who does not trust RANKIGI can still verify, against public infrastructure, that a particular chain head existed at a particular time.
5.1 Sigstore Rekor
The anchoring entry point is src/lib/webwitness/anchor.ts:124-220. It submits the daily snapshot root to the public Sigstore Rekor transparency log:
const res = await fetch(REKOR_BASE, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});REKOR_BASE is https://rekor.sigstore.dev/api/v1/log/entries (line 47). Submission uses ECDSA P-256 because the Rekor hashedrekord v0.0.1 schema rejects Ed25519. The signing key is read from RANKIGI_REKOR_PRIVATE_KEY. The caller is the daily cron at src/app/api/cron/snapshots/route.ts:161-186, which is non-blocking; an anchoring failure does not break ingest or the chain itself.
The Rekor response carries logIndex and logEntryId. These are stored on the snapshot row by the columns added in supabase/migrations/20260423000001_add_anchor_columns.sql:10-14:
ALTER TABLE daily_snapshots ADD COLUMN IF NOT EXISTS anchor_provider text;
ALTER TABLE daily_snapshots ADD COLUMN IF NOT EXISTS anchor_log_index bigint;
ALTER TABLE daily_snapshots ADD COLUMN IF NOT EXISTS anchor_log_entry_id text;
ALTER TABLE daily_snapshots ADD COLUMN IF NOT EXISTS anchor_url text;
ALTER TABLE daily_snapshots ADD COLUMN IF NOT EXISTS anchor_verified_at timestamptz;A third party verifies an anchor by fetching the Rekor entry directly and decoding the body; the public passthrough endpoint at src/app/api/verify-rekor/[logIndex]/route.ts:43-137 provides a documentation-grade redirect, but the verification is idempotent against the Rekor public key and does not require RANKIGI's involvement.
5.2 RFC 3161
In addition to Rekor, daily snapshots are timestamped against FreeTSA at https://freetsa.org/tsr. The implementation is at src/lib/timestamp/rfc3161.ts:265-310, using pkijs, asn1js, and @peculiar/x509. The TimeStampToken is persisted as base64 in the timestamp_proofs table created by supabase/migrations/20260411000069_rfc3161_timestamp_proofs.sql:8-49.
The verification path produced for external auditors is at src/lib/timestamp/evidence-package.ts:55-163. It includes the recipe an auditor follows to verify without RANKIGI:
- Download the
.tsrfile from the verification URL. - Download the FreeTSA root certificate from
https://freetsa.org/files/cacert.pem. - Run
openssl ts -verify -in proof.tsr -data snapshot_hash.bin -CAfile cacert.pem. - A successful verification confirms the hash existed at the stated time, signed by FreeTSA.
This makes "the chain head was committed at time T" provable to any party with openssl and an internet connection.
5.3 Solana Legacy Path
A Solana memo-program anchoring path remains active for the WebWitness capture flow at src/app/api/webwitness/capture/route.ts:95 and the legacy /api/v1/anchor endpoint. The implementation at src/lib/webwitness/anchor.ts:223-283 issues real Solana transactions when SOLANA_PAYER_PRIVATE_KEY is configured. New anchoring goes through Rekor and RFC 3161; the Solana path is preserved for backward compatibility with WebWitness receipts already in the wild.
5.4 WebWitness Receipts
WebWitness is a separate capture surface for agent web perceptions. Each capture produces a receipt signed with the org's Ed25519 key, defined at src/lib/webwitness/receipt.ts:6-21 and verified, again without RANKIGI, by the function at src/lib/webwitness/receipt.ts:141-150. The org's public key is exposed at the unauthenticated route src/app/api/v1/orgs/[org_id]/public-key/route.ts:11-37, so a holder of a receipt can verify it standalone.
6.The Seal
The Seal is the part of the architecture that frames a question rather than answers one. It is a public, externally verifiable statement that an agent has met a defined set of behavioral requirements over a defined period.
6.1 What Is Built
The Seal data model is at supabase/migrations/20260415000074_seal_system.sql:1-132. Three tables matter:
seals(lines 31-55): the seal definition, withname,slug,category(one ofhealthcare,finance,security,compliance,ai-safety,general),expiry_model(one-time,annual,continuous,custom), arequirementsJSON array, and astatus(draft,active,deprecated).seal_applications(lines 68-85): the application workflow, withstatus(pending,evaluating,passed,failed,manual_review,withdrawn),evaluation_result, andpayment_status.agent_seals(lines 95-113): the issued seal, withseal_hash,snapshot_hash,previous_seal_hash(chain linkage per agent),public_verify_url, andis_valid.
Community-proposed seals enter through seal_submissions (supabase/migrations/20260502000004_seal_submissions.sql:1-28) and are promoted to seals after staff review.
The evaluation engine at src/lib/seals/evaluation-engine.ts:1-212 understands these requirement types: governance_score_min, zero_violations, violations_max, chain_integrity, uptime_min, tool_required, tool_forbidden, human_oversight_min, oracle_attestation (with SSRF protection for the external authority callout), and manual_review. The application endpoint is src/app/api/v1/seals/[id]/apply/route.ts:1-80. External resolution is at src/app/api/v1/seals/verify/[hash]/route.ts:1-50, with a public UI at src/app/seals/verify/[hash]/page.tsx:1-220.
6.2 The Four Functions
A Seal is intended to perform four distinct functions. The current implementation status of each:
- Policy declaration. A Seal explicitly enumerates its requirements as structured JSON. Implemented; see the
requirementscolumn at line 42 of the seal system migration.
- External resolvability. A Seal can be verified by any party with the seal hash, with no RANKIGI authentication. Implemented; see
/api/v1/seals/verify/{hash}.
- Execution-time binding. A Seal should bind to the moment of agent action, so a per-event proof can carry "this action was admissible under Seal S at time T." This is not implemented. Today, Seals are evaluated against a historical window and issued; they are bound to the agent identity, not to individual events. Section 7 treats this as the open architectural question.
- Threshold enforcement. A Seal's requirements should fail closed when their thresholds are violated. Partially implemented. The evaluation engine produces correct pass and fail verdicts (see, for example,
governance_score_minat line 84). What is missing is a runtime gate that uses those verdicts to refuse a future action.
The honest summary is that Seals work as static, externally resolvable badges. They are not yet the runtime authority surface that "execution-time binding" implies.
7.The Binding Problem
The unresolved question in the RANKIGI architecture is how to bind a pre-execution admissibility decision to the cryptographic record of the action that follows.
The hash chain commits "what happened." The passport commits "who acted." Anchoring commits "when." The Seal can commit "under what authority." What is missing is the artifact that says, atomically and verifiably, "at the moment this event was committed, the agent held authority A, the action was admitted by policy P, and both were recorded in the same hash."
The closest the current system gets is the per-event passport signature. The ingest route at src/app/api/ingest/route.ts:249-292 requires a governed agent to present a fresh Ed25519 signature over the canonical payload before the event will be sealed. This proves that a holder of the passport's private key authorized this specific event. It does not prove that the action was admissible under a stated policy at that moment.
Policy evaluation happens, but in the wrong place in the timeline. The evaluator at src/lib/enforce/evaluate.ts:1-225 understands three policy types: hard_limit (block when a numeric field exceeds a threshold), tool_block (deny a tool by name), and time_restriction (deny outside a UTC window). It runs after the event is already sealed. The verdict is returned to the SDK as advisory. The agent is expected to halt voluntarily on a block verdict. There is no step where the evaluator's verdict is hashed into the chain entry alongside the event itself.
Several solutions are conceivable. The agent can request a pre-flight admissibility token before each action, the server can return a signed verdict, and the verdict's hash can be folded into the chain input as a sibling of intent_hash. The Seal that authorized the action can be embedded as seal_hash in the chain input, so a third party reading the chain can resolve "this event committed under Seal S." The policy evaluator can be moved into the same database transaction as the chain insert, with a deny verdict producing no row at all. Each option has different latency, atomicity, and verifiability trade-offs. None is implemented today.
This is the work the rest of the standard, and the next phase of the codebase, are organized around.
8.The Combined Architecture
Three proofs cooperate to answer three different questions. The composition is what makes the system useful.
What happened is the SHA-256 hash chain (Section 3). It is internally complete; any party with the chain rows can recompute hashes and confirm the chain is unbroken.
Who acted and under what authority is the passport plus the per-event signature (Section 4). The passport is the durable credential. The event signature is the per-action attestation. Together they answer "this passport's holder authorized this event."
When is the daily anchor in Sigstore Rekor and FreeTSA (Section 5). The anchor commits the chain head to public, append-only logs that no party, including RANKIGI, can rewrite. The tools needed to verify a Rekor entry or an RFC 3161 timestamp token are public and standard.
The relationship to the planned downstream layers is worth stating accurately. The codebase contains references to "downstream evidentiary layers" in copy. EVIDE does not exist in source: a literal search of src/ and supabase/migrations/ returns zero hits. FEDIS appears only in the published standard at src/app/standard/page.tsx:752-759, where it is described not as a runtime system but as a property: an externally resolvable authority reference whose chain is verifiable by a court-appointed expert using standard PKI tools without access to RANKIGI. The X.509 certificate chain validation already implemented in the KYA verifier (src/lib/kya/verify.ts, step 7 of Section 4.4) is what makes the system FEDIS-conformant.
In other words: FEDIS is satisfied today through the X.509 path. EVIDE is reserved space for future evidentiary abstractions and should be treated as Phase 2.
The federation surface is real but partial. The schema at supabase/migrations/20260317000031_federation.sql:1-76 defines federation_endpoints, federation_handshakes, and a trust score column. The trust score formula at src/lib/federation/trust-score.ts:1-32 combines chain-verified status, compliance score, drift state, owner presence, recent violations, and approver status into a 0.00 to 1.00 scalar. Routes exist for handshake initiation, verification, and completion. Cross-organization event sharing is not yet wired. Arbitration routes exist as scaffolding. Treat federation as Phase 2.
The Equilibrium subsystem (supabase/migrations/20260327000046_equilibrium.sql:1-178) is more complete. It captures six per-session metrics (tool usage, latency, decision distribution, scope compliance, output consistency, external calls), locks a baseline at 30 sessions, and supports tiered automated enforcement (throttle, sandbox, suspend, revoke). Equilibrium is the runtime gate the Seal system is missing; it operates on behavioral drift rather than on per-event admissibility, but the architectural pattern (compute a verdict, apply enforcement, record an enforcement_event with event_hash) is the same one the binding problem will eventually reuse.
9.Regulation Mapping
Compliance frameworks ask three recurring questions. This section maps each question to the corresponding RANKIGI artifact.
SOC 2. The criteria most relevant to autonomous AI systems are CC7.2 (system monitoring), CC7.3 (incident detection), and CC8.1 (change management). RANKIGI satisfies CC7.2 and CC7.3 with the events table plus the hash chain plus the alerts pipeline (alerts schema; severity values info, warn, critical per events.severity). The hash chain provides the integrity property that distinguishes a SOC 2 control from a logging hope: the auditor can verify the trail was not edited after the fact. CC8.1 is supported through the agent_passports.status lifecycle, which captures suspension, revocation, and the actor who performed the action.
EU AI Act, Article 13 (transparency and provision of information). Article 13(1) requires high-risk AI systems to be designed so that operators can interpret system output and use it appropriately. Article 13(3)(b) requires logs sufficient to trace function and identify causes of risk. RANKIGI satisfies the latter through the chain plus payload plus intent_ciphertext triple: every event carries a canonical payload, a server-stamped timestamp, and (for governed events) an encrypted intent record whose hash is committed alongside. The intent ciphertext can be decrypted by the operator's own key at audit time, enabling traceability without exposing intent plaintext to RANKIGI. Article 12 (record-keeping over the lifetime of the system) is supported by the daily snapshot and external anchor, which together prove that records have not been altered.
HIPAA. The Security Rule sections 164.308(a)(1) (security management process), 164.312(b) (audit controls), and 164.312(c) (integrity) are the relevant ones. Audit controls are satisfied by the chain itself. Integrity is satisfied by the SHA-256 hash, the canonical JSON discipline, and the FreeTSA timestamp; an integrity violation would be detectable by verify_agent_chain and visible to any third party with the snapshot's TimeStampToken. PHI is never committed to the chain in plaintext; the canonical payload is what is hashed, and operators using RANKIGI in HIPAA contexts are expected to encrypt PHI fields at the agent layer before they enter payload.
The pattern across all three frameworks is the same: the framework asks for evidence of a property; RANKIGI provides the cryptographic structure that makes the evidence verifiable rather than asserted.
10.Open Directions
Three directions are immediate and tractable.
The commitment handshake. The binding problem of Section 7 has a concrete first step: introduce a pre-flight admissibility endpoint that accepts a canonical action description, runs the policy evaluator synchronously, and returns either a signed admit token or a signed deny token. The token's hash is folded into the chain input on the next ingest, alongside intent_hash. The verifier can then prove, from the chain alone, that an event was admitted by a specific evaluator state at a specific time. The schema change is small (one column, admission_hash); the algorithmic change is local; the verification update is purely additive.
Cross-organization federation proof. Federation handshakes today produce federation_handshakes rows but no chain artifact. The next step is to anchor the handshake outcome in the same way events are anchored: hash the negotiated scope and the participating passports, sign the result with both organizations' Ed25519 keys, and submit the hash to Rekor. The handshake's federation_hash column already exists on the schema; it is reserved for this construction.
Dynamic Seal binding. The Seal as built today is a static badge. The version that makes it execution-time binding embeds the active Seal's hash into the per-event chain input, conditioned on the event being within the Seal's policy envelope. This is the same shape as the commitment handshake but with the Seal's policy declaration playing the role of the evaluator. The path forward is to merge the two ideas: the admissibility evaluator consults the active Seal, the Seal's hash and the verdict's hash are both folded into the chain, and the resulting chain entry is a per-event proof that this action committed under that authority at that time.
These three are the bridges from "we can prove what happened" to "we can prove what was authorized and whether the action matched." They are the architecture's next horizon.
Appendix A.File Index
For convenience, the primary citations referenced in the body of the paper:
| Concern | Path | Lines |
|---|---|---|
| Canonical JSON | src/lib/crypto/canonical-json.ts | 1-128 |
| SHA-256 helper | src/lib/crypto/hash.ts | 1-5 |
| Hash chain RPC | supabase/migrations/20260430000002_server_received_at.sql | 28-114 |
| Chain verification RPC | supabase/migrations/20260430000002_server_received_at.sql | 126-188 |
event_hash_chain schema | src/sql/001_schema.sql | 106-122 |
| Ingest route | src/app/api/ingest/route.ts | 50-409 |
| Events schema | supabase/migrations/20260220000001_schema.sql | 74-85 |
agent_passports schema | supabase/migrations/20260306000015_passports.sql | 8-28 |
| Post-quantum columns | supabase/migrations/20260427000001_post_quantum_crypto.sql | 12-18 |
| Passport JWT signing | src/lib/passport/jwt.ts | 1-62 |
| Hybrid Ed25519 + ML-DSA-65 | src/lib/crypto/index.ts | 106-150 |
| KYA verification | src/lib/kya/verify.ts | 1-310 |
| Public revocation | src/app/api/public/revocation/route.ts | 8, 128-195 |
| Rekor anchor | src/lib/webwitness/anchor.ts | 124-220 |
| Anchor schema | supabase/migrations/20260423000001_add_anchor_columns.sql | 10-14 |
| Public Rekor verify | src/app/api/verify-rekor/[logIndex]/route.ts | 43-137 |
| RFC 3161 | src/lib/timestamp/rfc3161.ts | 265-310 |
timestamp_proofs schema | supabase/migrations/20260411000069_rfc3161_timestamp_proofs.sql | 8-49 |
| RFC 3161 evidence package | src/lib/timestamp/evidence-package.ts | 55-163 |
| WebWitness receipt | src/lib/webwitness/receipt.ts | 6-21, 141-150 |
| Seal system schema | supabase/migrations/20260415000074_seal_system.sql | 1-132 |
| Seal evaluation engine | src/lib/seals/evaluation-engine.ts | 1-212 |
| Seal verification | src/app/api/v1/seals/verify/[hash]/route.ts | 1-50 |
| Federation schema | supabase/migrations/20260317000031_federation.sql | 1-76 |
| Trust score | src/lib/federation/trust-score.ts | 1-32 |
| Equilibrium schema | supabase/migrations/20260327000046_equilibrium.sql | 1-178 |
| Policy enforcement | src/lib/enforce/evaluate.ts | 1-225 |
| FEDIS spec text | src/app/standard/page.tsx | 752-759 |
Appendix B.Phase 2 and Unbuilt Surface
Items mentioned in this paper that are not yet implemented in code:
- Execution-time Seal binding. Seals today are static post-hoc verdicts.
- Pre-flight admissibility tokens. Policy evaluator runs after ingest, advisory only.
- Cross-organization event sharing under federation scope. Schema permits, code does not implement.
- Federation handshake anchored to Rekor.
federation_hashcolumn reserved, construction not yet wired. - EVIDE. Not present in source. Reserved for future evidentiary abstractions.
- HTTP route for passport revocation. Lifecycle column exists, no documented endpoint.
- Distributed rate limiting. Current implementation is in-memory per replica.
- Idempotency key on the events table. Duplicate ingest produces duplicate events; clients are expected to deduplicate.
These are honest gaps. They are the work that turns the paper into a system.