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"
}

merkletreejsnot present. Must be installed as a new runtime dependency. @types/merkletreejsnot present. Need to verify after install if merkletreejs ships its own .d.ts.

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 SQLite
  • import { 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).


Back to top

Colibri — documentation-first MCP runtime. Apache 2.0 + Commons Clause.

This site uses Just the Docs, a documentation theme for Jekyll.