The rules a host follows around verification. Each is short; the troubleshooting checklist and the recipes show them in code.
- Treat verifier success as a cryptographic result, not an authorization decision.
- Supply only already-trusted public keys selected outside this library.
- Derive method, URI, operation, and cast arguments on the server; never trust caller-selected expected context.
- Enforce live revocation, replay reservation, execution claims, host policy, and effect authorization in a stateful authority runtime.
- Use the published bounds and reject unknown versions or extensions.
- Never pass private keys, secrets, raw user values, or production credentials into fixtures, logs, errors, or telemetry.
- Pin a package release line and run the packaged conformance corpora before accepting a new release.
- Treat
untrusted_key_locator/2output only as a case-sensitive lookup hint withtrust: :not_evaluated; it does not parse claims, verify bytes, select trust, or authorize. - Pass only raw compact credentials to verification boundaries. Decoded values and verified facts are evidence outputs, never reusable credentials.
- Supply chain/archive verification with the intended predecessor/head, both expected anchors, the complete ordered historical-key path, raw archive digest, and exact object-store version. Do not infer those expectations from the archive being verified.
- Treat chain consistency as consistency only. It cannot by itself prove that a validly shortened or relinked history omitted nothing.
- Keep commitment preimages private. The public row carries only a fixed-width commitment.
- Depend on the published Hex release (
{:bounded_authority_protocol, "~> 1.0"}), never on a source tag or a mutable checkout. Select one profile per boundary through its named namespace: the contract-major surfaces stay byte- and verdict-identical, and each sibling profile is reached only through its own API. - Run the verifier CLI with an explicit
--corpus DIRpointing at a packaged corpus: install it withmix escript.install hex bounded_authority_protocol 1.0.0and runbounded_authority_conformance --corpus deps/bounded_authority_protocol/priv/conformance/v1/corpus, or callBoundedAuthorityProtocol.Conformance.Cli.run/1from Elixir. Never rely on a default corpus path. Treat exit 0 as conformance evidence only; it does not authorize. - For local-loopback HTTP, admit only literal
127.0.0.1or[::1], require the server nonce, derive the exact target from the direct listener, and reject proxy/forwarding-header authority. Never retry the standard profile after a local-profile rejection or vice versa. - For content assertions, hash the exact retained content bytes with
content_digest/2and pass that digest, the pinned profile digest, every expected identifier, one caller-selected attestor key and window, andnowtoverify_assertion/2. Verified facts prove signature, context, and window only. Keep durable per-subject generation and digest state to refuse replay, regression, and forks;verify_successor/3compares two facts values and nothing more. - Call
BoundedAuthorityProtocol.Ed25519.torsion_free?/1on every Ed25519 key at enrollment, at trust install, and before any deny-list or revocation comparison, and reject a key that fails it. Verification applies the strict rule itself and accepts mixed-order keys by design; the torsion-free check is what makes one secret map to exactly one key and thumbprint. Scan keys you stored before adopting the package, and archived key chains, withacceptable_public_key?/1andtorsion_free?/1, and treat any failure as a compromise. - Hand compact assembly only a signature a conforming signer emits. For ES256 that is the raw
64-byte
r || swith low S: convert a DER signature from an HSM, KMS, or secret store withBoundedAuthorityProtocol.Es256.der_to_raw/1, thennormalize_low_s/1, before assembly. Assembly never normalizes; it refuses a high-S or out-of-range signature. - Tag each selector value by what it is: an integral number is
{:integer, n}, never{:float, n.0}. Producers refuse a float-tagged integral selector value on every contract-major, and verification rejects a float zero in anequalsvalue or aone_ofmember. - When verification returns
{:error, :invalid}, diagnose from your own inputs with the troubleshooting checklist. Never retry with loosened expectations or under another profile.