Files
Ekklesia/scripts/test-corresponding-source.sh
ekklesiaandClaude Opus 5.5 e083c44e05
CI / verify (pull_request) Successful in 1m3s
CI / infrastructure-smoke (pull_request) Successful in 36s
CI / migration-smoke (pull_request) Successful in 1m53s
CI / package-release (pull_request) Successful in 36s
CI / package-publication (pull_request) Canceled after 0s
CI / deploy-smoke (same-origin) (pull_request) Canceled after 0s
CI / deploy-smoke (split) (pull_request) Canceled after 0s
CI / image-build (pull_request) Canceled after 30s
feat: corresponding-source artifacts for native packages that redistribute copyleft code (ADR 0055)
Ekklesia may publicly distribute a native Module Package containing LGPL or MPL components only beside
a version-specific corresponding-source artifact. The artifact carries the exact source, patches,
build definitions, and rebuild and relink instructions for those components. ADR 0055 (Accepted)
records the policy: project compliance policy, not legal certification. It decides the item ADR 0052
deferred, and extends ADR 0049's distributable-release gate. Module Package v1, packageFormat 1, and
both libvips supplements are unchanged.

The mechanism is generic and lives in @ekklesia/module-pack:
- Source recipes: reviewed data under corresponding-sources/<package>/<version>/. recipe.json names
  the exact package (name, version, content digest) and the exact supplement it was prepared with,
  and lists every component with its role, licence, licence material, and relationship to the
  binary. Every source file and patch is recorded by origin, size, and SHA-256; documents are hashed
  too, and every exclusion has a reason. Loading is strict and refuses anything unlisted.
- Acquisition (the only network step) fetches each recorded file once into a cache keyed by
  SHA-256. It uses node:https with exact headers and no decoding, retries transient failures, and
  stores only bytes that match.
- Construction is offline. From a built package it finds the covered native dependencies (by
  declared licence, or by a supplement component's licence), verifies every byte, and writes four
  sibling artifacts: the deterministic <package>-corresponding-source.tar.gz and its .sha256;
  <package>.redistribution.json, linking package, dependencies, supplement, recipe, and source by
  digest; and <package>.source-notice.txt, the release notice.
- The public-distribution gate (verifyRedistribution, `ekklesia-corresponding-source verify`)
  approves only when the recipe, supplement, canonical archive, manifest, and metadata identify
  exactly these files. It refuses bundled copyleft code, and approves packages with no covered
  native code without an artifact. packModule is untouched.

First complete proof: @img/[email protected] (sharp 0.35.5), from the reviewed supplement
and provenance.
- 383 components and 387 source files, about 208 MB: the sharp-libvips v1.3.4 build definitions,
  the 27 library archives, the five build-time patches, the 353 crates librsvg's resolved Cargo.lock
  records, and rust-src for nightly-2026-09-27 (rustc 75a75c3e0), which vendors std's dependencies.
- BUILDING.md and two helpers point sharp-libvips's unchanged build logic at those files.
- Followed in a Rocky Linux 8 container with networking disabled, they rebuilt
  libvips-cpp.so.8.18.7, byte-identically across two builds. Swapped into a verified, extracted
  native package, the rebuilt library was the one loaded, and sharp ran.
- The native proof's source artifact, native-proof-0.1.0-linux-x64-glibc-corresponding-source.tar.gz,
  is 207242785 bytes, sha256 cc405492032b04a5391ab84e64f0e5e50f78e571ef7818204782c25a03eb50b7. It is
  byte-identical from unrelated directories and from a cache acquired fresh from upstream.

Tests:
- corresponding-source.test.ts runs in CI: synthetic inputs, every negative case, acquisition with
  retries and refusal, and determinism.
- corresponding-source-native.test.ts checks the real recipe everywhere. With a verified cache
  (`pnpm test:corresponding-source`, which acquires once) it builds the real artifact twice, gates
  it, fails it closed, and runs sharp from the package.

Docs: the module guide's distribution section, the README and architecture notes, and an ADR 0052
cross-reference.

No Signage change, version bump, publication, or tag.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 23:17:35 -05:00

36 lines
1.9 KiB
Bash
Executable File

#!/usr/bin/env bash
# ADR 0055's full proof, with real sources: the native proof module (sharp 0.35.5, libvips 1.3.4) and
# its corresponding-source artifact.
#
# bash scripts/test-corresponding-source.sh
#
# 1. Acquisition (network, first run only): every checked-in recipe's recorded source files are
# downloaded into a verified cache, each checked against its recorded size and SHA-256. A file
# already in the cache with the right bytes is never fetched again.
# 2. Construction and the gate (offline): the artifact is built twice from unrelated directories and
# must be byte-identical; the public-distribution gate must approve it, and must refuse it once it
# is removed or corrupted; sharp must still run from the verified binary package.
#
# EKKLESIA_CORRESPONDING_SOURCE_CACHE chooses the cache directory; the default is
# ${XDG_CACHE_HOME:-$HOME/.cache}/ekklesia/corresponding-sources. It is about 200 MB per recipe.
set -Eeuo pipefail
root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$root"
cache="${EKKLESIA_CORRESPONDING_SOURCE_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/ekklesia/corresponding-sources}"
pnpm exec turbo run build --filter=@ekklesia/module-pack... --filter=@ekklesia/core... >/dev/null
recipes="packages/module-pack/corresponding-sources"
while IFS= read -r recipe; do
relative="${recipe#"$recipes"/}"
relative="${relative%/recipe.json}"
echo "==> Acquiring the recorded sources for ${relative%/*}@${relative##*/}"
node packages/module-pack/bin/ekklesia-corresponding-source.mjs acquire \
--recipe "${relative%/*}@${relative##*/}" --cache "$cache"
done < <(find "$recipes" -name recipe.json | LC_ALL=C sort)
echo "==> Building, gating, and running the native proof offline"
EKKLESIA_CORRESPONDING_SOURCE_CACHE="$cache" EKKLESIA_CORRESPONDING_SOURCE_REQUIRED=1 \
pnpm --filter @ekklesia/module-pack exec vitest run tests/corresponding-source-native.test.ts