Skip to content

Participate

Repositories

In the codeAfter this page you know which repository owns which decision, and how to check that the vendored protocol package is the one it claims to be.

Three public repositories, and hard lines between them. One owns the protocol. The other two are separate implementations that read it and are expected to agree without sharing code.

github.com/bitcoinuniverseio/index-tandem

This repository. It is Tandem indexer pipeline A: Node 24.19.0, TypeScript, NestJS and MySQL, published as @bitcoinuniverse/index-tandem version 0.1.0 under MIT, marked private because it is a service rather than a library.

What lives here is everything specific to running one indexer: configuration and its fail-closed boot validation, the marker inspection that binds a candidate to a deployment, the pure state engine, the reorg planner and its rollback, agreement tuple assembly and signing, the two-pipeline verified gateway, twenty-four HTTP routes, eleven database tables in two migrations, the container, and the compose stack.

What does not live here is any decision about what Tandem is.

github.com/bitcoinuniverseio/tandem

The public home of the protocol, under MIT, published as a documentation site at bitcoinuniverseio.github.io/tandem. It carries the normative specification tandem.md at its root, three JSON schemas, the generated golden vectors and their manifest, a release integrity record, and public guides. The reference implementation of the codec and validator is developed privately and reaches this repository only as published artifacts.

Nothing in the indexer may contradict this repository. If the two disagree, the specification is right and the indexer has a bug.

github.com/bitcoinuniverseio/tandem-verifier-rs

Pipeline B. Rust, axum and PostgreSQL, with its own parser, its own reducer, its own Bitcoin Core node and its own signing key. It shares no source code with this repository. The only material both pipelines consume is the frozen protocol corpus, which pipeline B pins by hash in its protocol-inputs.lock.json.

That repository documents what its signature does and does not prove, which is worth reading before treating a matching pair of tuples as a guarantee.

The indexer does not install the protocol package from a registry. It depends on a committed tarball:

"@bitcoinuniverse/tandem": "file:vendor/bitcoinuniverse-tandem-0.1.0.tgz"

A clone therefore contains the exact protocol bytes it was built against, with no network fetch and no chance of a silently different resolution. The Dockerfile copies vendor into the image before installing dependencies for exactly this reason.

SOURCE-PROVENANCE.json records where that tarball came from and what it should hash to:

Field Value
sourceCommit c0ff6cd231e2cdb90a6a8248a59dcabdf74873ad
packageVersion 0.1.0
artifact vendor/bitcoinuniverse-tandem-0.1.0.tgz
sha256 3fee883a8c1c60b214abae8c9aebcd02375da25ed79831b1340b8f3ab8ea624d

The same file declares that this implementation was independently authored for pipeline A, names the architecture patterns it was modelled on, and makes two negative claims explicitly: no protocol logic was copied, and no external runtime evidence is being asserted. It points at the public contract repository above, and records the private engineering commit the tarball was built from.

Now the honest part. Nothing in the repository or in CI recomputes that SHA-256, and the declared digest does not currently match the committed tarball. The verification gate runs lint, typecheck, tests, build and a high-severity audit, and none of those looks at provenance, which is how the two drifted apart without anything failing.

The committed artifact is intact: it is a valid gzip archive, npm install accepts it, and its SHA-512 matches the integrity value recorded in package-lock.json. Its actual SHA-256 is d7b3efb96dad4d5089aa42363110bbce7ae5fa737c35790eef63edd3213644b7, not the value declared above. The most likely explanation is that the declared digest was taken from a different build of the same package version, because a repacked tarball carries different timestamps and therefore a different digest even when the contents are identical.

Until the owner of the provenance record resolves which artifact the declared digest refers to, treat the sha256 field as unverified. The lockfile integrity check is the one that currently holds.

The lockfile check is a useful check and it is not the same check. It proves the install matches the lockfile, not that either matches the engineering commit the provenance file names.

If provenance matters to your deployment, verify it yourself:

Terminal window
sha256sum vendor/bitcoinuniverse-tandem-0.1.0.tgz

Automating that comparison as a CI step, and reconciling the declared digest with the artifact that is actually committed, are two of the smaller open jobs in the project.

Why pipeline B is not in this repository, and should not be

Section titled “Why pipeline B is not in this repository, and should not be”

The verified surface returns data only when two independently signed agreement tuples match on nine fields at the same canonical height. Everything that guarantee is worth comes from the word independent.

The architecture notes set the bar directly: pipeline B must use a separate codebase, node, store, owner and release process. Two implementations built in one repository would share their dependencies, their assumptions and their bugs, and would then agree with each other confidently about the wrong answer. The comparison would still pass. It would just stop meaning anything.

The gateway’s own design says the same thing from the other direction. Nine semantic fields must be identical, while each pipeline’s parser commit, indexer commit, binary hashes, key id and signature are deliberately never compared and are instead preserved side by side in the response. Those fields are expected to differ, because different programs built by different people is the entire point.

One consequence is worth knowing before you deploy: the code does not check that the two trusted key maps are disjoint, and does not check that the two key ids differ. If the same key appears in both maps, one signer can satisfy both sides of the comparison and the guarantee quietly disappears. Keeping them separate is the operator’s job.

For the files inside the protocol package and what each one is good for, read protocol artifacts. For what a second pipeline would have to implement, see pipeline B.