Compatibility with Tessera
webtessera is a translation of Tessera at 4a6d9f9, and a log
it writes is byte for byte the log Tessera writes for the same entries. 22 golden fixture files,
the golden suite on eight storage backends, Go interop and 86,008
differential records check that claim on every change. Each number below is counted from the repository while this
page is built.
Golden fixtures
fixtures/data holds 22 files recorded by
running Tessera at 4a6d9f9: paths, tiles, entry bundles, proofs,
notes and checkpoints, and complete logs of 0, 1, 2, 255, 256, 257, 1,000 and 5,000 entries written by its POSIX driver,
.state files included. The tests compare webtessera’s output with them byte for byte. CI regenerates them from Tessera on every change and fails if any byte differs.
The golden suite
Every storage backend, in every runtime it supports, must store exactly the files Tessera’s POSIX driver stores for the fixture logs: in one batch, in batches, across restarts, and when it carries on a log Go wrote.
| Backend | Node.js | Chromium | workerd | live server |
|---|---|---|---|---|
| memory | yes | yes | ||
| IndexedDB | yes | yes | ||
| node:sqlite | yes | |||
| libSQL | yes | |||
| rqlite | yes | yes | ||
| sqlite-wasm | yes | |||
| Cloudflare D1 | yes | |||
| Durable Object SQL | yes | |||
| WebCrypto keys | yes | yes | yes |
The last row replays Go’s signed notes and checkpoints through non-extractable WebCrypto keys. Ed25519 is deterministic, so they must match to the byte.
Go interop
In CI, Tessera’s own Go code verifies, reproduces byte for byte and carries on logs that
webtessera wrote, and webtessera does the same with logs Go wrote, on four backends:
memory, indexeddb (fake-indexeddb), sqlite (node:sqlite) and sqlite (libSQL). The harness is interop/, driven by
scripts/interop.mjs.
Differential corpora
Go’s verdict, exact error text and output on 86,008 generated inputs, most of them malformed, and on every Unicode code point, in 19 corpora replayed in Node.js, Chromium and workerd. Every difference from Go is a named divergence with a decision record, or a failure.
| Corpus | What Go recorded | Records |
|---|---|---|
differential_api | api.HashTile.UnmarshalText and api.EntryBundle.UnmarshalText | 108 |
differential_bundle_hashers | the entry-bundle hashers | 400 |
differential_checkpoint | formats/log | 8,460 |
differential_checkpoint_publisher | AppendOptions.CheckpointPublisher without witnesses | 414 |
differential_compact | merkle/compact | 6,061 |
differential_cosig | formats/note cosignature/v1 | 3,636 |
differential_ct_log | A static-ct log of 300 seeded CT entries | 308 |
differential_ed25519 | Ed25519 verification as Go's crypto/ed25519.Verify performs it | 3,420 |
differential_gostd | the Go standard library behaviour src/internal/gostd stands in for | 28,260 |
differential_layout | api/layout | 8,664 |
differential_note_keys | encoded note keys | 5,260 |
differential_note_open | sumdb/note Open | 1,654 |
differential_note_sign | sumdb/note Sign | 1,508 |
differential_proof_consistency | merkle/proof VerifyConsistency | 4,681 |
differential_proof_inclusion | merkle/proof VerifyInclusion | 4,835 |
differential_proof_nodes | merkle/proof Inclusion and Consistency | 3,023 |
differential_rfc6962 | merkle/rfc6962 | 436 |
differential_unicode | unicode/utf8.Valid | 4,005 |
differential_witness_policy | NewWitnessGroupFromPolicy | 875 |
Test parity
Every Go test, example, fuzz target and benchmark of Tessera and of the modules it depends on must have a
passing TypeScript test of the same name, or an entry in
scripts/test-parity-allowlist.json that
cites the decision record leaving it out: 20 entries today, citing
six records. CI fails on any other.
What is compared
| Artefact | Fixtures and golden suite | Interop harness |
|---|---|---|
| Tiles (full and right-edge partial) | byte-identical | byte-identical, and re-derived by Go’s fsck |
| Superseded partial tiles and bundles | byte-identical (the prefix POSIX wrote) | byte-identical |
| Entry bundles | byte-identical | byte-identical; every entry checked against the corpus |
| Signed checkpoint | byte-identical | byte-identical at every batch, signature verified by Go |
.state/treeState, .state/version |
byte-identical to Go’s | byte-identical; each side resumes the other’s |
.state/gcState |
not compared (GC is off in the fixtures; json_test.ts pins its encoding) |
not compared (GC off) |
| Set of paths | exact | exact |
Porting status
Each source file mirrors the upstream file of the same name, with its comments carried over.
docs/PORTING-MAP.md tracks every Go file:
| Status | Tessera’s files | Its Go dependencies’ files |
|---|---|---|
| Done | 53 | 37 |
| Not ported | 54 | 5 |
Every divergence from Go, however small, and every file not ported has a decision record: 153 records, of which 147 accepted and six superseded.
CI
ci.yml runs these jobs on every pull request and push, and “CI passed” fails if any of them does; a release is published only after the same jobs pass:
- Quality
- Lint, Typecheck, Go (gofmt, vet) and Workflows (actionlint).
- Test
- Unit (Node 22), Unit (Node 24), Browser (Chromium), Workers (workerd) and Services (rqlite, S3).
- Compatibility
- Fixtures are reproducible, Go and TypeScript interoperate and Upstream test parity.
- Package
- Build the tarballs and Check the tarballs.
- Runtimes
- Smoke test (node), Smoke test (bun) and Smoke test (deno).
- Examples
- Examples.
- Site
- Landing page.
Runtimes
webtessera runs on Node.js 22.18 or later, Deno 2, Bun, current browsers, and edge runtimes built on web standards. CI runs the unit tests on Node.js 22 and 24, the browser tests in Chromium, the Workers tests in workerd and the services tests against live rqlite and S3-compatible servers, and smoke-tests the built package on Node.js, Bun and Deno.
Reproduce it
bun install
bun run fixtures && git status --porcelain fixtures/data # nothing printed: the fixtures are Go's
bun run test:unit # includes the golden suite on Node
bun run test:browser # PLAYWRIGHT_CHROMIUM_EXECUTABLE=... to use a local Chromium
bun run test:workers
bun run test:parity # every upstream Go test has a TypeScript counterpart
bun run interop # checks out upstream and builds dist/ itself
bun run interop accepts --seed N, --size1 N --size2 N (for example --size1 3000 --size2 5000 for a quick
run), --backend NAME (repeatable), --keep to keep the logs, and --no-build to reuse an existing dist/. It
needs Go 1.24 or later on PATH and, the first time, access to the Go module proxy.
The Go tools also run on their own, for example against a log exported from a browser:
cd interop
go run ./verify -dir /path/to/log -vkey "$(cat log.vkey)" -history /path/to/checkpoints
go run ./produce -dir /tmp/log -skey "$SKEY" -seed 1 -from 0 -ends 1,256,1000 -history /tmp/log.history
docs/compatibility.md explains each layer of evidence in
full, and how to hold a new backend to it.