Skip to content
downpipes docs

Changelog and versioning

downpipes tracks more than one version, and they move independently. This page explains what each version line means, which versions are real today, and which release artefacts are aspirational rather than shipped. It is for a developer or an auditor who needs to reason about compatibility, and who needs an honest picture of what has and has not been published. It also hosts the human-readable change record.

The headline distinction is the one to hold onto. The on-disk archive format carries its own version, downpipe/0.1.0, and it moves independently of any tool’s version. It is a semver version. While the major is 0, a change to any byte-level rule of the format bumps the minor, which is a new format identity rather than a revision of the old one, and a patch changes no byte-level rule. The reader, the recovery CLI and the engine each carry their own version that moves separately, and a change to one of them is not a change to the format.

The version lines

There are four version lines, and they answer different questions.

Version line What it is Where it is set Moves when
Archive format The on-disk format string downpipe/0.1.0. It appears byte-identically in the cleartext root manifest and in the format’s key-derivation labels. The format constants, shared by the Go reference and the engine. Only a change to a format rule. While the major is 0, a byte-level change bumps the minor and is a new format identity; a patch changes no byte-level rule.
Tool (CLI) version The version of the offline downpipe reader and recovery CLI. Injected at release time by the build pipeline; an unstamped local build reports dev. Per CLI release.
Engine version The engine’s own release version, compared against the vendor-signed recommended version on the update check. A constant in the engine source, bumped per engine release. Per engine release.
Console version The console’s own build-stamped version, shown on the Provenance card and compared against the signed channel’s console component in the browser (the engine cannot know which console build a browser is running). Stamped by the console build into the bundle and its build-stamp file. Per console release.

The format version is the contract that matters for recovery. Because the reader and the format are versioned separately, an archive written today is readable by any reader that accepts the format identity the archive names, whichever engine version produced it. A reader for the current format accepts exactly downpipe/0.1.x, so it opens every archive in the current format line and refuses one that names a different identity. That separation is what lets the recovery path stay independent of the engine.

The format version is distinct from the tool version

Do not conflate the two. The archive format string downpipe/0.1.0 lives in the format spec and moves only when a format rule changes. The tool version is a separate number that the CLI binary reports, and a plain local build of the CLI reports dev rather than a release number. A release of the CLI does not move the format version, and a change to the format does not pin the CLI to any one tool version.

The archive format version

The archive format is downpipe/0.1.0, specified normatively in the format spec and pinned by the conformance vectors. The version string is not cosmetic: it appears byte-for-byte in the root manifest and is mixed into the format’s HKDF and MAC labels, so it is bound into the cryptography rather than being a loose tag. Change the string and you change the derived keys, which is why a byte-level change produces a new format identity rather than an amendment to the existing one.

A change to any byte-level rule of the format is a new format version, not an erratum and not a changelog entry. While the major is 0, that change bumps the minor, and a reader built for the current format does not accept the result. A patch is reserved for a change that touches no byte-level rule, which is why a reader accepts the whole downpipe/0.1.x line rather than one exact string. That is a deliberately high bar. It means an archive’s interoperability is governed by the spec and its vectors, and a reader either accepts an archive’s format identity or it does not. The change record below tracks the reader, the CLI and the library; it does not track format-rule changes, which are a spec concern.

The tool version and the engine version

The CLI’s version is injected at release time by the build pipeline through a linker flag. A plain go build, with no release stamping, leaves the version at dev, so an unstamped local build reports itself as dev. This is by design: a build that was not cut by the release pipeline says so honestly rather than claiming a release number it does not have.

The engine carries its own version constant, separate from the CLI. The update check reads it and compares it against the recommended version in the vendor-signed channel: an update is available when the recommended version differs from the engine’s own. The engine version also gates compatibility, since a release can declare a minimum engine version it must be applied onto, and the safe-apply harness refuses to apply a release onto too old an engine. Since channel schema v2 a release may also carry a console component with its own version; the console compares that against its own baked build stamp in the browser, because the engine cannot know which console build a browser is running.

What is real today, and what is aspirational

This is the part to read carefully, because the version numbers above could otherwise imply release artefacts that do not yet exist.

The working install path is go install github.com/downpipes/downpipe/cmd/downpipe@latest, which needs Go 1.26 or newer and network access to fetch the module, or a from-source vendored build of the CLI from a clone of the default branch for a no-network build. Either recovers an archive offline today; only fetching the module needs a network connection, the recovery itself never does.

Some release artefacts remain works in progress. A v0.2.0 git tag now closes the gap v0.1.1 used to leave, and it is worth stating precisely.

Artefact Status Why
A git tag Current v0.2.0, tagged 2026-08-03. Carries prune, recombine and unseal-export. A tag-drift CI check in the downpipe repository now fails if a tag is ever cut without all three commands present.
go install of the module at the latest version Resolves, to a complete reader @latest resolves to v0.2.0. Run downpipe --help after installing and confirm prune, recombine and unseal-export are listed before relying on it for a real recovery, since a cached module resolution can still hand you an older binary even when the tag itself is current.
A GitHub Release Exists, not yet published A v0.2.0 release exists on the repository (six platform archives, six CycloneDX SBOMs, a checksum file, a cosign signature) as a maintainer-reviewed draft. It is not a public download yet.
Signed, reproducible release binaries Built, not yet public The release pipeline produced cosign-signed, SBOM-attested binaries for six platforms against the v0.2.0 tag; they sit in the draft release above. A binary from go install or a local build still stamps its version as dev, since neither runs the release pipeline.
An SBOM-attested releases page Not shipped It depends on the same release pipeline output being published rather than held as a draft.

Confirm the command set, not just the version string

go install .../downpipe@latest resolves to a complete reader today: tag v0.2.0 carries prune, recombine and unseal-export, and a tag-drift check now guards against a repeat of the earlier v0.1.1 trap, where the newest tag was 51 commits behind and missing all three. Still confirm it yourself with downpipe --help before a real recovery. Neither go install nor a from-source build stamps a release version, so the version string alone will not tell you whether the command set is complete; the GitHub Release with a stamped version and signed binaries exists but is not yet published.

The engine and the console deploy from their main branches to the demo, which sits behind Cloudflare Access, and customers self-host their own engine and console. The update channel is live: the owner signer ceremony is complete and the vendor serves the signed channel that the self-hosted engines pull, so a base-config engine reports it as configured and verified. The deployed Cloudflare version id of a running engine is recorded for the safe-apply self-check, but it is not surfaced as a customer-facing version number; the version a customer reasons about is the engine version above.

The change record

The change record below follows the Keep a Changelog convention and Semantic Versioning. The 0.1.0 heading is the human record of the first release of the offline reader, the recovery CLI and the library; as noted above, it is the documented record rather than a published git tag. That heading is the tool’s version line. It is not the archive format version, which is downpipe/0.1.0 and is tracked separately, as set out above.

0.1.0, 2026-06-17

The first record of the offline reader, the recovery CLI and the library.

The format. The downpipe/0.1.0 archive format, specified normatively in the format spec and pinned by the conformance vectors. Any change to a byte-level rule is a new format version, which bumps the minor while the major is 0.

The offline recovery path. The keygen, inspect, verify, attest, restore and selftest commands. The recovery path imports no telemetry and no vendor SDK and makes no network call to Cloudflare or to the vendor, so a holder of the destination bucket bytes and the offline break-glass key can recover with no vendor in the loop.

Verification and attestation. End-to-end verification against an operator-pinned signer, the recipient set, the key commitment, every shard hash, the declared record count, the Merkle root and each record’s hash, with freshness enforced against the signed run log. A keyless attestation that verifies the root signature, the shard hashes and the run log without the break-glass identity and materialises no plaintext.

Restore targets. A dry run by default that plans the writes and reports conflicts, with --apply to write. The targets are a file per record, dotenv lines to stdout, and a discard sink that decrypts and verifies every record while writing nothing. A restore never overwrites existing target state.

The hybrid envelope. The post-quantum hybrid envelope: X25519 with ML-KEM-1024 for confidentiality with AES-256-GCM in a streamed cipher, and Ed25519 with ML-DSA-87 signatures where both halves are required and there is no downgrade, with HKDF-SHA-384 throughout. The hybrid combiner is pinned by a known-answer vector.

Reading from a bucket. Reading archives from a local directory or an S3-compatible bucket, with credentials taken from the standard AWS environment variables and S3 redirects refused.

The provisioner path. The setup, preflight, init and update commands used to stand up or upgrade the engine. The preflight command makes read-only GET-only calls to the Cloudflare API with an operator-supplied token. The setup command is a dry run by default that prints its plan and contacts nothing; under --apply it writes to Cloudflare, creating an Access application and policy and putting Secrets Store entries and Worker bindings. The init and update commands are plan printers that make no network call. None are on the recovery path.

Version reporting. The version, --version and -v outputs report the build version, injected at release time and reported as dev for an unstamped local build.

Why the format version and the tool version are kept apart on purpose

The format constants live in a leaf package that imports none of the crypto, reader or command code, so the version-pinned byte rules sit in one place that the writer, the reader and the conformance vectors all share. The engine carries a parallel copy of those same constants, checked against the Go reference, because a label or a size that diverges between the two would be a silent interoperability break.

Keeping the format version in that leaf, separate from the tool and engine versions, is what lets the tool evolve without touching the format and lets the format move without pinning the tool. It is also what lets the recovery story hold: an archive written by any engine version is openable by any reader that accepts the archive’s format identity, with no dependency on matching version numbers between the writer and the reader.

Where this fits

  • The command reference documents the downpipe CLI whose version this page describes, including the version command.
  • The open-core boundary explains which parts are open source and how the from-source vendored build is the supported install path.
  • Applying an update from the channel covers how the engine version is compared against the vendor-signed recommended version.
  • Precise claims and honesty is the canonical list of the honest phrasings this page follows, including the not-yet-shipped release artefacts.
  • Prove recoverability shows the offline reader recovering an archive independent of the engine version that wrote it.

Last updated .