Skip to content
Documentation (14 pages)

Status: the reproducible-build metadata (cargo-chef recipe + a fixed SOURCE_DATE_EPOCH, recorded in the manifest) and the verification instructions below are published. The build script defaults SOURCE_DATE_EPOCH to the source commit time; packages built before 2026-09-25 carry an empty sourceDateEpoch. The signing-identity decision (D-08) is made — minisign with a pinned key (trust/release-key.pub; no third party in the trust path). Release binaries ride the implemented chain (below); the /pkg manifest slot activates with the deploy-path wiring at the key ceremony. Nothing here is gated behind a third party: the hash comparison works today.

Reproducible Builds

The browser client ships as two artefacts — tallow_web_bg.wasm (the Rust encryption engine, compiled to WebAssembly) and tallow_web.js (the bindgen glue that loads it). Both are published with SHA-384 hashes in /pkg/manifest.json, and the in-page self-test re-hashes the bytes your browser actually receives against that manifest.

This page documents how to rebuild those artefacts from source and compare the result, so the hash in the manifest is something you can check rather than something you have to trust.

Building the package

The build is deliberately boring: a pinned toolchain, a fixed source timestamp and a deterministic dependency recipe.

Terminal window
git clone https://github.com/tallowteam/Tallow.git
cd Tallow
# 1. Fixed source timestamp — the build must not depend on the clock.
# Our releases use the release commit's time; to reproduce a published
# package, use the exact value recorded in its manifest (`sourceDateEpoch`).
export SOURCE_DATE_EPOCH=$(git log -1 --format=%ct)
# 2. Deterministic dependency layer (cargo-chef):
# the recipe is computed from Cargo.lock + Cargo.toml only, so the
# dependency build does not depend on source-file mtimes.
cargo install cargo-chef --locked
cargo chef prepare --recipe-path recipe.json
cargo chef cook --release --target wasm32-unknown-unknown --recipe-path recipe.json
# 3. Build the engine and emit the deployable package
bash scripts/build-browser-wasm.sh

scripts/build-browser-wasm.sh writes the package into website/public/pkg/: the wasm, the glue, .br/.gz sidecars, a CHECKSUM.sha256 and the SRI manifest.json (wasmHash / jsHash, both sha384-…).

Comparing against the published hashes

Terminal window
# Hashes produced by your build
python3 - <<'PY'
import base64, hashlib
for name in ("tallow_web_bg.wasm", "tallow_web.js"):
b = open(f"website/public/pkg/{name}", "rb").read()
print(name, "sha384-" + base64.b64encode(hashlib.sha384(b).digest()).decode())
PY
# Hashes the site publishes
curl -s https://sendtallow.com/pkg/manifest.json

Equality of both fields (wasmHash, jsHash) is the whole check. A mismatch is a real signal: either the toolchain drifted or the published artefact is not the one this source produces — report it (see /.well-known/security.txt).

The comparison proves the bytes you received are the ones the manifest describes; the rebuild recipe above is the second half — rebuild, compare, and the pins below are what make the result reproducible. Attesting who built a published artefact is the job of the signature slot (D-08, still null).

Hash algorithm

The manifest uses SHA-384 (SRI form, sha384-…). Older text in the design report refers to SHA-256 for the same artefacts; SHA-384 is the shipped format and the one the self-test verifies. The algorithm in the verification path is what matters, so the divergence is recorded here rather than papered over.

Signature (minisign, pinned key — D-08)

D-08 is decided: minisign (Ed25519), with the public key pinned in the source tree at trust/release-key.pub — trust is “pin the key”: no keyring, no keyserver, no third party in the trust path.

Release binaries ride the implemented chain (it activates at the key ceremony): each release publishes a MANIFEST.json — every archive with its sha256 — signed to MANIFEST.json.minisig. Verification:

Terminal window
minisign -Vm MANIFEST.json -x MANIFEST.json.minisig -p trust/release-key.pub
scripts/verify-release.sh <release-dir> # end-to-end incl. every artifact hash

The /pkg manifest slot (this page’s subject) activates with the same ceremony: the deploy-path wiring is the remaining step, and the pin file carries a fail-closed placeholder until the key exists — releases refuse to publish unsigned in the meantime. Custody model and the out-of-band channels (repo file now; CLI constant and DNS TXT queued) live in trust/README.md.

Toolchain pins

Component Pin
Rust target wasm32-unknown-unknown
wasm-bindgen CLI must match the locked crate version (the build script enforces it)
wasm-opt -O3 with the listed feature flags (binaryen)
Source date SOURCE_DATE_EPOCH — default: source commit time; recorded in the manifest