JIL Sovereign · Technical thesis & defence
JIL L1: chain per jurisdiction, policy inside consensus, and an evidentiary ledger that does not rest on decentralisation
7935fe2635
Measured 2026-08-27, estate hardware
Ring 0 52,447 lines / 11 crates
Pure core ~3,100 lines
Distributed ledgers earn trust two ways. Public chains earn it by making every participant a potential adversary and paying, in energy or capital, for safety under that assumption. Permissioned chains usually earn it by asking to be trusted, dressed in the vocabulary of the first approach.
This work takes a third route. JIL's Attested Consensus Layer is permissioned, single-operator, and makes no decentralisation claim at all. It argues that a ledger operated entirely by one company can still be evidentiary, provided three properties hold together: every vote is attributable to a named legal entity, every block is byte-for-byte replayable by a party who was not there, and disagreement between nodes is treated as a fact to be recorded rather than a minority to be outvoted.
The technical core is a bifurcated fault model in which a node that does not answer and a node that answers differently are handled as opposite events, not as points on a scale of availability. The engineering core is a ring discipline that makes the determinism this depends on a property of the compiled artifact rather than of code review. The product core is attribution: the difference between "a quorum signed" and "these four institutions signed, and here is which one dissented" is the whole evidentiary value.
The federation is what makes this a system rather than a chain. Each jurisdiction runs its own cell with its own genesis, validators and policy; value crosses only when the receiving cell can verify the hub's authorization against a key committed in its own genesis. Policy is evaluated inside consensus by ATCE, so the sending cell's rules are enforced on departure and the receiving cell's on arrival, and both verdicts are replayable rather than asserted.
The thesis also reports what building it found, including a signature encoding in which one authorization had two valid readings and two payouts, an anchor field that reported a derived value a reader would take as a chain confirmation, and one process pathology that accounts for the rest.
A bank asks a settlement ledger one question, in two tenses.
Now: did this transaction settle, and can you prove it.
In fifteen years: who agreed to it, under what authority, and can you show me that nobody objected at the time.
Neither question is about throughput, and neither is answered by a hash. They are questions about evidence that survives its author, and they are the questions a regulator asks in the same words.
Public-chain designs answer the first well and the second poorly: a pseudonymous validator set cannot tell you which institution agreed, and a chain that has hard-forked cannot tell you what it said before. Permissioned designs usually answer the second by asserting good faith, which is not evidence.
Three familiar moves are unavailable, and saying so early is what makes the rest defensible.
Byzantine fault tolerance is not the model. Every validator is operated by JIL. A threshold among nodes one company controls bounds accident and partial compromise; it does not bound that company. Claiming BFT here would invite a threat model the system does not satisfy, and an auditor would be right to reject everything that followed.
Decentralisation is not the argument. No open validator set, no staking, no economic security. A design that quietly implies otherwise is making a claim its operator would have to defend in court.
"Trustless" is false. The honest formulation is narrower and stronger: JIL's actions are checkable. A single-operator chain cannot defend against its operator. It can make what the operator did permanently inspectable by parties with standing to contradict it.
A ledger operated by a single party can serve as evidence, without any decentralisation claim, if disagreement is recorded rather than resolved by majority, if every vote is attributable to a named legal entity, and if the execution is deterministic enough that a stranger can re-run it years later and get the same answer.
Three claims follow, and the rest of this document is their defence.
The word that does most of the work in this system is cell. A cell is a jurisdiction's own chain, with its own genesis, its own validator set, its own policy, and its own regulator. It is not a shard, a subnet, or a tenant. It is a separate ledger that happens to run the same engine.
The federation is what lets value cross between them without either side having to trust the other's operator.
A single global ledger with jurisdictional flags in the rows is the obvious design, and it fails on three counts that a regulator asks about in the first meeting.
As-built jil-abci/src/federation.rs.
A cell honours a value release only when it can itself verify the hub's authorization, not because a bridge-authority key moved a balance. The hub's Ed25519 federation key is pinned at genesis, committed into the app hash through a storage slot under the reserved jil1federation account. A FederationRelease carries the hub's signature over a canonical authorization; the receiving cell verifies it against the pinned key, binds it to this cell, enforces exactly-once through a replay slot, and only then moves the float.
The distinction matters. A bridge that trusts a signer is a bridge whose security is the signer's key hygiene. A cell that verifies an authorization against a key committed in its own genesis is checking something it already believes for reasons that do not depend on the hub being honest today.
The v2 authorization was hand-written JSON, and recipient, settlement_id and issued_for are free-form strings the submitter chooses. A recipient containing ","settlement_id":" moved the field boundaries. One hub signature then had two valid readings, with two different settlement ids, and therefore two different exactly-once slots and two payouts against one lock.
Separately, from_cell was not bound at all, yet it is written into the anchored crossing event. So the source cell of an anchored crossing was submitter-chosen: the hub verified a lock on a specific source cell and then did not say which, and the same authorization could be settled and anchored as a crossing from any other cell while the signature still verified.
The fix is v4: every field length-prefixed under a domain tag, and from_cell bound. The corridor the event asserts is now the corridor the hub attested to.
It is a breaking signature-format change and it is fail-closed: a v2 signature does not verify, so a cell running v4 settles nothing until the hub signs v4 bytes. Update the hub first, cut the cells over after, and expect no in-flight authorization to survive the cut.
The general lesson is worth more than the fix. Canonicalisation is not serialisation. Any encoding where a caller-controlled string can move a field boundary means a signature covers a set of documents rather than one, and the signer never learns which one was settled.
The hub seals hybrid, Ed25519 and ML-DSA-65. On-chain, only the Ed25519 half is verified today, because deterministic in-consensus ML-DSA verification is Phase 2; the ML-DSA half is carried and anchored off-chain.
Built, not deployed This is stated rather than glossed. Until on-chain ML-DSA verification lands, the corridor's in-consensus authentication is classical, and the post-quantum property of a crossing rests on an off-chain artifact. A panel is entitled to hold that against the claim, and the design records it so nobody discovers it later.
As-built acl_roster::assess:
PROCEED if responding >= LIVENESS_FLOOR and all responding agree
EJECT+PROCEED if exactly one responder disagrees and the rest are unanimous
HALT if the disagreement is broader than that
STALL if responding < LIVENESS_FLOOR
A node that does not answer has a crash fault: benign, expected, tolerated. Networks partition and hosts die, and a design that cannot absorb that is not a design.
A node that answers differently has done something categorically worse. It has falsified the assumption the whole architecture rests on, because identical programs on identical inputs are supposed to produce identical output. That is not a minority opinion. It is a defect report about a machine, a build, or an adversary.
Disagreement is evaluated before the liveness floor. Read top to bottom, a disagreement occurring while the fleet is understaffed returns STALL, which discards a proven determinism violation because the audience happened to be small when it surfaced. Unavailability is recoverable. A determinism violation is not, and it does not become less true for having been observed by fewer nodes.
A dissent arriving after the commit still counts. Otherwise the guarantee is false exactly when a dissent is slow, which is when it is most likely to be missed.
Both were wrong in a first draft and both are now pinned by tests. That they were wrong first is itself evidence for the claim: the ordering is not obvious, and a system that gets it wrong looks identical to one that gets it right until the day it matters.
LIVENESS_FLOOR is three quarters of the voting set, rounded up, never below a strict majority.
Safety comes from unanimity among responders, and unanimity does not depend on how many responded. The floor answers a different question: how much of the fleet must be present before agreement among those present counts as evidence. Unanimity among two of twenty is technically unanimous and means almost nothing.
This distinction earns its own sentence because floors in other systems are safety thresholds, and a reader who assumes that will mis-read the entire model.
If two honest nodes can disagree, the fault model above becomes noise. So the question is not whether the code is deterministic but what forces it to stay that way across years and authors.
As-built Eleven crates, 52,447 lines, in three rings.
| Ring | Contract | Contains |
|---|---|---|
| 0 | Pure. No I/O, clock, randomness, threads, or unordered iteration reaching a hash. | acl-types, acl-codec, acl-crypto, acl-roster, acl-genesis, acl-app-api, acl-consensus, acl-request, acl-invariants, acl-dispute, acl-app |
| 1 | Impure and dumb. Performs effects, makes no protocol decisions. | acl-state, acl-store, acl-net, acl-sync |
| 2 | Tools. Reads the world, drives Ring 0. | acl-node, acl-verify, acl-sim |
Ring 1 being dumb matters as much as Ring 0 being pure. A storage layer that decides anything has moved a protocol decision outside the audited boundary. The 2026-08-02 production divergence lived in exactly that gap: consensus-relevant state held beside the persisted set, so a restarted node came back with a different one and diverged permanently.
Ring 0 is no_std, so std::time, std::fs, std::net and std::thread are not in scope: a clock read does not compile. A clippy deny list covers the rest, including indexing, unchecked arithmetic, floating point, and lossy casts.
Where a rule can be moved into a type, it is:
ConsensusTime has a private field and no clock-reading constructor. Block time comes from the block.SignatureVerified has one constructor, so "I checked the signature" cannot be asserted by a caller that did not.ValidatorIdx is an index resolved against an epoch roster, so a key rotation cannot silently re-point a historical vote.A comment binds the reader who happens to look. A type binds everyone.
Ring 0 is additionally linked into a cdylib for wasm32-unknown-unknown and its import section is asserted empty. A WebAssembly module reaches the outside world through exactly one channel. If that section is empty, the module has no clock, no filesystem, no network, no entropy, and no way to observe the machine it runs on.
This is the part worth defending hardest, because it converts a claim about discipline into a measurement of the compiled binary. It is checkable by a third party who does not trust the authors and does not read the source.
It caught this work twice in one day. The triage classifier written for §10 was rejected for indexing and unchecked arithmetic. Both were genuine panic paths in consensus-adjacent code, written by an author who knew the rule and violated it anyway. That is the case for mechanical enforcement, stated more persuasively than any argument.
As-built jil-abci/src/atce.rs. The Automated Trust & Compliance Engine is the federation's border guard and the currency platform's per-transfer gate. It is a deterministic, in-consensus policy evaluator.
Compliance is normally a service the chain calls, or a check performed before submission. Both put the decision outside the ledger, which produces the same problem in two shapes: the chain records that a transfer happened and cannot show why it was allowed, and two nodes can reach different verdicts because the service answered differently, or was down.
ATCE puts the evaluation inside the state machine. Every validator re-runs the same evaluation over the same inputs and reaches the same verdict, so the decision is part of the app hash. Three consequences follow, and they are the whole reason for the cost:
decision_record_hash binds profile, context and verdict into one SHA-256 commitment that goes into the CourtChain anchor.When value crosses, the sending cell's policy is enforced on departure and the receiving cell's on arrival. Neither cell is asked to accept the other's judgment, which is the only arrangement two regulators can both live with.
The same evaluate() serves both callers, and also serves the currency platform's per-transfer gate. One evaluator, three call sites: a second implementation of "is this allowed" would be a second answer waiting to happen.
The invariants are the ones §5 demands, applied to policy: fail-closed (any missing input, any threshold breach, deny), deterministic (no clocks, no RNG, no floats, no map-iteration order; the verdict is a pure function of profile and context), and tamper-evident.
Concretely: a jurisdiction allow-list is bounded at 64 entries, so a malicious profile cannot force unbounded work during evaluation. Identity assurance is an ordered enum mirroring NIST 800-63 tiers plus a KYB tier, because a policy that requires "verified" has to mean something a second implementation would agree with.
The cost is that ATCE cannot consult anything live. No sanctions API at evaluation time, no current price, no external risk score computed on demand. Everything it reads must already be on-chain and agreed. That is a real constraint on what compliance can express, and it is the price of the verdict being evidence rather than an assertion.
JIL L1 was EVM-compatible. Of roughly thirty native transaction types dispatched in finalize_block, twenty-seven required no virtual machine. Three did: ContractDeploy, ContractCall, EthTransaction.
The chain was already a settlement-and-evidence machine with a general-purpose VM attached to a tenth of its surface, and that tenth was the source of every structural problem the design had: two identity namespaces on one chain, a permanent classical-crypto surface under a post-quantum ledger, EIP-155 replay protection reduced to a single integer, and a compatibility claim that was not compatible in either direction.
As-built ADR-001 removed the compute surface from both the deployed path and the ACL successor: the jil-vm crate, eth_tx, vm_bridge, revm_bridge, services/eth-rpc, the /eth query subtree, evm_chain_id and its boot gate, and ACL's own 6,661-line port of the same VM. Roughly 19,000 lines deleted. The native operation set is now 31 kinds, none of which need a VM.
What is gained is not a mitigation. The smart-contract exploit class ceases to exist rather than being defended against: no reentrancy, no delegatecall confusion, no proxy-upgrade surface, no approval exploits, no unbounded gas metering. One identity namespace, so a compliance control screens one thing and every future control inherits that property.
What is lost is real and permanent: MetaMask, Foundry, Hardhat, ethers, Solidity as a developer surface, and the ability of a cell operator to deploy custom logic. Every extension becomes a governed protocol change on JIL's schedule.
This is the decision most likely to be wrong, and §16 treats it as such rather than defending it.
Not by argument. By reading the live chains.
| Chain | Height | Transactions | Contracts |
|---|---|---|---|
jil-cell-demo2-1 | 4,367,600 | 1 | 0 |
jil-mainnet-1 ×4 validators | ~3,789,48x | 0 | 0 |
Zero contract accounts anywhere, and effectively no transaction history to break by removing a dispatch arm. The tool that established this (cell-inspect) was written for the purpose and refuses to give a false clean bill of health: a zero count at height 0 exits non-zero, because that is what the wrong storage backend looks like.
The second row is also, read differently, the most uncomfortable finding in this document. See §16.
Designed only with the data structures as-built.
A roster entry is a named legal entity with an LEI in a stated regulatory zone, not a public key. Rosters chain by digest, so a verifier walks from a genesis they trust to the roster in force at any later height without asking JIL to vouch for the intermediate steps.
The halt rule of §4 is meaningless without this. You cannot honour a dissent you cannot attribute. "The fleet disagreed" is not actionable and, more to the point, is not admissible. What a regulator needs is which named entity produced which state root.
Two consequences follow, and one is expensive.
Signatures are Ed25519 and ML-DSA-65, require-both. An OR would be weaker than either scheme alone, since an adversary attacks whichever is currently cheaper.
There is no aggregation. No BLS, no FROST, not later. Aggregation destroys attribution, which is the product. The cost is roughly 1.43 TB per year of retained per-validator signatures at full block rate, and the design records that this cost will silently pressure someone toward aggregation, reopening the one decision that must not be reopened.
As-built jil-abci/src/currency.rs. Removing the VM (§7) leaves a question the panel will ask immediately: if there are no smart contracts, how does anyone issue an instrument?
The answer is a Currency Object: a native managed instrument, not a loose contract. A control-plane service authors it off-chain; the consensus half is a registry pin, mint / circulate / redeem state machines, exactly-once replay slots, and the per-transfer gate, which is atce::evaluate, the same engine as the federation border.
Solvency becomes an on-chain-checkable relation. Circulating currency is a native denom in account balances, and its outstanding amount is the real L1 total supply. So reserve >= outstanding is something a verifier evaluates against the ledger, not a number the issuer reports. With a contract, the same claim is whatever the contract's own accounting says, audited by reading its code.
Authority is bound, and it does not include JIL. Only the licensed issuer's mint and burn key can mint or burn. JIL operates every validator and cannot issue the instrument. That separation is expressible because the operation is native and the authority is a genesis-pinned key, rather than an owner field inside a contract that the deployer can transfer.
Expressiveness. A Currency Object does what the state machine implements and nothing else. A novel instrument is a governed protocol change on JIL's schedule, reviewed like consensus code, which is deliberately slower than deploying a contract.
This is the trade §7 makes, stated in its concrete form: the exploit class is gone, and so is the long tail of things nobody anticipated. §18 names the measurement that would show the trade going wrong.
The rule as first built halted the chain on any disagreement, including one node against nineteen. It is sound, and it does not survive a target of millions of transactions per minute: a four-hour resolution window is roughly 240 million transactions of settlement stopped by one failing memory module. That is not a control. It is an outage with a principled explanation.
Built, not deployed Real in Ring 0. When exactly one node holds the minority root and the remainder is unanimous and still above the floor, the dissenter leaves the responding set and the chain continues. The dissent is recorded, attributed, and adjudicated while the fleet runs rather than while it is stopped.
The floor is measured against the current roster, not the post-ejection one. Ejection is a signed roster transition that has not happened yet at that point, and shrinking the denominator to justify the decision that shrinks it is how a floor stops being a floor.
No transaction fails as a result. Transaction validity is deterministic: check_tx::validate_message is a pure function and the single source of truth, called from the mempool and again from consensus. Every honest node reaches the same verdict. So a lone divergence commits the block normally and touches nothing. There is no legitimate outcome called "consensus could not agree about this transaction". Disagreement is a fault report about a machine, never a verdict about a trade.
Continuing on the majority rests on faults being uncorrelated. Independent hardware does not coordinate, so nine machines failing identically is far less likely than one.
That argument fails precisely in the situation this architecture creates. ACL requires single-revision: every node runs one signed, digest-pinned build. A defect reached on some input is therefore reached by all of them. The nine are not nine independent witnesses. They are one program run nine times, and the ejected node, on different hardware with a different memory layout, may be the only one that noticed.
In a single-revision fleet, a correlated software fault is a more probable cause of divergence than an uncorrelated hardware one. This is not the slim tail one builds for and moves on.
As-built acl_dispute::pattern reads the history of divergences, and the distribution separates the two causes.
Without that gate, ejection selects for agreement. Every ejection is locally justified, and repeating it prunes the fleet down to the nodes that agreed with each other, which under a correlated fault is exactly the wrong survivors. The gate is wired into the consensus engine so an ejection cannot be emitted without it, and the test proving this was verified to fail when the gate is disabled.
Designed only The verdict comes from clean-room replay: provision hosts that were not in the incident, load the agreed parent state, execute the single disputed block, compare roots. Bisection (acl-dispute, O(log n) rounds) reduces the search from a history to one block and one input state first.
This is the step that answers the question a majority vote gets backwards. If the clean room sides with the ejected node, the committed state is wrong, the window since that height is rolled back, and the affected transactions return to a Held state, never silently to settled and never to void.
As-built jil-abci/src/evidence_anchor.rs.
The sealing authority signs every escalated finding with a hybrid Ed25519 + ML-DSA-65 seal over a SHA-256 hash chain, and reports a JIL L1 anchor alongside it.
Until this module existed, the reported anchor was a deterministic derivation of the anchor hash. Reproducible by a third party, and never actually submitted to or confirmed by JIL L1. A number that looked like a chain commitment and was not one.
It is worth being precise about why that is the most serious class of defect in a system like this. Every other failure in this document degrades a capability. This one manufactures evidence: it produces an artifact a court could be shown, which asserts a property nothing established. The system's own source now documents the gap in two places, and the derived-anchor code paths were removed rather than deprecated.
The module is the real thing. Pinning an anchor here means it is committed inside an actual transaction, in an actual block, under the same agreement rule as everything else. One reserved system account holds every pinned hash in a domain-separated storage slot, mirroring federation.rs and currency.rs: three subsystems, one storage discipline, so there is one thing to audit rather than three.
The estate-wide rule that follows is stated as an operating instruction rather than a lesson: never report a derived value in a field a reader will take as a confirmation. If the anchor is not on-chain, the field says so.
As-built 2026-08-27, two hosts. Hardening pass and re-verification 2026-08-28; the density figures are unchanged by it, and the Ring-0 gate and the 47-suite workspace were re-run from a clean checkout of main after the last merge.
| Cells | Host | Cores | Disk | blk/s per cell | MiB/cell | Cells/core |
|---|---|---|---|---|---|---|
| 600 | sales | 96 | RAID1 | 0.97 | 35.1 | 6.3 |
| 1,000 | jil-gpu | 48 | NVMe | 0.85 | 35.8 | 20.8 |
| 1,074 | jil-gpu | 48 | NVMe | ceiling | — | 22.4 |
| 2,061 | sales | 96 | RAID1 | 0.63 | 47.1 | 21.5 |
The ceiling was derived on 48 cores and used to predict the 96-core host at ~2,150. It saturated at 2,061: within 4%, on different silicon, core count, and storage. A rule confirmed by prediction on hardware it was not fitted to.
Disk decides degradation, not the ceiling. Both hosts saturate near 21 per core, but every cell fsyncs every block: NVMe holds 0.85 blk/s at 6.7% iowait, RAID1 falls to 0.63 at 25.1%.
RAM never bound. At the ceiling, 195 GiB of 251 remained free.
At saturation the chains keep committing, roughly 15% slow on NVMe. What stops is the container runtime accepting new starts. A host at its ceiling looks healthy to the chain and broken to the operator. Capacity alarms must watch container-start latency, not block rate.
Load average was actively misleading: 877 on 48 cores while block production was 15% off target. With ~2,000 processes waking each second, load counts sleepers.
The cells were single-validator with zero transactions on young chains, which is the cheapest possible cell. A production cell runs the seven-validator ACL floor, real signature verification, and corridor traffic. A mature cell measured 134.7 MiB against 35 MiB idle.
And the whole measurement was taken on the CometBFT-based stack. ACL replaces that engine, so none of these figures survive the cutover. They are a floor on cost, never a capacity promise.
Three habits did most of the work, and each caught something.
Guard the guard. Every safety test was checked against its own failure mode. The meta-blob format test passed in its first form even with the format broken: the reader tries six backward-compatible tuple shapes and the fields being asserted lead all of them, so the assertions could never fail. It was rewritten against a fixture with a populated tail and verified to fail when the fixture is damaged. The pattern-gate test likewise passed vacuously at first, because votes after the first height are ignored without a proposal, so it asserted nothing.
Attribute before blaming. Every defect found was checked against the commit preceding this work before being reported as pre-existing. Two were.
Prefer refusal to invention. Where a fix required credentials nobody had decided the custody of, the work stopped and documented rather than generating them.
Every cell logged failed to index FinalizeBlock events: event type and attribute key "block.height" is reserved on every block since genesis. Three event types collided with CometBFT-reserved composite keys, so the indexer rejected the entire event set for every block, and height- and hash-based search silently did not exist. Consensus looked healthy throughout.
Found by reading logs during a load test, not by monitoring. Fixed, with a reserved-key detector and a test proving the detector catches the original shape.
The Ring-0 gate, the mechanism that makes the §5 argument true, had been failing on main since at least 2026-08-26, and CI reported it on every run. Six violations in library code. The design document meanwhile stated that all Ring-0 crates passed.
This is the shape the project's own invariant register was written against: the check exists, it runs, and nothing acts on the result.
cell-up.sh exits 0, prints a verify-finality instruction, and leaves a validator crash-looping. Its genesis does not pubkey-pin the seven system accounts, and the assertion rejecting it was added 2026-07-05 as a pentest remediation.
The running cells predate the assertion, and their genesis files are exactly the shape it rejects. So the remediation exists in the repository and not in production, and the gap is invisible from either side: the repo looks fixed and the fleet looks healthy.
Deliberately not fixed here. Those seven are authority credentials; jil1gov controls upgrades and jil1currency controls mint. Generating them in a build script would create the most privileged keys in the system with no custody story.
Every defect above is something built and left one wire short, where it reads as done:
acl-dispute bisection: built, tested, and reachable from nothing.acl-dispute::pattern: built the same morning, wired to nothing until §10.4.parallel-execution: compiles now, and still silently no-ops governance transactions.A capability that appears in a list stops anyone asking whether it works. The check that catches all six is uniform: for each safety mechanism, name the test or alarm that fails when it is disconnected. Where there is none, the mechanism is decoration.
A second pass on 2026-08-28 closed eleven more findings, and they refine §11.4 rather than repeating it. “Built and left one wire short” is one shape of something more general: a safety property asserted somewhere a reader will believe, and enforced nowhere. The wiring gap is the version where the code exists. There are two others.
Two separate safety arguments in acl-app justified an overflow being unreachable by appealing to governed parameter bounds. check_tx reads “the fee product is not reachable: gov::GovParam::bounds() caps MinGasPrice at 1e9”. types reasons from a fixed block reward.
GovParam::bounds() did not exist in ACL. It lives in the pre-port original and was never carried across; the ported gov module has no parameter validation of any kind, so every governed parameter was unbounded and both arguments cited a function that was not in the tree. A proposal could have set a gas price no transaction could pay, or a block reward that overflows staking immediately.
A disconnected mechanism can be found by looking for its callers. A cited absence is only found by trying to follow the citation, and citations are exactly what a reviewer treats as already checked.
Four tests were named vacuous by the audit. Three genuinely were.
header_sign_bytes had no golden vector. Flipping the integer fields from big-endian to little-endian left every existing assertion passing, because they check framing and field distinctness and never the byte order of the two integers. Endianness is invisible in review, identical on a single host, and diverges only when two nodes on different builds sign the same header, which is a consensus split discovered in production.u64::MAX * u128::MAX overflows. True by a factor of 1.8e19, and therefore blind to an off-by-one in a guard.The fourth needed no test. The property it should have asserted was already pinned elsewhere, and writing a second copy would have made the suite look stronger while proving nothing new, which is the same failure one level up.
A third variant, structurally distinct from both. MAX_VALIDATORS was declared three times: 256 in the IBC light client, 1024 in the roster, 1024 again in the request codec. The three interlock. A cell builds a roster under one bound, names those validators in outcomes under the second, and a counterparty tracks that same set under the third, so a roster of three hundred was legal to build, legal to name, and impossible for any other cell to track. Every corridor to that cell would have frozen at the moment of a routine validator-set expansion. The ML-DSA public key length was declared twice, once derived from FIPS-204 and once typed by hand.
Neither had drifted. Both agreed by having been typed correctly, once, by someone who happened to know. That is not a property; it is luck with a maintenance schedule.
§13's first habit was guard the guard. The second pass made it the acceptance criterion rather than a habit: for every fix, break the thing the fix protects, and confirm that only the new test fails.
That last clause carries the weight. Confirming the new test fails proves it is connected. Confirming the pre-existing tests still pass proves the gap was real and had been invisible, which is the claim being made about the codebase and is otherwise unfalsifiable.
| Defect introduced | What noticed |
|---|---|
to_be_bytes to to_le_bytes in the header | only the new golden vector; the old framing test passed |
>= for > in a hand-rolled fee guard | only the boundary pair; both original fee tests passed |
| two admission checks reordered | only the precedence ladder |
| an empty recipient on a release | only the new test; the other 45 federation tests passed |
| the ML-DSA length drifted to 1920 | nothing in acl-app, which built with zero errors; only the cross-crate pin |
| the roster bound re-forked to 1024 | nothing in acl-roster, which built with zero errors; only the pin |
| a new rejection variant with a copied wire code | the golden table, and an unpinned variant now fails to compile |
Two of those rows are the argument in miniature. acl-app builds cleanly with a wrong cryptographic constant, and acl-roster builds cleanly with a bound that would freeze every corridor to the cell. Nothing local can catch either, because neither crate contains the other half of the fact.
The strongest guards found were the ones that are not tests. An exhaustive match over a rejection enum makes an unpinned wire code a compile error rather than a test failure. It cannot be skipped, ignored, or left red in CI, which is precisely how §11.2's gate went unread for days.
The residual-criticals tracker carried nine findings against vm_bridge, all reading OPEN, including one recording that a native deploy credited msg.value with no debit. Money creation. The module had been deleted months earlier by the generality removal in §7: the files are gone, the message kinds are retired, and there is nothing left to be wrong.
The register had been maintained for the code that remained and not for the code that left. Read by counsel or an external auditor, it asserted nine live problems including an inflation bug.
A register that erases findings once their code goes cannot be checked against its own history, and the resolution removed rather than repaired is itself the evidence that §7 did what it claimed. But it is the same failure as the others, pointed at the paperwork instead of the system: a document reporting a state that nothing re-derives from the tree it describes.
Stated as an adversary would state them.
The consensus engine is self-written and unaudited. This is the highest-risk software a company can undertake and will be a diligence question at every bank forever. Partial mitigation: the pure core is ~3,100 lines with a safety argument thirty years old, small enough for an external auditor to actually read. That is mitigation, not an answer.
There are zero external verifiers. The verification plane is the entire correctness argument, and it does not exist. Until at least one non-customer verifier is contracted and running, the story is "we are careful", not "independent parties check us". A design that halts loudly and is never independently checked has replaced one unfalsifiable assertion with another.
Reproducible builds do not exist, and this now gates more than an audit. Triage step one distinguishes a differing build from a failing machine by comparing digests. Without reproducibility, a rolling upgrade produces exactly the one-against-many signature ejection is built for, and the fleet can eject every node that has not yet crossed.
The chain has never carried load. jil-mainnet-1 has produced 3.78 million blocks and committed zero transactions. Every claim about behaviour describes an idle system.
Eject-and-continue does not run. It is real in Ring 0 and fails closed in the shell. The throughput argument that motivated the change is not yet delivered.
The generality removal may be wrong. If cell operators need logic JIL has not anticipated, the constraint on adoption becomes JIL's engineering throughput, and reversing means reintroducing a VM into a chain whose security argument is that it has none.
ACL is deployed nowhere. Zero acl-node containers across the estate. The running system is jil-abci driven by CometBFT 0.38.17.
They should not, yet. The claim is not that it is correct; it is that it is small enough to check (~3,100 lines in the pure core), that its purity is a measurable property of the compiled wasm artifact rather than a promise, and that it halts rather than guessing. External audit is named as a precondition, not a follow-up.
Because the product is attribution and replay, not decentralisation. A database can hold the same rows; it cannot let a party who distrusts the operator re-execute the history and get the same answer, nor prove which named institution agreed at a height fifteen years ago. That is what is being sold, and it is stated without borrowing the vocabulary of a model this system does not satisfy.
It differs in three ways that matter. The dissent is recorded permanently and attributably rather than discarded. The verdict comes from clean-room replay of one block, which establishes who was correct rather than who was numerous. And the distribution over time gates the decision, so a fleet-wide pattern halts instead of ejecting. A majority vote has none of the three. What is conceded is that the chain may build on state a later verdict overturns, which is why the rollback window is treated as a safety parameter.
Correct, and it is stated in the specification rather than discovered by a customer. They are a floor: single validator against a production floor of seven, zero transactions, no corridor traffic, young chains, and on a consensus engine ACL replaces. Anything quoted from them without those qualifications is being misused.
This is the sharpest question and the design answers it explicitly. Majority agreement is weak evidence precisely because single-revision homogeneity means a software defect is shared by all of them. So the majority is treated as evidence only while an independent replay computes the verdict, the verdict can overturn it, and a divergence pattern spread across the fleet stops the chain rather than letting it eject its way down to the nodes that share the bug.
That the removals were safe, from state read off five live chains. That the density rule predicts a machine it was not fitted to, within 4%. That the purity gate rejects real violations, including two in this work. And that four defects existed which nothing else had surfaced. What has not been proven is behaviour under load, and that is listed first in future work.
Three reasons a regulator raises in the first meeting. A row in a German ledger is not made German by a column saying so, and data residency is a chain-level property. One failing validator on a single global chain stops settlement for every jurisdiction, whereas a fault in a cell scopes to that cell and corridors into it hold. And two regulators can both be right about what a legitimate transfer is; one chain forces one answer, cells let each enforce its own at its own border. The cost is real: many chains to operate, and §12 measures what one host can carry.
It is the binding constraint, and it is chosen. A verdict that consults a live service is not replayable: two nodes can reach different answers because the service was down, and a stranger re-executing the block years later cannot re-derive the decision at all. So everything ATCE reads must already be on-chain and agreed, which means external facts enter as attested inputs rather than as calls. That genuinely limits what policy can express, and the exchange is that a compliance decision becomes evidence rather than an assertion.
As a Currency Object: a native managed instrument with registry pin, mint / circulate / redeem state machines, and the same ATCE gate per transfer. Two things follow that a contract cannot give you. Solvency is an on-chain-checkable relation, because circulating currency is a native denom and its outstanding amount is the real total supply, so reserve >= outstanding is evaluated against the ledger rather than reported by the issuer. And authority is genesis-pinned to the licensed issuer's key, so JIL operates every validator and still cannot mint. What is lost is the long tail: a novel instrument is a governed protocol change, not a deployment.
That is the right question to ask, and the honest answer is that the v4 fix addressed the federation authorization specifically, not a systematic audit of every signed structure. The defect class is caller-controlled strings inside a non-length-prefixed encoding, so any signed document assembled by string concatenation is suspect until checked. The fix pattern is settled: length-prefix every field under a domain tag, and bind every value the resulting event asserts. What is not yet done is applying that check to every signed structure in the estate.
A rising, ageing queue of "we cannot express this" requests from cell operators. That is measurable from month one, and it is the leading indicator, not a post-mortem.
The work reported here does not claim a correct consensus engine. It claims something narrower: that an evidentiary ledger is achievable without a decentralisation argument, and that the properties which make it so are attribution, replayability, and treating disagreement as a fact rather than a vote.
The strongest single result is not the design. It is §15.4. Five safety mechanisms were found built and disconnected in one codebase in one day, each reading as complete. If that generalises, then the dominant failure mode in systems of this kind is not building the wrong thing. It is building the right thing and leaving it one wire short, in a place where a list says it is done.
The remedy is uniform and cheap: for every safety mechanism, name the test or alarm that fails when it is disconnected. Where there is none, the mechanism is decoration, and the system's real behaviour is whatever remains after removing it.