Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/candidate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,9 @@ jobs:

./scripts/verify.sh $candidate_id $manifest_sha256 --repo "$GITHUB_REPOSITORY"

Checksums record transport integrity; independent receipts are
required before a maintainer can publish these bytes locally.
Checksums record transport integrity. Maintainers may record optional
rebuild receipts or confirm comparisons performed out-of-band during
collection before explicitly publishing these bytes locally.

Build: https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID
EOF
Expand Down
54 changes: 36 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ distributions of [LDK Node](https://github.com/lightningdevkit/ldk-node).

GitHub runners build unsigned candidates. Anyone can download those candidates,
rebuild their supported targets, and compare the exact package bytes without
GitHub credentials. A maintainer publishes the verified files through a separate
local command. Registry credentials, signing keys, and personal access tokens
stay on the maintainer's machine.
GitHub credentials. A maintainer collects and publishes the CI-built files through
separate local commands. Rebuild receipts are optional; collection can record
comparisons performed on other hosts without copying their receipts. Registry
credentials, signing keys, and personal access tokens stay on the maintainer's
machine.

`release.json` selects the native revision, bindings version, and toolchain.
Rust source, the authoritative UDL, native tags, and crates.io publication remain
Expand Down Expand Up @@ -77,8 +79,8 @@ output belongs in disposable directories under `/tmp`.
| `jvm` | Linux x86_64 or aarch64 | JAR with all four native libraries and unsigned Maven files |

JVM assembly takes `--candidate DIR` containing the native outputs. Assembly
verification records their hashes; the complete release also needs independent
verification of the Linux and Darwin groups. Python wheels support Python 3.10
verification records their hashes; independent verification of those payloads
belongs to the Linux and Darwin groups. Python wheels support Python 3.10
and newer on Linux x86_64/aarch64 and macOS x86_64/arm64.

## Prepare a candidate
Expand Down Expand Up @@ -216,19 +218,27 @@ and writes a receipt identifying the recipe, inputs, group, files, and hashes.
Verifying a downloaded checksum alone is transport validation; it does not
establish that a build reproduced the candidate.

Send the small receipts to the publishing maintainer. After deliberately
selecting the receipts to trust, collect complete coverage:
Rebuild receipts can be sent to the publishing maintainer, but copying them is
optional. A maintainer can instead observe `VERIFICATION SUCCESSFUL` on each
host or compare independently rebuilt hashes. Collection asks y/n for every
group without a supplied receipt and records yes answers as out-of-band
comparisons. No or blank leaves a group unconfirmed; collection still succeeds.

Collect the candidate, optionally supplying a directory of trusted receipts:

```bash
./scripts/collect.sh --candidate "$candidate_url" \
--receipts /tmp/accepted-receipts --output /tmp/verified-bindings.json
--output /tmp/collected-bindings.json
```

The collector checks the actual candidate files against those receipts. Missing
targets, incompatible receipt hosts, different recipes, altered files, or unverified JVM dependencies prevent
Add `--receipts /tmp/accepted-receipts` to import available receipts. Use
`--non-interactive` to skip prompts or `--require-verification` to require every
group to have a receipt or an out-of-band confirmation. These options never
skip artifact hashing or validation of supplied evidence. Incompatible receipt
hosts, different recipes, altered files, or mismatched JVM imports prevent
collection. See [release operation](docs/releasing.md) for the local publication
commands and recovery rules. Explicit local Swift publication sends the
verified generated wrapper, `Package.swift`, licenses, provenance, and unchanged
generated wrapper, `Package.swift`, licenses, provenance, and unchanged
XCFramework ZIP to `lightningdevkit/ldk-node-swift`; its version tag belongs to
that distribution repository.

Expand All @@ -241,34 +251,42 @@ the pinned Nix release tool and accept its existing options; they preserve
relative paths and do not select a different recipe checkout. Run either with
`--help` for the full interface.

After collecting complete verification coverage, explicitly publish one
destination with credentials supplied through your local environment or
After collecting the candidate and reviewing its recorded coverage, explicitly
publish one destination with credentials supplied through your local environment or
credential store:

```bash
candidate_id=CANDIDATE_ID
candidate_url="https://github.com/lightningdevkit/ldk-node-bindings/releases/tag/$candidate_id"
./scripts/publish.sh --candidate "$candidate_url" \
--verified /tmp/verified-bindings.json --destination maven \
--collected /tmp/collected-bindings.json --destination maven \
--confirm "$candidate_id"
```

Choose `testpypi`, `pypi`, `maven`, or `swift`. PyPI requires the identical wheels
to pass the TestPyPI gate first. Swift defaults to `lightningdevkit/ldk-node-swift`;
`--repo` changes that publication destination. The candidate URL independently
selects the build repository, including a test repository. Publication requires
the verified ledger and exact candidate confirmation, rejects CI, and reuses the
the collection ledger and exact candidate confirmation, rejects CI, and reuses the
existing publication record when resumed. See [the release guide](docs/releasing.md)
for each destination's credentials, qualification requirements, and recovery.
Publication requires every group to have a receipt or an out-of-band confirmation
by default and rechecks all artifact bytes. To deliberately publish with missing
comparisons, pass `--allow-unverified` to that publication command. Each attempt
records its coverage and override in publication history. `--require-verification`
explicitly selects the default and conflicts with the override. `--verified`
remains an alias for `--collected` for existing commands.

## Verification status

Pinned environments and normalized archives are the basis for reproducible
builds; they do not prove equality by themselves. Every platform requires a
successful CI build and independent local comparison before production use.
successful CI build; independent comparisons can be performed on any supported
host and confirmed during collection. Missing receipts are not proof that a
comparison did not occur, and checksums alone do not prove independent rebuilding.
See [qualification evidence](docs/qualification.md) for checks actually run and
outstanding platform validation. macOS recipes are supported but still require
execution and comparison on the selected Apple host before publication.
outstanding platform validation. macOS builds and native verification require
the selected Apple host.
Development checks alone do not imply
that a release has been built, independently reproduced, or published.

Expand Down
38 changes: 29 additions & 9 deletions docs/interfaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ group from committed inputs into an empty output directory. It runs the same
Nix derivations or pinned external recipes in CI and locally. No credentials,
signing, publication, source mutation, or dependency updates occur here.
`--candidate` is required only for JVM assembly, supplying the four named JVM
native assets. Acceptance of this assembly for final publication requires their
independent verification; CI can assemble the initial candidate beforehand.
native assets. Reproducing this assembly establishes assembly equality; the
imported libraries have their own Linux and Darwin verification groups.
Generated sources and all project intermediate outputs must rebuild locally
during verification, even if already present in a Nix store or build cache.

Expand Down Expand Up @@ -61,12 +61,32 @@ Rust workspace exposes `release` via `nix run .#release -- COMMAND ...`.
- `verify --candidate URL_OR_DIR --group GROUP --output DIR`: anonymous download,
independent rebuild, byte comparison, JSON receipt, and explicit partial
coverage. Require the local clean recipe commit and input identity to match.
- `collect --candidate URL_OR_DIR --receipts DIR --output FILE`: rehash actual
candidate files, require all groups and accepted independent receipts,
validate receipt hosts and JVM native dependencies, emit a verified ledger.
- `collect --candidate URL_OR_DIR [--receipts DIR] --output FILE
[--non-interactive] [--require-verification]`: rehash all candidate files,
validate supplied receipt identities, hosts, files, and JVM imports, then
prompt y/n for every group without a receipt. Store affirmative answers in
`out_of_band_groups`, separately from `receipts`. No or blank answers leave
the group unconfirmed; EOF fails instead of inventing an answer. Emit a
collection ledger bound to the candidate manifest hash and recipe.
`--non-interactive` skips prompts and records no additional comparisons.
`--require-verification` requires every group to have a receipt or confirmation.
`scripts/collect.sh` exposes the same options through pinned Nix tools.
- `publish --verified FILE --candidate URL_OR_DIR --destination DEST
--confirm CANDIDATE_ID`: local-only final publication of verified bytes.
- `publish --collected FILE --candidate URL_OR_DIR --destination DEST
--confirm CANDIDATE_ID [--allow-unverified]`: local-only final publication
of candidate bytes. `--verified FILE` remains an alias for old commands.
Receipts are optional, but every group must have a receipt or out-of-band
confirmation by default. `--allow-unverified` explicitly permits missing
coverage for this invocation; it conflicts with `--require-verification`,
which selects the default. Validate each confirmation group and reject
duplicate/foreign claims even with the override. Before accessing a destination,
append its receipt, out-of-band, and unconfirmed groups and override choice to
publication `verification_history`. Each destination status references its
zero-based `verification_attempt`; retries never rewrite history. Preserve old
global `verification` fields without attributing them to earlier destinations.
Swift provenance contains candidate identity only, so recollection does not
change the metadata commit. Existing provenance with a historical `verification`
field remains accepted without relaxing any candidate identity or artifact check.
Old schema-1 ledgers without `out_of_band_groups` remain valid.
Destinations: `testpypi`, `pypi`, `maven`, `swift`. Reject CI environments.
`scripts/publish.sh` exposes the same options and requires the same explicit
confirmation. Collection/publication wrappers preserve the caller's working
Expand All @@ -82,7 +102,7 @@ The initial build/candidate repository is
`https://github.com/lightningdevkit/ldk-node-bindings`. SwiftPM distribution is
published separately to `https://github.com/lightningdevkit/ldk-node-swift`.
Its version commits contain the generated Swift wrapper, `Package.swift`,
licenses, and provenance; its release assets contain the unchanged verified
licenses, and provenance; its release assets contain the unchanged candidate
XCFramework ZIP. The package manifest points at that ZIP with its SHA-256.
Keep the distribution Git URL and asset destination configurable for a possible
Forgejo migration. The initial publication adapter supports GitHub; neither
Expand All @@ -97,7 +117,7 @@ reproducible payloads. Final uploads rehash files again; publication records
support checking remote state and resuming without overwriting versions.

Maven publication uses the Central Portal Publisher API from the local CLI.
Sign the verified unsigned artifacts locally, assemble the repository-layout
Sign the hash-checked unsigned candidate artifacts locally, assemble the repository-layout
bundle, upload with `publishingType=USER_MANAGED`, persist the deployment ID,
and poll validation. The explicitly confirmed publish invocation may then
publish a validated deployment and poll its final state. Keep the Portal token
Expand Down
15 changes: 9 additions & 6 deletions docs/qualification.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,16 @@ the candidate AAR and its native payloads are unchanged.

## Remaining release qualification

Before production publication, independently reproduce every group on its
required host. The current candidate still needs native Linux x86_64, Linux
Independent reproduction of every group provides complete reproducibility
coverage. The test candidate above still lacks native Linux x86_64, Linux
aarch64, and Darwin receipts from outside GitHub; its other groups have local
reproduction evidence. Exercise consumer installation separately from checksum
comparison. Live TestPyPI staging and Maven publication have not been exercised;
registry credentials and namespace ownership remain unqualified. TestPyPI
staging must pass before PyPI publication.
reproduction evidence. The release workflow now permits optional receipts and
records out-of-band confirmations during collection. Publication requires
complete recorded coverage by default; proceeding with missing comparisons
requires an explicit `--allow-unverified` choice. Exercise consumer installation
separately from checksum comparison. Live TestPyPI staging and Maven publication
have not been exercised; registry credentials and namespace ownership remain
unqualified. TestPyPI staging must pass before PyPI publication.

Repeat Android consumer qualification for each candidate, including a 16 KiB
device and API 21–23 devices with the documented desugaring configuration.
Expand Down
Loading