Junction Rule
Every credit needs its debit
A mint or release must match a finalized burn or lock with the same message id, amount and recipient. A credit with no debit is forged, caught on the first transaction.
KIRCHHOFF checks if the money adds up. Conserved signs. Broken never moves.
$292M
created from nothing. The bridge checked the signature. Nobody checked the sum
Kelp DAO, April 18 2026 · Crypto TimesBorrowed from Kirchhoff's circuit laws. Chains are nodes, bridges are wires, money is current. When the current stops adding up, the circuit opens.
A bridge asks
enough to stop a forged credit
KIRCHHOFF asks
no, or no signature
Every credit needs its debit
A mint or release must match a finalized burn or lock with the same message id, amount and recipient. A credit with no debit is forged, caught on the first transaction.
Backing covers every claim
Each epoch, the Conservation Engine reads every chain at pinned blocks with DON consensus. Escrow must cover remote supply plus everything in flight, whichever path minted it.
The same read model Mission Control runs on. Every figure opens its onchain read
01
Forge
One verifier key signs a credit. 116,500 kETH leaves the escrow
02
Detect
W1 searches every remote chain for the burn. None exists
03
Break
BROKEN lands on all three ledgers in the same CRE run
04
Contain
CCIP lanes freeze, the attacker is tainted, the feed flips
05
Refuse
The escape transfer to Base gets FAIL TOKEN_BROKEN. It never executes
The forgery is simulated honestly: a WeakBridge message signed with its single verifier key, with no matching burn. It reproduces the effect of the Kelp attack, a credit with no debit, not LayerZero's exact bug.
Issuers can require their own verifier beside the Committee Verifier. Both must sign, so a refusal holds the message Chainlink, Sept 28 2026
CRE Conservation Engine
Four workflows read every chain at pinned blocks with DON consensus and write signed reports to the ledger
CCV committee
Every cell runs the same Judge. FAIL withholds the signature, so the message never executes
Conservation Feed
An AggregatorV3 status feed per chain. Lenders stop borrowing against broken collateral
abstract contract KirchhoffProtected { AggregatorV3Interface public immutable kirchhoffFeed; uint256 public constant MAX_AGE = 300; function _requireConserved() internal view { (, int256 s,, uint256 updatedAt,) = kirchhoffFeed.latestRoundData(); if (block.timestamp - updatedAt > MAX_AGE) revert CollateralStatusStale(block.timestamp - updatedAt); if (s != 1 && s != 2) revert CollateralNotConserved(s); // 1 CONSERVED, 2 DRIFT }}It catches every path that creates value from nothing. It does not catch these