This is the implementation contract shared by build recipes, release tooling,
and workflows. The checked-in release.json is the release input source of truth.
release.json schema 1 contains native (url, rev, version, archive_url,
archive_sha256, udl_sha256, source_date_epoch), bindings_version,
uniffi_version, rust_version, features, default_features, and groups.
Each group maps to an array of exact, flat release asset filenames. Additional
tool/SDK/image pins live in tools and dependency locks. Review selected native
features, default features, and UniFFI compatibility whenever changing releases.
Groups are generated, linux-x86_64, linux-aarch64, android, darwin,
and jvm. generated produces generated-sources.tar with the Kotlin, Android
Kotlin, Python, Swift, and FFI header sources. Linux groups produce their Python
wheel and jvm-linux-ARCH.so for x86_64 or aarch64. Darwin produces
jvm-darwin-x86_64.dylib, jvm-darwin-aarch64.dylib, both Python wheels, and
LDKNodeFFI.xcframework.zip. Android and JVM each produce five unsigned primary
Maven files: binary, sources JAR, Javadoc JAR, POM, and module metadata.
Python asset names are ldk_node-VERSION-py3-none-PLATFORM.whl, with PLATFORM one of
manylinux_2_28_x86_64, manylinux_2_28_aarch64, macosx_12_0_x86_64,
macosx_12_0_arm64. Maven names use ldk-node-jvm-VERSION or
ldk-node-android-VERSION. Keep exact filenames in the manifest, not inferred globs.
scripts/build.sh --group GROUP --output DIR [--candidate DIR] builds a single
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. 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.
Rust workspace exposes release via nix run .#release -- COMMAND ....
prepare --repo OWNER/REPO: validate clean committed inputs, push the dated preparation branch, dispatch candidate CI, watch the run, download candidates, and record a resumable ledger; never publish final packages.inventory --dir DIR --recipe COMMIT --group GROUP [--candidate DIR] --output FILE: record exact files, sizes, hashes, group, and hash of release inputs plus dependency locks.candidate --dir DIR --recipe COMMIT --run-id ID --attempt N --output FILE: combine complete group inventories intocandidate.json; require all groups and identical inputs.verify-candidate ID MANIFEST_SHA256 [--group GROUP] [--output DIR]: authenticate the publiccandidate.jsonbytes against the supplied SHA-256, require recipe ancestry from officialmain,MAJOR.MINOR.x, or dated preparation branches, then check out the exact recipe in a temporary clone and independently rebuild the supported host groups. Continue after individual group failures. The caller's checkout remains unchanged. Write receipts under the result directory and report partial coverage explicitly.scripts/verify.shprovides the same command through the pinned Nix CLI.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 [--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 inout_of_band_groups, separately fromreceipts. 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-interactiveskips prompts and records no additional comparisons.--require-verificationrequires every group to have a receipt or confirmation.scripts/collect.shexposes the same options through pinned Nix tools.publish --collected FILE --candidate URL_OR_DIR --destination DEST --confirm CANDIDATE_ID [--allow-unverified]: local-only final publication of candidate bytes.--verified FILEremains an alias for old commands. Receipts are optional, but every group must have a receipt or out-of-band confirmation by default.--allow-unverifiedexplicitly 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 publicationverification_history. Each destination status references its zero-basedverification_attempt; retries never rewrite history. Preserve old globalverificationfields without attributing them to earlier destinations. Swift provenance contains candidate identity only, so recollection does not change the metadata commit. Existing provenance with a historicalverificationfield remains accepted without relaxing any candidate identity or artifact check. Old schema-1 ledgers withoutout_of_band_groupsremain valid. Destinations:testpypi,pypi,maven,swift. Reject CI environments.scripts/publish.shexposes the same options and requires the same explicit confirmation. Collection/publication wrappers preserve the caller's working directory and require it to be within the candidate's clean recipe checkout.
Candidate IDs: candidate-VERSION-RECIPE12-RUN_ID-ATTEMPT; non-SemVer, unique,
never overwrite. Candidates are PUBLIC GitHub prereleases (not drafts), with
flat files and candidate.json. Download with ordinary HTTPS without gh/PAT.
Build jobs are read-only; only candidate upload uses GitHub's temporary token.
All user PATs, registry tokens, and PGP signing keys remain on local machines.
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 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
Swift semantic version tags nor distribution metadata belong in the build
recipe repository.
Receipts have schema, candidate/input/recipe identity, group, file hashes/sizes, and imported JVM native dependency hashes. Receipt collection is a deliberate maintainer action, not automatic trust of public submissions. Transport hashes alone never count as independent reproduction. Logs/timestamps are outside the 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 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
and PGP key local; do not invoke legacy Gradle uploads to Central.