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):
| Category | Functions |
|---|---|
| Producer | grant_signing_input/2, proof_signing_input/2, boundary_anchor_signing_input/2, key_transition_signing_input/2, assemble_compact/2, assemble_compact/3 |
| Decode / verify | untrusted_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_allowancesallowlist 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.