Release-candidate contract

Copy Markdown View Source

This document is the candidate-facing contract for the 0.1.0 package. It states the locked public API surface, the versioning posture, and the candidate-verification recipe a reviewer follows to compare a local candidate against a CI-attested candidate. It is evidence a human reviewer compares, not an authority shape — there is no decision, receipt, or authorization here (per AGENTS.md rule 1, "verification is not authority ... there is no allowed?, authorized?, decision, or receipt"). The decision record is ADR 0008.

Status

0.1.0 is the published first release — the exact reviewed candidate. BAP-06 froze the candidate; BAP-07 (connected verification and first public release) executed 2026-08-20 by owner decision: the exact candidate passed the private runtime's PostgreSQL 18 gate and the consumer connected gates, the fresh correctness/security/gate-integrity/cross-vendor review set closed, and the same archive published to Hex. Consumption uses the Hex release; the v0.1.0 git tag marks the published commit.

Locked public API surface (0.1.0)

The locked API is the set of modules and functions a consumer depends on. It is enforced mechanically and enumerated here for human readers + SemVer review.

Mechanical enforcement: tools/architecture_gate.exs @compiled_export_allowances pins the exact compiled exports per .beam for the dominant contract modules (the V1 facade, V1.Runtime, the codecs, the public structs) — any export addition, removal, or arity change to those turns mix architecture red. The named decoder/bounds submodules (V1.Json, V1.Base64Url, V1.Bounds) are enforced under @compiled_dynamic_allowances (dynamic-call-count): removal/rename/arity change of their existing functions surfaces via the dynamic-call check plus the unpacked-consumer gate. The V1.beam facade pin is the authoritative full-arity lock for the dominant surface; the enumeration below lists primary arities.

BoundedAuthorityProtocol.V1 facade (lib/bounded_authority_protocol/v1.ex):

CategoryFunctions
Producergrant_signing_input/2, proof_signing_input/2, boundary_anchor_signing_input/2, key_transition_signing_input/2, assemble_compact/2, assemble_compact/3
Decode / verifyuntrusted_key_locator/2, decode_grant/2, decode_proof/2, verify_grant/3, check_envelope/2, request_digest/3, encode_consumption_entry/2, check_chain/2, encode_anchored_export/2, verify_historical_anchor/3, verify_key_transition/4, verify_anchored_export/3

Named submodules: BoundedAuthorityProtocol.V1.Json.decode/2, BoundedAuthorityProtocol.V1.Base64Url.decode/2 (the only decoder façade, per ADR 0002), and BoundedAuthorityProtocol.V1.Bounds.new/1 / Bounds.maximum/0 (the tightening-only bounds constructor assemble_compact/3 consumes).

Public structs: GrantFacts, EnvelopeFacts, ChainFacts, AnchorFacts, KeyTransitionFacts, AnchoredExportFacts, SigningInput (the producer functions' return and assemble_compact's first argument), plus the Expected* / Historical* / input structs a consumer builds, and BoundedAuthorityProtocol.V1.Bounds (the tightening-only bounds constructor).

Anything else in lib/ is internal.

assemble_compact/3 is the additive bounds-aware form accepted on 2026-08-19 under ADR 0020. /2 remains the profile-maximum default and is byte-identical to /3 with %{} or Bounds.maximum().

Versioning posture

0.1.0 is the first release-candidate version. Under pre-1.0 SemVer (SemVer §4), the 0.x.y line reserves the right to break compatibility until 1.0.0. The API lock above is the commitment that the enumerated surface is the intended 1.0.0 surface; breaking changes before 1.0.0 land as 0.x.0 version bumps with a CHANGELOG entry, never silently.

  • Removal, rename, signature change, or a new REQUIRED argument to a locked function: a major change (0.x→0.(x+1) before 1.0.0), requiring an @compiled_export_allowances allowlist edit in the same commit.
  • A new optional argument or a new additive function: a minor change (0.x.y→0.x.(y+1)), requiring an allowlist extension.
  • 1.0.0 (first stable) is a future decision; BAP-06 does not declare stability.

Candidate verification

Every trusted-main CI build produces an unpublished Hex archive, a SHA-256 checksum, release and tooling CycloneDX documents, and separate GitHub build-provenance and SBOM attestations. A reviewer verifies a candidate against the CI-attested record with:

sha256sum --check SHA256SUMS
gh attestation verify bounded_authority_protocol-0.1.0.tar \
  --repo baselabs/bounded_authority_protocol \
  --signer-workflow baselabs/bounded_authority_protocol/.github/workflows/supply-chain.yml \
  --source-ref refs/heads/main \
  --deny-self-hosted-runners

These are not releases, do not grant authority, and do not replace the connected release gates in BAP-07. The full security policy, including vulnerability reporting, is in SECURITY.md.

Reproducibility

The candidate archive is checked for reproducibility on every mix quality run (local, CI, and supply-chain) via the release.candidate gate. The gate copies the source tree into two fresh, cache-isolated build roots (no _build, no deps), builds the archive in each, and asserts byte-identical SHA-256. A green run prints the candidate archive SHA-256 — the same yardstick SHA256SUMS uses — so a local candidate and a CI-attested candidate at the same commit compare on the same basis.

mix release.candidate

The gate's claim is "two independent builds agree," scoped as regression detection: it catches the moment a future change introduces a non-deterministic packaged input. It does not assert the build is reproducible from a shared-cache self-comparison. See ADR 0008 for the scope and the rejected alternatives.