All notable changes to this project are documented here. The format is based on Keep a Changelog. Pre-1.0, 0.x minor bumps may carry breaking changes (SemVer §4).

[0.3.0] — 2026-08-26

Pre-1.0 feature release. The 1.0 cut is DELIBERATELY DEFERRED (owner direction, 2026-08-26) until the release sees real consumer use and the surface settles; the stability contract in upgrading.md stands as the PROPOSED freeze that 1.0 will publish, not an operative one. Everything the contract enumerates is in this release and pinned by the doc-currency tripwire: the four signing functions, the closed error sets, the key-handle behaviour, the value-free telemetry surface, the dependency posture — plus the gate battery, the package-boundary and reproducibility gates, the CI matrix, the guide set, the Igniter installer, the doctor preflight, and the supply-chain workflow.

Documentation

  • Reconcile the governing charter, strategy, contributor guide, and examples to the live release state: 0.2.1 and its source are public, it consumes BAP from Hex, and the separate BA runtime remains a private commercial application that is never public-Hex-published and is not privately Hex-distributed because no paid private-package subscription exists. A future private BA release requires an active subscription and fresh approval for that exact release and destination.
  • Remove private-consumer identifiers and deployment-specific topology from the tracked public surface and rewritten repository history; harden the hash-based gate over tracked files, commit messages, historical paths, every reachable commit snapshot, merge content, and annotated-tag messages.

Added

  • Supply-chain parity: a tag-push supply-chain workflow (exact archive built through the full gate battery, SHA256SUMS, SLSA provenance + CycloneDX SBOM attestations via actions/attest, in the SLSA build/attest split — the build job holds NO signing permissions (it runs third-party code); the attest job is minimal-permission and runs no project code; artifact names derived from mix.exs @version, never hardcoded) and the release-candidate reproducibility gate (scripts/check_reproducible.exs, the protocol sibling's pattern read first-hand: two fresh source-tree copies, independent deps.get + hex.build, byte-identical SHA-256 required) wired as a mix ci + CI battery step. RED-proven: a packaged source tampered between the two builds reds the gate (digests diverge, exit 1). SECURITY.md gains the consumer verification procedure (SHA256SUMS, gh attestation verify, hex.pm cross-check).

  • mix bounded_authority_report_adapter.doctor --handle MyApp.HolderKey [--ref term] [--live] — the handle-contract preflight (read/probe only, no side effects): FATAL on an unloaded module, a missing required callback (sign/2, public_key/1, thumbprint/1), or a public_key/1 that does not return a 32-byte key for the ref; ADVISORY on the operation-blocking optional callbacks (key_identity/1 absent blocks anchors/transitions, signing_identity/1 absent blocks grants) and, with --live, a wrong-key probe that signs a DOCTOR-GENERATED synthetic message and runs the adapter's own verify guard before the first real signing call. Every fatal check is RED-proven by a scratch module missing exactly that thing. Plus an invocation-id guidance section in getting-started (the UUID grammar gate and the fails-far-from-the-cause trap).

  • Igniter installer: mix bounded_authority_report_adapter.install --module MyApp.HolderKey scaffolds a starter key-handle (all five callbacks present so the behaviour compiles clean; every body raises until you wire your custody store — nothing key-shaped is ever uncommented FOR you), imports the dep into the consumer's .formatter.exs, and carries the which-callback-matters-for-which-operation contract as comments. :igniter (~> 0.8) is an OPTIONAL dependency (unscoped, so consumer builds order it before this package and the real task compiles; without it the file compiles to a fallback task that raises with the one-line remedy). Smoke-verified end-to-end in a scratch consumer: install → wire a dev key → sign_report → check_envelope green.

  • Governance surface: CONTRIBUTING.md (the per-file floor, the surgical-pathspec discipline, red-first + mutation-proof expectations, the bump policy pointer), CODE_OF_CONDUCT.md (Contributor Covenant v2.1, the canonical text with the reporting contact substituted), docs/upgrading.md (per-version notes newest-first + the 1.0 stability contract: the public surface enumerated — the four signing functions and their return shapes, the four closed error sets, the key-handle behaviour, the telemetry surface, the dependency posture — with reserved break-rights and the frozen-wire-formats statement), and an enriched SECURITY.md (security invariants restated from the charter for reporting, a report-content checklist, acknowledgment ≤7 days + weekly status as a best-effort cadence, not an SLA). All four shipped in the package and extras.

  • Recipes and a security model, shipped in the package and indexed: compile-paste-verified integration shapes for a network-HSM handle (with the timeout-to-safe_callback exit-catch posture), a KMS handle (why key_identity/1 must be ONE atomic version snapshot), and a condensed Plug consumer; plus the porting note for non-Elixir holders (the spec's signing-input and compact sections by durable identity, the conformance corpus as the oracle — closes the "signing beyond Elixir" residual as documentation, not a second-language SDK). The security doc's named misuses each state the consequence: caller-supplied key ids, skipped identity binding, no nonce ledger, trusting the C1 declaration as role separation, value-carrying telemetry, and shipping the test-support handle.

  • Doc-currency tripwires (test/docs_currency_test.exs) + the regression-pinned Livebook: the round-trip notebook now commits its expected cell outputs (persist_outputs; verdicts and counts pinned — volatile bytes like thumbprints and sizes deliberately excluded and annotated). The tripwire suite diffs docs/errors.md's atoms against the lib's error @types both directions (generic scans — a future atom the docs don't cover reds), resolves every backticked fun/arity in usage-rules.md against the adapter's exports and behaviour callbacks (arity-exact), checks the README Documentation index against the ex_doc extras both directions, and pins the getting-started dep requirement to the current version. Mutation-proven: a deleted atom row, a renamed function, a novel lib atom, a callback-arity drift, and an index-only link each red the suite. On landing, the identifier pass surfaced a doc inconsistency (usage-rules said V1.Json.decode/1; the shipped call shape is /2) — fixed in both guides.

  • Core DX guides, shipped in the package and indexed from the README: usage-rules.md (12 flat rules, exact identifiers, reasons folded in — the C1-gate-is-declaration-rejection rule leads), docs/errors.md (all four closed-atom error sets as atom → meaning → what to check → recovery, authored to be mechanically checkable against the @types), and docs/getting-started.md (first sign in minutes with a self-contained dev handle, then the path to a production handle).

  • Value-free sign telemetry: the four signing entry points now emit a closed two-event surface ([:bounded_authority_report_adapter, :sign, :start|:stop]) with monotonic duration and atoms-only metadata (object in [:report, :anchor, :grant, :key_transition]; result_class in [:ok, :invalid_input, :invalid_key_handle, :signing_failed, :producer_error] — never key ids, message bytes, report content, or error values). The emitters are shape-validated (off-shape emissions are refused with {:error, :telemetry_invalid} instead of emitted — the mechanical value-free guarantee, tripwire-proven by planting a key id into the metadata and watching the stop event vanish), telemetry never alters a signer's return, and a raise inside the signer still propagates. docs/telemetry.md documents the event/class tables, the alerting guidance, and the attach example; a docs-parity test diffs the tables against the emitter's closed sets. Adds :telemetry (~> 1.3, zero transitive deps) as the first runtime dependency besides the protocol package itself.

  • Library gate battery, parity-pinned between mix ci and the CI workflow's gate job: a coverage floor (mix test --cover, threshold pinned one display-hundredth under the measured 76.79% — Mix compares the raw ratio, so 76.78 is the tightest flake-free pin), dialyzer (PLT + analysis under :test so test/support/ is analyzed), doc warnings (mix docs --warnings-as-errors), and the library's own dependency audits (mix hex.audit + mix deps.audit via mix_audit, both dev/test-only). Each gate is mutation-proven red-capable, and the CI advisory parity test now pins the full battery step order in both orchestration surfaces — dropping any one battery step reds parity.

  • CI compatibility matrix: both workflow jobs now run a three-cell Elixir/OTP matrix (1.18.4/27.3.4.14, 1.19.5/28.5.0.3, 1.20.2/29.0.3 — the protocol sibling's proven lanes, fail-fast: false), with every cell running the identical battery. mix ci stays pinned to the local asdf lane and is documented as such in the workflow. The parity test pins the exact cells per job and that setup-beam consumes the matrix variables — a dropped lane reds (a lane that never runs looks green by absence). Validated with actionlint only (Actions billing-blocked; the limitation is stated in the commit). The dead git-pin-era "configure git for private deps" steps were already removed with the gate battery.

  • scripts/check_package.exs — the shipped-artifact gate (pattern: the protocol sibling's package check, adapted): builds the exact Hex archive, unpacks it, asserts the payload census against the expected file set exactly in BOTH directions (a stale files: allowlist and an accidental inclusion both red), pins the outer metadata (version and requirement read from the live project config, so the pin never drifts on a bump), compiles the unpacked package in :prod, and compiles + smoke-runs a minimal consumer against the UNPACKED artifact — a self-implemented key handle, a sign_report → check_envelope positive, and a tampered-request negative. Mutation-proven: dropping a shipped file from the allowlist reds the census; a corrupted consumer reds its compile. Runs as a mix ci + workflow gate step; scratch-cleaned per run.

  • Guard against silent protocol-version drift: the dependency-direction wall now pins the resolved mix.lock version (mutation-proven, including the 0.1.20 prefix-extension case), an exact identity invariant ties the wall's requirement floor to its locked-version attribute, and the edge-agent example asserts its lock resolves the protocol at the same version as the library's (a split between them would put the two CI jobs on different protocol spans).

  • scripts/check-bap-drift.sh — a read-only, one-command ecosystem drift check (locked version vs hex.pm releases vs the authority runtime's pin vs protocol main, with the release span's lib/ delta classified). A probe for audit and bump sessions, not a gate.

Changed

  • Fixed every Ed25519 signing call site to :crypto.sign/4's documented contract — :eddsa with digest :none and the curve in the key list. The previous form passed the curve (:ed25519) in the digest slot, which the runtime tolerates but OTP 29's typespec rejects — the new dialyzer gate surfaced it as 35 findings across the test-support fixtures and the edge example (zero in lib/). Signatures are byte-identical under both forms (probed first: both verify), and the full suite is green after the sweep. The one intentional contract-violating fixture keeps its violation under a narrowly-scoped @dialyzer annotation.
  • Removed the vestigial "configure git for private deps" CI steps and comments from both workflow jobs: the protocol package has been consumed from public Hex since 0.2.0 and neither project lock carries a git dependency.
  • Re-align the protocol dependency to bounded_authority_protocol 0.1.2 (~> 0.1.2), the authority runtime's pin (ADR-0010 Decision 3 re-alignment, not a BARA-ahead bump). The release span's lib/ delta is conformance tooling only (conformance/{cli,report}.ex — zero V1.* change to the consumed surface), priv/conformance is unchanged, and the protocol package's own mix.exs carries a version-line-only change. The wall's requirement + locked-version attributes, both project locks, and the drift-guard mutation fixtures (plain drift 0.1.3, prefix extension 0.1.20) moved in the same commit.
  • ADR-0010 amended with a Hex-era mapping (Decision 6): the substrate moved to Hex consumption on 2026-08-20; the decision records how each policy term (BA's pin, the lib/-empty gate, the same-commit bump discipline, RA2-at-version) maps onto release tags and the now-mechanical wall clauses. ADR-0013's one-vector scope reaffirmed.
  • Independent full-range review hardened the drift probe to withhold release verdicts on a malformed Hex response and to select only BA's :bounded_authority_protocol ref; both paths now have executable regressions. The review also replaced the compatible-version and duplicated-predicate guard checks with exact shared predicates, each tamper-proven RED.

Security

  • The edge example now fails both mix ci and its GitHub job on Hex advisories in its own lock. Bandit moved from vulnerable 1.12.4 to 1.12.5, which fixes HIGH GHSA-xj8g-532w-jv94 and MEDIUM GHSA-x3gh-xhj4-3vq8. The two orchestration commands are independently mutation-proven by the parity test.

[0.2.1] — 2026-08-20

Fixed

  • Reference the repository's examples/ (Livebook demo and edge-agent app) in prose rather than by relative link, since examples/ is not shipped in the package — removes the dangling documentation links from the published README and consumer-integration guide.

[0.2.0] — 2026-08-20

First public release on Hex.

Added

  • Holder-side companion signer for the Bounded Authority Protocol, with four instantiations of one shared signing tail: sign_report/3 (holder proof), sign_anchor/3 (boundary anchor), sign_key_transition/3 (key transition), and sign_grant/3 (grant, structurally gated to an issuer-role handle so a holder cannot mint a capability).
  • Key-handle custody contract: the private key never enters the library; callers pass a {module, ref} handle implementing the signing callbacks against their own key store, and every sign path verifies its output against the public key before returning.
  • Conformance round-trip against the protocol package's published oracle vectors, and a dependency-direction wall proving the adapter depends only on bounded_authority_protocol.
  • A runnable edge-agent example and a self-contained Livebook demo (issuer → holder → verifier).

Changed

  • Consume bounded_authority_protocol from its Hex release (~> 0.1.1) rather than a source dependency.

[0.1.0]

Internal genesis of the companion signer and its four signing instantiations, developed against a source dependency on the protocol package prior to the protocol's first Hex release.