Participate
Repositories
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.
The indexer
Section titled “The indexer”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.
The protocol contract
Section titled “The protocol contract”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.
The independent verifier
Section titled “The independent verifier”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.
How the protocol package is vendored
Section titled “How the protocol package is vendored”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:
sha256sum vendor/bitcoinuniverse-tandem-0.1.0.tgzAutomating 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.