An interactive, browser-based visualization of Commonware's QMDB — the authenticated state database powered by a Merkle Mountain Range. Watch operations append in real time, generate cryptographic inclusion proofs, and tamper with hashes to see the proof break.
Every byte is hashed with real SHA-256 in your browser. No mocks, no backend, no fake hashes.
Built as a learning aid for the Commonware blog post on MMRs. If you've ever wondered how an append-only authenticated log actually grows, or why you can't forge an inclusion proof — this shows you, click by click.
Replace this image with your own screenshot — see the Screenshots section below.
A single-page React app that implements the canonical MMR algorithm exactly as Commonware describes it:
- Append-only: every operation becomes a new leaf at the next position. No existing node is ever modified.
- Mountain merging: when the last two peaks have equal height, they merge into a parent. Repeat until heights are strictly decreasing.
- Root commitment: the root hash is
SHA-256( totalNodes (u64 LE) ‖ peak[0] ‖ peak[1] ‖ … ), peaks ordered largest mountain to smallest. - Inclusion proofs: a proof for any historical leaf is the sibling hashes from leaf to peak, plus all other peak hashes — logarithmic in the log size.
The visualizer lets you do all of this interactively and watch what happens.
- Live MMR construction — assign or delete keys and watch the tree grow with smooth D3 transitions.
- Real cryptography —
crypto.subtle.digest('SHA-256', …)via Web Crypto API. The leaf hash format isSHA-256( location(u64 LE) ‖ key ‖ value ), the parent hash isSHA-256( left ‖ right ). No shortcuts. - Auto-seed mode — toggle on to append a random key/value every 1.5s; great for watching mountains form.
- History scrubber — every operation snapshots the full state (deep-cloned). Drag the slider to replay the MMR at any point in time.
- One-click proof generation — click any operation in the log and the inclusion proof lights up:
- leaf-to-peak path in yellow
- sibling hashes in orange
- other peaks in blue
- Tamper Mode — every hash in the proof becomes editable hex. Change one character; the recomputed root instantly diverges from the MMR root and the proof flips to INVALID. The cryptographic guarantee, made tangible.
- Educational tooltips on every key concept (MMR, root hash, proof path, tamper mode).
- 100% client-side — no backend, no API calls, works offline once loaded.
| Layer | Choice |
|---|---|
| UI | React 18 + TypeScript |
| Build | Vite 5 |
| Styling | Tailwind CSS (via PostCSS) |
| Tree rendering | D3.js v7 (React-controlled SVG, D3 owns node/edge updates) |
| Hashing | Web Crypto API (SubtleCrypto.digest) — no third-party crypto libraries |
| Bundle | ~205 KB JS / ~14 KB CSS (~67 KB gzipped total) |
git clone https://github.com/<your-username>/qmdb-visualizer.git
cd qmdb-visualizer
npm install
npm run devOpen http://localhost:5173 and start clicking.
npm run build # outputs to dist/
npm run preview # serve dist/ locally to verifyThe output is a static bundle — drop dist/ on Vercel, Netlify, GitHub Pages, Cloudflare Pages, or any static host.
Given an existing MMR with nodes, peaks, and totalNodes, appending an operation (key, value) works like this:
const location = operationLog.length;
const leafPos = nodes.length;
const leafHash = SHA-256( u64_le(location) || key || value );
nodes.push({ position: leafPos, hash: leafHash, height: 0 });
peaks.push(leafPos);
while (peaks.length >= 2) {
const right = nodes[peaks[peaks.length - 1]];
const left = nodes[peaks[peaks.length - 2]];
if (left.height !== right.height) break;
const parentHash = SHA-256( left.hash || right.hash );
const parentPos = nodes.length;
nodes.push({ position: parentPos, hash: parentHash, height: left.height + 1, leftChild: left.position, rightChild: right.position });
peaks.pop(); peaks.pop();
peaks.push(parentPos);
}
const root = SHA-256( u64_le(totalNodes) || peaks.map(p => nodes[p].hash).join() );Each leaf gets a globally unique position (assigned in insertion order, including internal nodes). Peaks are stored in largest-first order, which is also the order they get bagged into the root.
For any leaf at position P:
- Walk up via parent pointers; at each step record the sibling's hash.
- Identify which peak the leaf belongs to.
- Return:
siblings[](with direction flags),otherPeaks[](in canonical order), andpeakIndex(where the leaf's peak sits among all peaks).
- Reconstruct the leaf's peak by hashing
leafwith each sibling at each level (respecting the left/right direction). - Insert the reconstructed peak into the
peakIndexslot, fill the rest fromotherPeaks. - Bag with
totalNodesand compare against the expected root.
This is exactly what Tamper Mode re-runs on every keystroke.
See src/mmr/MMR.ts and src/mmr/proof.ts for the actual implementation — it's about 200 LOC total.
qmdb-visualizer/
├── index.html
├── package.json
├── vite.config.ts
├── tailwind.config.js
└── src/
├── main.tsx
├── App.tsx # state orchestration, snapshot history, auto-seed
├── mmr/
│ ├── types.ts # MMRNode, MMRState, InclusionProof
│ ├── MMR.ts # hashing, append, snapshot cloning
│ └── proof.ts # generation + verification
├── components/
│ ├── Controls.tsx # input row + history slider
│ ├── LogPanel.tsx # operation log (left)
│ ├── MMRTree.tsx # D3 SVG tree (center)
│ └── ProofPanel.tsx # proof inspector + tamper mode (right)
└── styles/
└── globals.css
Place your own screenshots in docs/ and reference them here. Suggested shots:
docs/screenshot-tree.png— a 7–10 op MMR with multiple mountains visibledocs/screenshot-proof.png— a proof selected, yellow/orange/blue highlight visibledocs/screenshot-tamper.png— Tamper Mode on, INVALID badge red


- Commonware — for QMDB and the excellent blog post that this visualizer is based on.
- Patrick O'Grady — for the writing that made the algorithm click.
- Exoware
This project is independent and not affiliated with Commonware. It's a community-built educational tool.
Issues and PRs welcome. Some ideas:
- Performance proofs / batch proofs
- Range proofs over a contiguous slice of leaves
- A "verifier-only" mode that takes a JSON proof and a root and verifies offline
- Export / import operation logs as JSON
- Streaming append from a remote source
