aere-docs/AERE-REPRODUCIBLE-BUILDS.md
Aere Network e4cead319d Initial public release
Aere Network public source. Everything here can be checked against the live
chain (chain id 2800, https://rpc.aere.network).

Scope note, stated up front rather than buried: consensus on chain 2800 is
classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at
the signature, precompile, account and transport layers. Nothing here makes the
consensus post-quantum, and no document in it should be read as claiming so.
2026-07-20 01:01:36 +03:00

496 lines
29 KiB
Markdown

# spec-28: Aere Network Reproducible Builds and Deterministic Release
Prepared 2026-07-19. This is the rebuild-the-binary manifest for Aere Network. Its companion,
[spec-23 / REPRODUCE.md] (`aerenew/REPRODUCE.md`), is the run-the-tests manifest: that document lets
a third party re-run our test suites, formal models, cryptographic known-answer tests, and
benchmarks. This document covers the other half of supply-chain integrity: how a third party rebuilds
the node client and the contract bytecode from source and confirms, by SHA256, that what is running
matches what was published. The two are meant to be read together and deliberately do not duplicate
each other.
Honesty tags used throughout:
- **[REAL]** the artifact exists in the tree at the cited path and was inspected for this document.
- **[CONFIRMED]** additionally exercised here (a script was run, a command produced output) on
2026-07-19.
- **[VERIFY: ...]** a claim that could not be confirmed from the tree alone and must be checked
before it is trusted.
- **[MEASURE: ...]** a heavy build or a digest that was not computed here. Run the stated command to
produce the real value. Do not quote a figure until you have.
If you find a bare claim without a tag where one is warranted, treat it as a bug in this document.
---
## 1. Why reproducible builds matter
A blockchain that "nobody owns" is only as trustworthy as the operator's ability to check what code
is actually running. Reproducible builds turn that from a promise into a checkable property:
> Anyone with the source tree at a given commit can rebuild the byte-identical client image (and the
> byte-identical contract bytecode) that the network runs, and confirm the match by SHA256.
The supply-chain threat this closes is a tampered binary masquerading as the real release. If the
published release carries a canonical SHA256 digest, and a stranger can independently rebuild from
public source and land on the same digest, then a modified binary (a backdoored signer, a silent
consensus change, an exfiltration hook) cannot pass as the genuine artifact: its digest will differ,
and the difference is visible to everyone at once. This is the precondition for two claims the
project makes elsewhere:
1. "The Foundation cannot secretly modify the chain." Unenforceable unless the running code is
verifiable against published source.
2. "Permissionless validator entry without trusted setup." A community operator joining the
validator set (the live set is N=7 today, roadmap to 21) must be able to prove they are running
the same code as every other operator, without trusting the Foundation to tell them so.
Reproducibility is the mechanism. The rest of this document inventories what is built, states plainly
what is not yet live, and gives the exact commands.
---
## 2. What exists today (inventory)
Every path below is relative to the repository root `aerenew/` unless stated otherwise. All were read
for this document.
### 2.1 The reproducible node image build
**`aerenew/node/Dockerfile.reproducible`** [REAL]. A multi-stage Docker build that compiles
Hyperledger Besu at a pinned ref and assembles a distroless runtime image. The reproducibility levers
actually present in the file:
- **Base images pinned by digest, not tag.** The builder stage is
`FROM eclipse-temurin@sha256:...` and the runtime stage is
`FROM gcr.io/distroless/java21-debian12@sha256:...`. A tag can be retagged to point at different
bytes; a digest cannot. The file states the intent explicitly ("the tag is documentation; the
digest is the law").
- **`SOURCE_DATE_EPOCH` build arg** threaded into `ENV` and used to stamp fixed mtimes: every source
file is `touch -d "@${SOURCE_DATE_EPOCH}"` before the Gradle build and every install output is
re-touched after. Identical epoch means identical mtimes throughout, so archive contents do not
drift with wall-clock time.
- **Deterministic Gradle flags.** `./gradlew --no-daemon --no-build-cache --no-watch-fs
-Porg.gradle.parallel=false -PreproducibleBuild=true installDist`. No daemon state, no local build
cache, no filesystem watcher, single-threaded, reproducible-archive mode. `GRADLE_OPTS` also sets
`-Dorg.gradle.daemon=false -Dorg.gradle.parallel=false -Dfile.encoding=UTF-8`, and `LC_ALL=C`,
`TZ=UTC` fix locale and timezone.
- **Besu ref pinned** via `ARG BESU_REF=25.5.0`; the build clones
`https://github.com/hyperledger/besu` at `v${BESU_REF}` with `--depth=1` and then `rm -rf .git` so
no git metadata leaks into a layer.
- **Alpine toolchain packages pinned by version** in the builder: `git=2.45.1-r0 bash=5.2.21-r0
unzip=6.0-r14`, and there is no `apk upgrade` (upstream package mutation would break determinism).
- **Fixed UID/GID** (`USER 10001:10001`) so no `useradd -m` writes a fresh home-directory timestamp.
- **Runtime layer is distroless** (`gcr.io/distroless/java21-debian12`): no shell, no package
manager, no version drift in the shipped image. Only the deterministic Besu install, the AERE
`config/genesis.json`, and `config/config.toml` are copied in. Genesis and config are copied last
so an application-level config change does not invalidate the deterministic Besu layer above it.
- **OCI labels** record source, license (Apache-2.0), and `io.aere.reproducible="true"`.
**[VERIFY: the two `FROM ... @sha256:` digest values in the Dockerfile appear to be illustrative
placeholders, not registry-resolved digests.** The builder digest ends
`...a0c2e4f6a8c0e2f4a6c8e0a` and the distroless digest ends `...c1d0e9f8a7b6c5d4e3f2a1b0`; both are
visibly patterned rather than random SHA256 output. Before any real reproducible build can resolve,
these must be replaced with genuine digests, obtained with
`docker buildx imagetools inspect eclipse-temurin:21.0.3_9-jdk-alpine` and
`docker buildx imagetools inspect gcr.io/distroless/java21-debian12:latest`, and pinned. As written,
the structure is correct and the levers are real, but a `docker build` would fail to pull those exact
digests. This is the single most important gap in the "what exists" column.]
**Scope note.** This Dockerfile builds stock upstream Hyperledger Besu at `v25.5.0` plus the AERE
genesis and config. QBFT consensus and the 0.5 second block period are configured through
`config/genesis.json` and `config/config.toml`, not through a client source patch, so the mainnet
consensus binary in this image is upstream Besu with AERE configuration. The post-quantum precompile
work (Falcon, ML-DSA, SLH-DSA, ML-KEM, HashToPoint at `0x0AE1..0x0AE7`) is a **separate** build path
with its own pinned base commit; see section 2.5. [VERIFY: confirm whether the live N=7 validator
image is this stock-Besu-25.5.0 build or the PQC-precompile fork build, and if the latter, whether
`Dockerfile.reproducible` should target the fork ref rather than upstream `v25.5.0`. The two build
recipes exist in the tree independently and their relationship to the live binary should be stated
explicitly.]
### 2.2 The third-party verify script
**`aerenew/scripts/verify-besu-image.sh`** [CONFIRMED]. A bash script that verifies a locally built
image against a published manifest. `./scripts/verify-besu-image.sh --help` was run here and returned
exit 0 with the usage banner, so the script is present and executable. What it does:
1. Requires `docker jq curl git` on PATH.
2. Resolves the local image digest: `docker image inspect "$IMAGE" -f '{{.Id}}'` (default image tag
`aere-besu:reproducible`). If the image is not present it prints the build command and exits 2.
3. Fetches the manifest JSON, either from a `--manifest` local file or from the URL
`https://aere.network/.well-known/besu-image-digest` (overridable with `--url`).
4. Parses `imageDigest`, `sourceCommit`, `sourceDateEpoch`, `besuRef`, `publishedAt`, `chainId` from
the manifest. Asserts `chainId == 2800` (exits 3 otherwise).
5. Compares the local digest against `imageDigest`. On mismatch it prints both digests, lists the
likely causes (wrong `SOURCE_DATE_EPOCH`, buildkit/arch difference, local edits to
genesis/config, upstream toolchain drift), and exits 4.
6. As a sanity check, confirms the manifest's `sourceCommit` exists in the local checkout via
`git rev-parse`. On match it prints the VERIFIED banner.
The script is real and self-consistent. Its comparison step, however, has nothing live to compare
against yet; see section 5.
### 2.3 The design and status document
**`aerenew/node/REPRODUCIBLE.md`** [REAL]. The narrative that accompanies the Dockerfile: how the
build achieves determinism, how to verify a running node, the intended published-manifest flow, the
weekly drift-detection cron, and the strategic rationale (audit-contest scoping, MiCA Article 22
operational controls, validator decentralization). Critically, it already carries an honest status
banner dated 2026-07-16 stating that the manifest is not yet published and that the end-to-end
guarantee is therefore not yet third-party verifiable. This document does not soften that; it
restates it in section 5.
### 2.4 The CI workflow
**`aerenew/.github/workflows/reproducible-besu-build.yml`** [REAL]. A GitHub Actions workflow that
mechanizes the double-build determinism check. Triggers: push to `main` touching `node/**`,
`config/genesis.json`, `config/config.toml`, or the workflow itself; manual `workflow_dispatch` with
an optional `besu_ref` input; and a weekly `cron: '0 3 * * 0'` (Sunday 03:00 UTC) for drift
detection. Job steps:
- Checkout with `fetch-depth: 0` (full history is needed to derive `SOURCE_DATE_EPOCH` from the HEAD
commit timestamp).
- Compute `SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct HEAD)`.
- Resolve `BESU_REF` from the `workflow_dispatch` input, falling back to the `ARG BESU_REF` line in
the Dockerfile.
- **BUILD #1 (canonical)** with `--provenance=mode=max --sbom=true`, capture image ID.
- **BUILD #2 (independent)** after `docker buildx prune` and with `--no-cache`, capture image ID.
- **Compare digests.** On mismatch, emit `::error::`, `docker save` both images, and diff their tar
listings for debugging, then fail.
- Write `artifacts/besu-image-digest.json` (version, chainId 2800, imageDigest, besuRef,
sourceDateEpoch, sourceCommit, workflowRun, publishedAt). This is exactly the schema
`verify-besu-image.sh` parses.
- Upload that artifact with 365-day retention.
- **Sigstore keyless attestation** via `actions/attest-build-provenance@v1` over the image digest
(`permissions: id-token: write, attestations: write`).
- **Publish manifest to `/.well-known`**, gated on `github.ref == 'refs/heads/main'` and a
`WELLKNOWN_DEPLOY_TOKEN` secret. If the secret is unset, the step prints a warning and exits 0
without publishing.
[VERIFY: whether `WELLKNOWN_DEPLOY_TOKEN` is configured in the repository's Actions secrets. Its
absence is the concrete reason no manifest is served today: the workflow reaches the publish step and
skips it. Also [VERIFY] whether this workflow has ever executed, since the public mirror repositories
it would run in are themselves not yet published, per REPRODUCE.md section 2.]
### 2.5 The PQC fork build recipe and its build record
The post-quantum precompile client is a distinct artifact from the mainnet node image, built by a
separate, pinned recipe. It is the "fork" whose KATs REPRODUCE.md section 3c covers.
- **`aerenew/pqc-fork/setup-fork.sh`** [REAL] pins the upstream Besu base commit
`BASE=d2032017bb3b8cb215a97303980a1e4a643f7180`, fetches it shallow, overlays the AERE PQC EVM
layer (five existing precompiles at `0x0AE1..0x0AE5`), then adds the two newer precompiles
(`0x0AE6` ML-KEM-768, `0x0AE7` Falcon HashToPoint), committing each stage for a clean patch.
- **`aerenew/pqc-fork/build-dist.sh`** [REAL] runs the single full build
`./gradlew -q :besu:installDist -x test` and lists the resulting `bin/besu` and lib jars.
- **`aerenew/parallel/combined-fork-binary/BUILD_INFO.txt`** [REAL] is a committed build-and-validation
record with real SHA256 checksums for a related fork artifact, including:
- install tarball `sha256 = 9561c40d591a10c03518a88423ae3098f8ee3791dbddb5f73ce388cd426276fc`
- `besu-evm.jar sha256 = 1d2bf44a08f306042c17276764fe05f040fe58371fb8e169fb1804bee815eb2d`
- `besu-ethereum-core.jar sha256 = c3b7ebee48e34ec83454d6f3e9dd5baf3261d87cedee1cebe574e7696b7478d2`
- the untouched "gold" isolator binary `sha256 = 7c5c0743088e9fa765d54c745814601b3c7a27a892e396156f88092b34c80772`
and a JDK-21, `./gradlew installDist -x test` rebuild recipe from a one-file diff. This is the
closest thing in the tree to a published expected-digest for a client artifact, though it is a
BUILD_INFO text record for a specific offline validation, not a signed manifest served at a stable
URL.
These checksums are jar-level and tarball-level SHA256, not the Docker image digest that
`verify-besu-image.sh` compares. They demonstrate that the project already computes and records real
build hashes; they are not yet wired into the manifest the verify script fetches.
### 2.6 The on-chain binary-commitment registry
**`aerenew/contracts/contracts/validators/AereValidatorManifest.sol`** [REAL] (with
`aerenew/contracts/test/validator-manifest.test.js`). A no-admin Solidity contract where each
validator's own QBFT signer key declares the SHA256 `binaryDigest` of the Besu image it runs plus a
`manifestUri` (typically the published `.well-known/besu-image-digest`), an operator label, and a
region. Commitments are keyed by signer with a monotonic serial and full event history; there is no
Foundation override. The contract's own NatSpec is honest that it cannot verify a validator actually
runs the claimed binary: that is the job of third-party probing plus the reproducible build. It gives
one canonical on-chain place to attest so audits do not drift across mediums. [VERIFY: whether this
contract is deployed on chain 2800 and, if so, at what address; the file and its test exist in the
tree but deployment was not confirmed here.]
### 2.7 The publication checklist
**`aerenew/GITHUB_DEPLOYMENT_CHECKLIST.md`** [REAL]. The gate that every public publish passes.
Section 3 below turns it into the deterministic release checklist. It is a GitHub-publication
checklist, explicitly not a mainnet deployment procedure: chain 2800 is already live and nothing in
it deploys a contract or touches consensus.
---
## 3. The deterministic release process
A release is deterministic when it runs the same way every time and produces the same published
artifacts from the same source commit. The checklist below composes the publication gate
(`GITHUB_DEPLOYMENT_CHECKLIST.md`), the reproducible-build CI
(`reproducible-besu-build.yml`), and the verify step (`verify-besu-image.sh`) into one repeatable
sequence. Steps marked (roadmap) are not yet live; see section 5.
1. **Freeze the source at a commit.** All release artifacts are keyed to one commit SHA. Nothing is
built from a dirty working tree. The commit timestamp is the single source of
`SOURCE_DATE_EPOCH`.
2. **Secret scan (hard gate).** No private keys, no `.env` files with real values, no API tokens, no
internal host addresses anywhere in the diff. The DevRel publishing flow gates publication on a
secret-scan pass and it must not be bypassed. (`GITHUB_DEPLOYMENT_CHECKLIST.md`, and
REPRODUCE.md section 1.)
3. **Claim honesty check.** No fabricated or stale performance claims. Every TPS, latency,
validator-count, block-time, supply, or "audited" statement in copy must match the checkable state
of the chain or an existing dated doc under `docs/`. In particular: consensus is Besu QBFT with
classical secp256k1 ECDSA (not proof-of-stake, not post-quantum); block time is 0.5 seconds with
single-slot BFT finality; total supply is 2,800,000,000 AERE fixed since genesis-v2 (2026-05-07);
273,000 TPS is a synthetic design ceiling, not a measured mainnet number, and must be stated that
way if it appears. No forbidden marketing superlatives ("world-class," "excellence," "gold
standard," "trusted by"). No em-dashes in prose copy. Address book (`sdk-js/src/addresses.ts`)
current with honest status for any referenced contract.
4. **Green CI (hard gate).** Build, the full test suite, and the secret scan run in CI, on a pull
request, before merge. Merge to a protected branch happens through a reviewed pull request, not a
direct push.
5. **Deterministic build, twice, byte-equal.** The `reproducible-besu-build.yml` workflow builds the
node image on a clean runner, wipes the buildkit cache, rebuilds `--no-cache`, and asserts the two
image IDs are identical. A mismatch fails the release and dumps a tar-listing diff for triage. The
weekly cron runs the same check to catch upstream toolchain drift before an operator does.
6. **Compute and record the canonical digest.** CI writes `besu-image-digest.json` (chainId 2800,
imageDigest, besuRef, sourceDateEpoch, sourceCommit, workflowRun, publishedAt) and uploads it with
365-day retention.
7. **Sign the build (provenance).** Sigstore keyless attestation over the image digest via
`actions/attest-build-provenance@v1`, plus `--provenance=mode=max --sbom=true` on build #1.
8. **Publish the manifest at a stable URL (roadmap).** POST `besu-image-digest.json` to
`https://aere.network/.well-known/besu-image-digest`, gated on `main` and on the
`WELLKNOWN_DEPLOY_TOKEN` secret. This is the step that makes third-party verification possible and
is not yet live (section 5).
9. **Tag the release honestly.** If the publish includes a release tag, the tag description matches
what actually shipped, not aspirational figures. (`GITHUB_DEPLOYMENT_CHECKLIST.md`.)
10. **Post-publication smoke checks.** Confirm `https://rpc.aere.network` responds before linking it.
Spot-check cited contract addresses with `eth_getCode` on the live RPC (non-empty result confirms
real deployment). (`GITHUB_DEPLOYMENT_CHECKLIST.md`.)
11. **Third-party verification path (roadmap, once step 8 is live).** A stranger checks out the
release commit, builds locally, and runs `verify-besu-image.sh`, which fetches the published
manifest and asserts a digest match. Until step 8 serves a real manifest, this step can only be
run against a `--manifest` local file, which proves local double-build determinism but not
agreement with a Foundation-published canonical digest.
Optionally, operators record their running binary's digest on chain via `AereValidatorManifest`
(section 2.6) so the attestation is queryable, not just claimed.
---
## 4. Contract bytecode reproducibility
The node client is one supply-chain surface; the deployed contracts are the other. Reproducing
contract bytecode means: compile the source with the pinned compiler and settings, and confirm the
result matches what is deployed on chain 2800 via `eth_getCode`.
**Pinned compiler and settings** (`aerenew/contracts/hardhat.config.js`) [REAL]:
- Global compiler `solc 0.8.23`, `optimizer.enabled = true`, `optimizer.runs = 1` (runs=1 chosen for
contract-size headroom, documented in-file), `viaIR = true`. Default EVM version is shanghai for
0.8.23.
- One per-file override: `contracts/AereCancunCanary.sol` compiles with `solc 0.8.24`,
`optimizer.runs = 200`, `evmVersion = "cancun"`. Every other contract stays on 0.8.23 so its
bytecode is untouched by the canary.
The Foundry profile (`aerenew/contracts/foundry.toml`) [REAL] independently pins `solc = '0.8.23'`,
`optimizer_runs = 200`, OpenZeppelin remapped to the single installed copy (OpenZeppelin 4.9.6). Note
the two profiles differ in `optimizer_runs` (Hardhat 1, Foundry 200) and Foundry is scoped only to
`contracts/pqc` for symbolic analysis, so the **functional deploy artifacts come from the Hardhat
build**; use the Hardhat settings when reproducing deployed bytecode, not the Foundry profile.
**Reproducing deployed bytecode:**
```bash
cd aerenew/contracts
npm ci # pinned toolchain (hardhat, solc 0.8.23, OZ 4.9.x)
npx hardhat compile # deterministic given the pinned solc + settings above
# The deployed runtime bytecode for a given contract is in
# artifacts/contracts/<Name>.sol/<Name>.json -> .deployedBytecode
```
Then compare against what is live:
```bash
# Fetch on-chain runtime bytecode for a deployed address on chain 2800:
curl -s -X POST https://rpc.aere.network \
-H 'content-type: application/json' \
--data '{"jsonrpc":"2.0","id":1,"method":"eth_getCode","params":["<CONTRACT_ADDRESS>","latest"]}'
# Compare the returned 0x… string against the compiled .deployedBytecode.
```
**Honest caveat on the metadata hash.** `hardhat.config.js` does not set
`settings.metadata.bytecodeHash`, so solc's default applies and the compiled bytecode ends with a
CBOR-encoded metadata section that embeds a hash of the contract metadata (source, settings,
compiler). Two compiles agree on that trailer only when the full metadata matches, so a byte-exact
`eth_getCode` comparison requires the identical source, identical `solc 0.8.23`, identical optimizer
and `viaIR` settings, and identical import paths. If any of those differ, the executable opcodes can
still match while the metadata trailer differs. For a strict match, either reproduce the exact
metadata or compile with `metadata.bytecodeHash = "none"` on both sides and compare the code minus
the trailer. [MEASURE: pick one deployed contract, compile it under the pinned settings, and publish
both the compiled `deployedBytecode` and the `eth_getCode` result so the match (or the metadata-only
difference) is on the record. No specific contract byte comparison was run for this document.]
REPRODUCE.md section 3a is the companion here: it covers running the contract **test** suite (137
Hardhat test files, spot-checked green on 2026-07-19) and the symbolic layer. This document covers
reproducing the **bytecode**; the two together cover both "does it behave as claimed" and "is the
deployed code the compiled code."
---
## 5. Honest status: done vs roadmap
**Done and real today (local reproducibility):**
- The digest-pinned, multi-stage `Dockerfile.reproducible` with `SOURCE_DATE_EPOCH`, deterministic
Gradle flags, distroless runtime, and fixed UID/GID. [REAL]
- The `verify-besu-image.sh` script, executable and self-consistent, with a `--manifest` local-file
path that works without any network dependency. [CONFIRMED runs]
- The `reproducible-besu-build.yml` CI workflow: double build, byte-equal compare, provenance and
SBOM, Sigstore attestation, artifact upload. [REAL, execution not confirmed here]
- The publication gate (`GITHUB_DEPLOYMENT_CHECKLIST.md`): secret scan, claim honesty, green CI. [REAL]
- Pinned contract compiler settings (`solc 0.8.23`, optimizer, `viaIR`) enabling deterministic
contract compilation. [REAL]
- Real, recorded SHA256 checksums for fork jar and tarball artifacts in
`combined-fork-binary/BUILD_INFO.txt`, and pinned fork base commits in `pqc-fork/`. [REAL]
- The `AereValidatorManifest` on-chain attestation contract. [REAL, deployment unconfirmed]
**Roadmap, not yet live (third-party verifiability):**
- **The public digest manifest is not published.** This is the load-bearing gap. Per the status note
in `REPRODUCIBLE.md` (dated 2026-07-16), a live check of
`https://aere.network/.well-known/besu-image-digest` returned `Content-Type: text/html`, the site
SPA fallback, not the JSON manifest. So the third-party comparison step in `verify-besu-image.sh`
has nothing canonical to diff against. The CI publish step exists but is gated on a
`WELLKNOWN_DEPLOY_TOKEN` secret and skips (with a warning) when the secret is unset. State it
plainly: the build recipe exists and is reproducible locally; **publishing the canonical expected
digests, so a stranger can diff against them, is the remaining step.** [VERIFY: re-check the
`.well-known` URL and whether the deploy token is configured before making any "published" claim.]
- **The Dockerfile base-image digests appear to be placeholders** (section 2.1), so even a local
`docker build` will not resolve until real registry digests are pinned. This must be fixed before
the first genuine reproducible build. [VERIFY]
- **Kernel and host environment are not pinned** for operators. `REPRODUCIBLE.md` notes CI pins
`ubuntu-22.04` but operators run their own systems; a recommended host kernel plus a reference OS
image (Yocto / NixOS) is Phase 2.
- **Genesis-snapshot distribution** for operators bootstrapping from state (rather than from genesis)
needs its own signed snapshot manifest; tracked separately.
- **Public source repositories that the site and docs reference do not all resolve yet** (REPRODUCE.md
section 2, PASUL 0: `git.aere.network/aere-network/aerenew` returned 404 on 2026-07-19). Reproducible
builds presume the source is actually published; until it is, "rebuild from source" over-promises.
Bottom line: everything needed to reproduce the build **locally** and to prove double-build
determinism is in the tree. The one thing standing between that and a stranger independently
confirming a running validator matches a Foundation-published binary is the publication of the signed
digest manifest at a stable URL, plus replacing the placeholder base-image digests. Neither is a
research problem; both are wiring.
---
## 6. Reproduction commands
Concrete values from the current checkout (`aerenew/`, HEAD `6c55540`, commit timestamp
`1781391508` = 2026-06-14T01:58:28+03:00). Substitute the release commit when reproducing a specific
release.
### 6.1 Rebuild the node image and check double-build determinism
```bash
# From a checkout of the release commit:
cd aerenew
SDE=$(git log -1 --pretty=%ct HEAD) # SOURCE_DATE_EPOCH from the commit, e.g. 1781391508
# Build once.
docker build \
--build-arg SOURCE_DATE_EPOCH=$SDE \
--build-arg BESU_REF=25.5.0 \
-f node/Dockerfile.reproducible \
-t aere-besu:reproducible \
.
# Read the resulting image digest.
docker image inspect aere-besu:reproducible -f '{{.Id}}'
```
[MEASURE: this is a full Besu compile in Docker (tens of minutes, several GB). It was NOT run for
this document, and it will not resolve until the placeholder base-image digests in
`node/Dockerfile.reproducible` are replaced with real registry digests (section 2.1). Publish the
resulting image ID once the build succeeds; that value is the canonical digest.]
```bash
# Double-build determinism, the check CI automates:
docker build --build-arg SOURCE_DATE_EPOCH=$SDE --build-arg BESU_REF=25.5.0 \
-f node/Dockerfile.reproducible -t aere-besu:build1 .
docker buildx prune -f
docker build --no-cache --build-arg SOURCE_DATE_EPOCH=$SDE --build-arg BESU_REF=25.5.0 \
-f node/Dockerfile.reproducible -t aere-besu:build2 .
diff <(docker image inspect aere-besu:build1 -f '{{.Id}}') \
<(docker image inspect aere-besu:build2 -f '{{.Id}}') && echo "REPRODUCIBLE"
```
[MEASURE: two full builds. Not run here. The identical-ID assertion is the reproducibility property.]
### 6.2 Verify against a manifest
```bash
cd aerenew
# Against the published manifest (roadmap; not serving JSON yet):
./scripts/verify-besu-image.sh --image aere-besu:reproducible
# Against a local manifest file (works today, proves local determinism, not
# agreement with a Foundation-published canonical digest):
./scripts/verify-besu-image.sh --image aere-besu:reproducible --manifest ./local-manifest.json
```
[CONFIRMED: `./scripts/verify-besu-image.sh --help` was run on 2026-07-19 and returned exit 0. The
digest-comparison path was not exercised because no image was built here and no manifest is served.]
### 6.3 Reproduce contract bytecode
```bash
cd aerenew/contracts
npm ci
npx hardhat compile
# Compare artifacts/contracts/<Name>.sol/<Name>.json .deployedBytecode
# against eth_getCode for the deployed address (see section 4 for the curl call).
```
[MEASURE: run a specific contract's compile-and-compare and publish both byte strings. Mind the
metadata-hash trailer caveat in section 4. Not run for this document.]
### 6.4 Rebuild the PQC precompile fork (separate artifact, Linux)
```bash
cd aerenew/pqc-fork
bash setup-fork.sh # clones besu@d2032017, overlays fork EVM, adds 0x0AE6/0x0AE7 precompiles
bash build-dist.sh # ./gradlew :besu:installDist -x test (one full build)
```
[MEASURE: heavy Linux-only build, targets `/root/besu` on a scratch box. Not run here, and never on
the live infra box, which runs production services. Publish the resulting `bin/besu` and jar SHA256
values and compare against `combined-fork-binary/BUILD_INFO.txt`.]
---
## 7. Relationship to REPRODUCE.md (spec-23)
`aerenew/REPRODUCE.md` (spec-23) and this document (spec-28) are the two halves of "verify Aere
yourself" and are meant to be non-overlapping:
- **spec-23 (REPRODUCE.md): run the tests.** Re-run the Hardhat suite, the z3 SMT formal models, the
PQC KAT/ACVP harnesses, and the gas benchmarks, and get the same results we get. It answers "does
the code behave as claimed."
- **spec-28 (this doc): rebuild the binary.** Rebuild the node image and the contract bytecode from
source and confirm the SHA256 match against the published release. It answers "is the running code
the published code."
Both are gated by the same publication gate (secret scan plus green CI) and both are honest that the
public source repositories and the digest manifest are not fully live yet. Neither pushes anything;
they document the verification story and the exact commands to execute it.