Skip to content

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.0 as a project but published each package at 0.0.1; five packages (raijin-consensus, raijin-mempool, raijin-da, raijin-validator, raijin-sdk) then went 0.0.2 (broken tarballs — a workspace:* publish bug) → 0.0.3 the same day, while raijin-core, with no internal dependency to leak, stayed at 0.0.1. The scoped @johnhenry/raijin-* packages restart at 0.0.0 — a new address and era, not a maturity signal. Current releases: raijin-core, raijin-consensus and raijin-validator at 0.2.0, raijin-mempool at 0.1.1, raijin-sdk at 0.1.0, raijin-da at 0.0.4.

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: chainId is 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 zeroed stateRoot/receiptRoot on 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 − f with f = 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; codecTransport only adapts a byte channel you supply to the message interface.

Install only what you need — packages are independent, all depending only on raijin-core:

Terminal window
npm install @johnhenry/raijin-sdk @johnhenry/raijin-validator

Latest on npm: @johnhenry/[email protected], @johnhenry/[email protected].

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.

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.

  • 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 — PBFTConsensus and ValidatorSet, quorum math first, plus codecTransport for 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 the EthBlobDA stub
  • Validator — ValidatorNode, BlockProducer, transaction gossip, genesis, and checkpointed restarts

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), and parentHash is 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 a PersistentStateStore plus a checkpoint.
  • Real wire format: a canonical binary codec (encodeMessage/decodeMessage) and codecTransport for byte channels, instead of every integrator writing a JSON replacer.
  • Signature verifier argument order is WebCrypto’s (publicKey, signature, message) everywhere — a breaking change in raijin-core 0.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