Quickstart

If you need to trust who wrote content and why it was accepted, run Oak Chain locally and follow one signed write. Start from observable behavior first, then read the architecture. The walkthrough below starts a 3-validator cluster, confirms leader and follower roles, submits one signed write, waits for it to commit, and reads it back.

It runs in mock mode: payments are simulated and signatures are only format-checked. It exercises ordering, replication and durability. It does not exercise payment or signature verification.

After the first win, Consensus Model explains the mechanics; the rest of the guide goes deeper from there.

Prerequisites

The Four Checks

  1. Start a 3-validator local cluster
  2. Confirm one node is LEADER and two are FOLLOWER
  3. Submit one signed write and wait until it is COMMITTED
  4. Read it back from the API

If these four checks pass, the local write path works: the cluster orders the write, replicates it, and serves it back.

Start the Cluster

# Create a sibling workspace for the infra and Oak repos
mkdir oak-chain-workspace
cd oak-chain-workspace

# Clone the repositories the workflow expects
git clone https://github.com/somarc/oak-chain-infra.git
git clone https://github.com/somarc/jackrabbit-oak.git

# Install the oak-chain CLI dependencies (used to sign the test write)
cd oak-chain-infra
npm ci

# Use a disposable runtime root; --fresh refuses the default ~/oak-chain
export CLUSTER_RUNTIME_ROOT=/tmp/oak-chain

# Build, then start three validators
./modes/mock/validators/lifecycle/start-cluster.sh --build --fresh

# For faster iteration after the first build (same shell)
./modes/mock/validators/lifecycle/start-cluster.sh

start-cluster.sh is the current public path for a local validator cluster. It starts IPFS if needed, builds jackrabbit-oak (with --build), and starts three validators on HTTP ports 8090, 8092 and 8094. On a fresh runtime root it asks whether to generate a cluster wallet; press Enter to accept.

It also starts a local OPS proxy if oak-chain-edge-worker is cloned next to the other two repositories. Without it, the script prints OPS proxy skipped and ends with a "not fully healthy" warning. You can ignore that warning for this guide.

Stop the cluster with ./modes/mock/validators/lifecycle/stop-cluster.sh, using the same CLUSTER_RUNTIME_ROOT.

Verify It's Working

Check Validator Dashboard

Open http://localhost:8090 in your browser.

You should see:

Check Consensus Status

# Ask each validator for its role and the leader it sees
for p in 8090 8092 8094; do
  curl -s http://localhost:$p/v1/consensus/status \
    | jq -c '{currentRole, currentLeader, reachableValidators}'
done

# Expected: one LEADER, two FOLLOWER, the same currentLeader on all three, e.g.
# {"currentRole":"LEADER","currentLeader":"http://localhost:8090","reachableValidators":3}
# {"currentRole":"FOLLOWER","currentLeader":"http://localhost:8090","reachableValidators":3}
# {"currentRole":"FOLLOWER","currentLeader":"http://localhost:8090","reachableValidators":3}

The full response also includes contractVersion (consensus.status.v1), consensusType, isLeader, currentEpoch, currentTerm, allFollowers and ethereumEpoch. Any of the three ports can be the leader.

Write Some Content

# Still in oak-chain-infra. Find the current leader.
LEADER=$(curl -s http://localhost:8090/v1/consensus/status | jq -r .currentLeader)

# Build a signed test write with public, deterministic test keys
BODY=$(node bin/oak-chain.mjs tx write --message 'Hello, Oak Chain!' --format form)
ID=$(printf '%s' "$BODY" | tr '&' '\n' | sed -n 's/^proposalId=//p')

# Submit it to the leader: expect HTTP 202 and "state": "PENDING"
RESP=$(curl -s -X POST -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-binary "$BODY" "$LEADER/v1/propose-write")
echo "$RESP" | jq '{state, proposalId, wallet, storagePath}'

# Repeat until "state" is "COMMITTED" (usually within seconds)
curl -s "$LEADER/v1/ops/operations/$ID" | jq '{state, durabilityState}'

Send writes to the leader. A follower answers 307 with the leader's URL in the Location header, and the operation record lives only on the validator that accepted the write. FAILED or TIMED_OUT means the write did not land.

Read It Back

WALLET=$(echo "$RESP" | jq -r .wallet)
curl -s "$LEADER/v1/wallets/content?wallet=$WALLET" | jq

# Expected: a "content" list with an entry whose "message" is "Hello, Oak Chain!"

Run the same query against the other two ports. Each validator answers from its own store.

What Just Happened

  1. Your write proposal went to the leader
  2. Leader checked the request and the signature format (mock mode does not verify signatures cryptographically or check payments)
  3. Leader queued the proposal, answered 202 PENDING, and proposed it to the Aeron cluster
  4. Aeron ordered the write in its replicated log, and every validator, the followers included, applied it
  5. Content persisted to each validator's Oak segment store
  6. The write became COMMITTED once a majority of validators had applied it and flushed it to disk
  7. All three validators now hold the same content. Convergence is logical: same nodes, property types and values. Their RecordIds, journal heads and TAR files can differ.

Modes

Data Table
Mode
Purpose
When to Use
MOCK
Default. Payments simulated, signatures format-checked only
First proof, docs walkthrough, local debugging
SEPOLIA
Chain-backed payments on the Sepolia testnet; not yet validated
Payment and contract experiments
MAINNET
Unavailable
The validator refuses mainnet in this version

Next

Developer Guide for the full map of concepts, contract surface, and reference.