Audit: P0.8.1 η Merkle Tree Construction
Task: P0.8.1 — first η (Proof Store) surface in Colibri
Branch: feature/p0-8-1-merkle-tree
Date: 2026-04-17
Auditor: T3 Executor (Claude Sonnet 4.6)
1. Surface inventory
1.1 η surface state (before this task)
No src/domains/proof/ directory exists. No prior η code anywhere in the repo.
Confirmed via ls src/domains/:
skills/
tasks/
trail/
src/domains/proof/ — does not exist (target of this task).
1.2 Existing dep scan — merkletreejs absent
package.json dependencies (current):
{
"@modelcontextprotocol/sdk": "^1.0.4",
"better-sqlite3": "^11.5.0",
"dotenv": "^16.4.7",
"gray-matter": "^4.0.3",
"zod": "^3.23.8"
}
merkletreejs — not present. Must be installed as a new runtime dependency.
@types/merkletreejs — not present. Need to verify after install if merkletreejs ships its own .d.ts.
1.3 Related prior art in ζ (Decision Trail — P0.7.1 + P0.7.2)
The ζ surface provides the hash-chain primitives that η will anchor:
| File | Relevant exports |
|---|---|
src/domains/trail/schema.ts |
computeHash(record) — SHA-256 over canonical-JSON; ZERO_HASH (64 '0's); canonicalize(value) |
src/domains/trail/repository.ts |
createThoughtRecord(...) — CRUD that produces hash values; future η consumer |
The η Merkle layer takes ζ hash values (64-char lowercase hex) as leaves and builds
a Merkle tree over them. No ζ source code modification is needed.
1.4 Crypto primitive alignment
ζ uses node:crypto createHash('sha256').update(canonical, 'utf8').digest('hex') →
64-char lowercase hex string.
η will use node:crypto createHash('sha256').update(data).digest() → Buffer, passed
as the hash function to MerkleTree(leaves, hashFn, opts). Same primitive, same output.
Using Buffer internally is idiomatic for merkletreejs; converting to/from hex for the
public API aligns with ζ’s string-based convention.
1.5 Test infrastructure state
src/__tests__/domains/trail/repository.test.ts exists and uses:
import Database from 'better-sqlite3'— in-memory SQLiteimport { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'— MCP transport- Standard Jest ESM patterns with
node --experimental-vm-modules
No prior η test directory. Target: src/__tests__/domains/proof/merkle.test.ts.
No MCP tool registration in this task (that is P0.8.3). Tests are pure unit tests.
1.6 Existing audit/contract/packet docs for η
None — confirmed via ls docs/audits/ | grep p0-8:
(no output)
This document is the first η artifact.
2. Dependency risk assessment
| Concern | Assessment |
|---|---|
merkletreejs types |
Package ships its own index.d.ts (TypeScript-first library). No @types/merkletreejs needed. Verify post-install. |
merkletreejs ESM |
Library ships CJS. Works fine in ESM TypeScript project via import { MerkleTree } from 'merkletreejs' with TS esModuleInterop: true. Check tsconfig. |
| Buffer usage in tests | Buffer.from(h, 'hex') is standard Node.js. No polyfill needed. |
sortPairs option |
Makes the tree canonical (leaf order independent). Critical for determinism. Must be set. |
| Empty tree edge case | new MerkleTree([], sha256Fn).getRoot() returns empty Buffer. Must special-case. |
3. tsconfig check
tsconfig.json must have "esModuleInterop": true for CJS default imports.
Will verify during implementation — expected to be present from Wave A.
4. Files to create
| File | Purpose | New? |
|---|---|---|
src/domains/proof/merkle.ts |
η Merkle primitives | Yes — first file in this dir |
src/__tests__/domains/proof/merkle.test.ts |
Unit tests | Yes |
package.json |
Add merkletreejs dep |
Modify |
package-lock.json |
Lock file update | Modify |
docs/audits/p0-8-1-merkle-tree-audit.md |
This file | Yes |
docs/contracts/p0-8-1-merkle-tree-contract.md |
Step 2 output | Yes |
docs/packets/p0-8-1-merkle-tree-packet.md |
Step 3 output | Yes |
docs/verification/p0-8-1-merkle-tree-verification.md |
Step 5 output | Yes |
5. Integration points (consumed by future tasks)
| Future task | Dependency on this task |
|---|---|
| P0.8.2 η retention policy | Will call buildMerkleTree(hashes) on a set of thought-record hashes |
P0.8.3 merkle_finalize MCP tool |
Will call all three exports; builds the root committed to a session |
P0.7.3 audit_verify_chain |
Verifies ζ hash chain; η anchors the verified chain root |
6. Audit conclusion
Blockers: None.
New dep required: merkletreejs (runtime). Install via npm install merkletreejs --save.
No breaking changes to existing surfaces.
Proceed to Step 2 (contract).