raijin
@johnhenry/raijin-* is a browser-native mesh rollup framework: six small TypeScript packages that compose into an L2 where the users visiting your web app are the validators. A deterministic state machine, a simplified PBFT consensus engine with leader rotation, a fee-ordered mempool, a pluggable data-availability layer, a validator composition root, and a client SDK — all transport-agnostic, storage-agnostic, and identity-agnostic (you inject signing, storage, and networking; the framework never touches WebRTC itself, and its one IndexedDB backend is an opt-in KVBackend you hand it). Zero runtime dependencies; runs in browsers and Node.
Previously published unscoped, with a quirky version history: the initial release (2026-03) was tagged
v0.1.0as a project but published each package at0.0.1; five packages (raijin-consensus,raijin-mempool,raijin-da,raijin-validator,raijin-sdk) then went0.0.2(broken tarballs — aworkspace:*publish bug) →0.0.3the same day, whileraijin-core, with no internal dependency to leak, stayed at0.0.1. The scoped@johnhenry/raijin-*packages restart at0.0.0— a new address and era, not a maturity signal. Current releases:raijin-core,raijin-consensusandraijin-validatorat0.2.0,raijin-mempoolat0.1.1,raijin-sdkat0.1.0,raijin-daat0.0.4.
What this is not
Section titled “What this is not”Read this before anything else, because the name “rollup” over-promises:
- Not a production blockchain. This is experimental consensus research. Nothing here is audited, and load-bearing shortcuts remain:
chainIdis carried in every signed transaction but not enforced by the state machine itself. (Two shortcuts that used to be documented here — unverified commit signatures, and zeroedstateRoot/receiptRooton the finalized header — have since been fixed; see Consensus. The pre-execution proposal a leader broadcasts still carries zeroed roots, since neither can be known before the block runs — only the post-execution header nodes actually chain to has the real values.) - Not Byzantine-fault-tolerant below 4 validators. Quorum is
quorumSize() = n − fwithf = floor((n-1)/3)— below n = 4 there’s no fault tolerance, though (unlike an earlier version of this page) quorum at n = 2 or 3 now requires every validator, not a single one. See Consensus. - Not a settlement layer. There’s no bridge, no fraud/validity proofs, no sequencer escape hatch. The DA layer posts bytes and verifies content hashes — it does not prove inclusion.
- Not batteries-included networking. There is no transport in the box. Every consensus and gossip interface takes an object you implement over WebRTC, WebSockets, or anything else;
codecTransportonly adapts a byte channel you supply to the message interface.
Install
Section titled “Install”Install only what you need — packages are independent, all depending only on raijin-core:
npm install @johnhenry/raijin-sdk @johnhenry/raijin-validator<!-- In <head>, before any <script type="module"> or modulepreload. --><script type="importmap"> { "imports": { "@johnhenry/raijin-core": "https://cdn.jsdelivr.net/npm/@johnhenry/[email protected]/dist/index.js", "@johnhenry/raijin-sdk": "https://cdn.jsdelivr.net/npm/@johnhenry/[email protected]/dist/index.js", "@johnhenry/raijin-consensus": "https://cdn.jsdelivr.net/npm/@johnhenry/[email protected]/dist/index.js", "@johnhenry/raijin-mempool": "https://cdn.jsdelivr.net/npm/@johnhenry/[email protected]/dist/index.js", "@johnhenry/raijin-validator": "https://cdn.jsdelivr.net/npm/@johnhenry/[email protected]/dist/index.js" } }</script><script type="module"> import * as raijinSdk from "@johnhenry/raijin-sdk"; import * as raijinValidator from "@johnhenry/raijin-validator";</script>Quick start
Section titled “Quick start”A one-validator chain in one process (quorum of 1 finalizes instantly):
import { InMemoryStateStore } from '@johnhenry/raijin-core'import { ValidatorNode } from '@johnhenry/raijin-validator'
const node = new ValidatorNode({ chainId: 1n, // required — no default identity: { publicKey, sign, verify }, // your keys — see Getting started transport: { broadcast() {}, send() {}, onMessage() {} }, timer: { set: (ms, cb) => setTimeout(cb, ms), clear: clearTimeout }, store: new InMemoryStateStore(), validators: [publicKey], // must include self})node.onBlockFinalized((block, receipts) => console.log(block.header.number, receipts))node.start()await node.submitTransaction(signedTx)The runnable version, with real Ed25519 keys and genesis funding, is Getting started.
Which package do I want?
Section titled “Which package do I want?”| You want to… | Package | Docs |
|---|---|---|
| Build/sign transactions, query a chain | @johnhenry/raijin-sdk |
Getting started |
| Run a validator (the usual entry point) | @johnhenry/raijin-validator |
Validator |
| Use the state machine, hashing, or types directly | @johnhenry/raijin-core |
Core |
| Drive PBFT yourself over your own transport | @johnhenry/raijin-consensus |
Consensus |
| Fee-ordered transaction pooling (the pool a validator runs) | @johnhenry/raijin-mempool |
Mempool |
| Post block data to a DA layer (local/Celestia) | @johnhenry/raijin-da |
Data availability |
A seventh workspace package, raijin-test-harness, is internal multi-node test infrastructure and is deliberately unpublished — see Architecture.
The pages here
Section titled “The pages here”- Architecture — how the six packages compose, the block lifecycle from tx submission through the PBFT phases to state commitment, and where DA actually sits (spoiler: not yet in the hot path)
- Getting started — the smallest working node, driven through
@johnhenry/raijin-sdk; doubles as the SDK API reference (Wallet,RaijinClient,ClientTransport) - Core — state machine, transaction/block types, hashing, canonical encoding, signatures, the wire codec, durable state, genesis
- Consensus —
PBFTConsensusandValidatorSet, quorum math first, pluscodecTransportfor byte channels - Mempool — fee ordering, eviction rules, and the fee-byte convention that sits right after the tx-type byte
- Data availability —
DALayer, what commitments prove,LocalDA/CelestiaDA, and theEthBlobDAstub - Validator —
ValidatorNode,BlockProducer, transaction gossip, genesis, and checkpointed restarts
Status
Section titled “Status”Experimental consensus research, unaudited, versioned pre-1.0 (raijin-core, -consensus and -validator at 0.2.0, raijin-mempool at 0.1.1, raijin-sdk at 0.1.0, raijin-da at 0.0.4). What’s real: deterministic tests across the six packages, runnable single-node examples smoke-tested in CI, and a multi-node scenario harness (partitions, leader crashes, churn) that runs in-repo.
Things that used to be on the “not implemented” list and are now real, verified against the packages’ own READMEs and type definitions:
- Every PBFT vote is signature-verified — PRE-PREPARE, PREPARE, COMMIT, and VIEW-CHANGE are all checked against a required, injected
verify(there is no unverified mode), scoped to chain/epoch/view/sequence so a signature is never reusable outside the exact round it was cast for. - Block headers carry a real, computed
stateRoot/receiptRoot, filled in by PBFT after the block actually runs (the pre-execution proposal that nodes vote on leaves them zeroed), andparentHashis the block hash of that completed header. - The validator runs the real mempool: fee-ordered, sender+nonce-deduplicated, signature-verified, evicting the lowest fee when full;
submitTransaction()throws on a bad signature, duplicate, or full pool, and accepted transactions are gossiped to the other validators. - Genesis is first-class (
GenesisConfig,genesisHash,ValidatorNode.create/ready(),fetchGenesis), and a restarted validator can resume from aPersistentStateStoreplus acheckpoint. - Real wire format: a canonical binary codec (
encodeMessage/decodeMessage) andcodecTransportfor byte channels, instead of every integrator writing a JSON replacer. - Signature verifier argument order is WebCrypto’s
(publicKey, signature, message)everywhere — a breaking change inraijin-core0.1.0.
What’s still not: full prepared-certificate carry-over on view change (a new leader is blocked from proposing a conflicting block at a sequence that already reached a PREPARE quorum, but what was already prepared is not automatically re-proposed), a transport implementation (WebRTC/WebSocket adapters are the caller’s job — transport and timers are injected, never shipped), a tested real-browser IndexedDB run (IndexedDbKVBackend has been exercised against an in-process IndexedDB double, not a browser), DA in the validator’s block path, and EthBlobDA (still a documented stub that throws with implementation instructions).
Source: github.com/johnhenry/raijin