# `BoundedAuthorityProtocol.V1`
[🔗](https://github.com/baselabs/bounded_authority_protocol/blob/v1.0.0/lib/bounded_authority_protocol/v1.ex#L1)

Explicit entry point for the immutable v1 wire profile (contract-major 1, suite
`BAP1-Ed25519-SHA256`, normative in `spec/bap-v1.md`).

The functions fall into three groups:

  * **Producers** build the exact bytes a signer signs and assemble the compact form from a
    signature the caller made elsewhere: `grant_signing_input/2`, `proof_signing_input/2`,
    `boundary_anchor_signing_input/2`, `key_transition_signing_input/2`,
    `assemble_compact/2` and `assemble_compact/3`, `encode_consumption_entry/2`,
    `encode_anchored_export/2`, and `request_digest/3`. None of them takes a private key.
  * **Decoders** read a compact value without checking any signature, for diagnosis only:
    `decode_grant/2`, `decode_proof/2`, and `untrusted_key_locator/2`.
  * **Verifiers** check raw bytes against caller-supplied trusted public keys, expected
    context, time, and limits: `verify_grant/3`, `check_envelope/2`, `check_chain/2`,
    `verify_historical_anchor/3`, `verify_key_transition/4`, and `verify_anchored_export/3`.

## Return values

Every function returns `{:ok, value}` or exactly `{:error, :invalid}`. The error carries no
reason, on purpose: a wrong key, a changed byte, an expired window, a mismatched expectation,
and an over-limit input all look the same. Functions never raise on bad input.

Verified results are facts structs (`GrantFacts`, `EnvelopeFacts`, `ChainFacts`,
`AnchorFacts`, `KeyTransitionFacts`, `AnchoredExportFacts`). They hold identifiers, digests,
and times, never raw signatures, keys, nonces, or arguments, and they render as
`#Module<redacted>`. `GrantFacts`, `EnvelopeFacts`, and `AnchoredExportFacts` carry
`authorization: :not_evaluated`; `ChainFacts`, `AnchorFacts`, `KeyTransitionFacts`, and
`AnchoredExportFacts` carry `trust: :not_evaluated`. A facts value is
never an authorization decision.

## Limits

`limits` is a map of tightening overrides (or a `BoundedAuthorityProtocol.V1.Bounds`
struct); `%{}` means the profile maxima. See `BoundedAuthorityProtocol.V1.Bounds.coerce/1`.

## Example

The getting-started guide and the README quickstart build a grant and a proof with these
producers and verify them with `check_envelope/2`; the repository's test suite runs both.

# `assemble_compact`

```elixir
@spec assemble_compact(BoundedAuthorityProtocol.V1.SigningInput.t(), binary()) ::
  {:ok, binary()} | {:error, :invalid}
```

Assembles the compact JWS from a signing input and a raw 64-byte Ed25519 signature, at the
profile maxima. Same as `assemble_compact/3` with `%{}` limits.

# `assemble_compact`

```elixir
@spec assemble_compact(
  BoundedAuthorityProtocol.V1.SigningInput.t(),
  binary(),
  BoundedAuthorityProtocol.V1.Bounds.t() | map()
) :: {:ok, binary()} | {:error, :invalid}
```

Assembles the compact JWS from a `BoundedAuthorityProtocol.V1.SigningInput` and a raw
64-byte Ed25519 signature under caller `limits`.

The assembled value is re-parsed as its kind (grant, proof, boundary anchor, or key
transition) before it is returned as `{:ok, compact}`. The signature is not verified here:
verification happens in `verify_grant/3`, `check_envelope/2`, and the evidence verifiers.
Returns `{:error, :invalid}` for a wrong signature width, a signing input that does not
re-parse, or an over-limit result.

Since 0.9.0, assembly also refuses, with `{:error, :invalid}`, a
signature that no conforming signer emits: an Ed25519 signature whose `S` is not below the group
order or whose `R` is non-canonical or of small order (erratum 3). It never rewrites the
signature bytes it accepts.

# `boundary_anchor_signing_input`

```elixir
@spec boundary_anchor_signing_input(
  BoundedAuthorityProtocol.V1.BoundaryAnchor.t(),
  BoundedAuthorityProtocol.V1.Bounds.t() | map()
) :: {:ok, BoundedAuthorityProtocol.V1.SigningInput.t()} | {:error, :invalid}
```

Builds the deterministic boundary-anchor signing input from a
`BoundedAuthorityProtocol.V1.BoundaryAnchor`.

Returns `{:ok, %BoundedAuthorityProtocol.V1.SigningInput{}}` or `{:error, :invalid}`.

# `encode_anchored_export`

```elixir
@spec encode_anchored_export(
  BoundedAuthorityProtocol.V1.AnchoredExportInput.t(),
  BoundedAuthorityProtocol.V1.ExpectedExport.t()
) ::
  {:ok, BoundedAuthorityProtocol.V1.EncodedAnchoredExport.t()}
  | {:error, :invalid}
```

Frames an anchored export from a `BoundedAuthorityProtocol.V1.AnchoredExportInput` after
checking it against the caller's `BoundedAuthorityProtocol.V1.ExpectedExport`.

Returns `{:ok, %BoundedAuthorityProtocol.V1.EncodedAnchoredExport{chunks: chunks, digest: digest,
byte_count: count}}` or `{:error, :invalid}`.

Since 0.9.0, encoding also refuses, with `{:error, :invalid}`, a
signature that no conforming signer emits: an Ed25519 signature whose `S` is not below the group
order or whose `R` is non-canonical or of small order (erratum 3). It never rewrites the
signature bytes it accepts.

# `encode_consumption_entry`

```elixir
@spec encode_consumption_entry(
  BoundedAuthorityProtocol.V1.ConsumptionEntry.t(),
  BoundedAuthorityProtocol.V1.Bounds.t() | map()
) ::
  {:ok, BoundedAuthorityProtocol.V1.EncodedConsumptionEntry.t()}
  | {:error, :invalid}
```

Encodes one consumption-chain row from a `BoundedAuthorityProtocol.V1.ConsumptionEntry`.

Returns `{:ok, %BoundedAuthorityProtocol.V1.EncodedConsumptionEntry{bytes: row, hash: hash}}`
with the exact canonical row bytes and their domain-separated hash, or `{:error, :invalid}`.

# `grant_signing_input`

```elixir
@spec grant_signing_input(
  BoundedAuthorityProtocol.V1.Grant.t(),
  BoundedAuthorityProtocol.V1.Bounds.t() | map()
) :: {:ok, BoundedAuthorityProtocol.V1.SigningInput.t()} | {:error, :invalid}
```

Builds the deterministic grant signing input from a `BoundedAuthorityProtocol.V1.Grant`.

Returns `{:ok, %BoundedAuthorityProtocol.V1.SigningInput{}}`; sign its `message` with the
issuer key, then call `assemble_compact/2`. Returns `{:error, :invalid}` when a field is out
of profile (for example an empty audience list, an expiry not after the start, or a selector
outside the closed set) or over `limits`.

# `key_transition_signing_input`

```elixir
@spec key_transition_signing_input(
  BoundedAuthorityProtocol.V1.KeyTransition.t(),
  BoundedAuthorityProtocol.V1.Bounds.t() | map()
) :: {:ok, BoundedAuthorityProtocol.V1.SigningInput.t()} | {:error, :invalid}
```

Builds the deterministic historical-key-transition signing input from a
`BoundedAuthorityProtocol.V1.KeyTransition`.

Returns `{:ok, %BoundedAuthorityProtocol.V1.SigningInput{}}` or `{:error, :invalid}`; both
keys must pass `BoundedAuthorityProtocol.Ed25519.acceptable_public_key?/1`.

# `proof_signing_input`

```elixir
@spec proof_signing_input(
  BoundedAuthorityProtocol.V1.Proof.t(),
  BoundedAuthorityProtocol.V1.Bounds.t() | map()
) :: {:ok, BoundedAuthorityProtocol.V1.SigningInput.t()} | {:error, :invalid}
```

Builds the deterministic holder-proof signing input from a `BoundedAuthorityProtocol.V1.Proof`.

The input binds the grant (by the digest of its exact compact bytes), the method, the
normalized target URI, the invocation id, the operation, the request digest of the typed
`cast_arguments`, the issue time, and the optional nonce. Returns
`{:ok, %BoundedAuthorityProtocol.V1.SigningInput{}}` or `{:error, :invalid}`; the holder
public key must pass `BoundedAuthorityProtocol.Ed25519.acceptable_public_key?/1`.

# `request_digest`

```elixir
@spec request_digest(
  binary(),
  BoundedAuthorityProtocol.V1.Json.value(),
  BoundedAuthorityProtocol.V1.Bounds.t() | map()
) :: {:ok, binary()} | {:error, :invalid}
```

Returns `{:ok, digest}`, the base64url SHA-256 request digest over the operation and the
typed `cast_arguments` (`BAP1-REQUEST` domain), or `{:error, :invalid}`.

Integer and float arguments stay distinct: `{:integer, 1}` and `{:float, 1.0}` digest
differently.

# `decode_grant`

```elixir
@spec decode_grant(binary(), BoundedAuthorityProtocol.V1.Bounds.t() | map()) ::
  {:ok, BoundedAuthorityProtocol.V1.DecodedGrant.t()} | {:error, :invalid}
```

Decodes a raw compact grant within `limits` without checking its signature.

Returns `{:ok, %BoundedAuthorityProtocol.V1.DecodedGrant{verification: :not_evaluated}}` or
`{:error, :invalid}`. Use it to diagnose a rejection, never to make a decision.

# `decode_proof`

```elixir
@spec decode_proof(binary(), BoundedAuthorityProtocol.V1.Bounds.t() | map()) ::
  {:ok, BoundedAuthorityProtocol.V1.DecodedProof.t()} | {:error, :invalid}
```

Decodes a raw compact holder proof within `limits` without checking its signature.

Returns `{:ok, %BoundedAuthorityProtocol.V1.DecodedProof{verification: :not_evaluated}}` or
`{:error, :invalid}`. Use it to diagnose a rejection, never to make a decision.

# `untrusted_key_locator`

```elixir
@spec untrusted_key_locator(binary(), BoundedAuthorityProtocol.V1.Bounds.t() | map()) ::
  {:ok, BoundedAuthorityProtocol.V1.KeyLocator.t()} | {:error, :invalid}
```

Parses only the protected grant header and returns its untrusted `kid` hint.

The complete compact input is bounded. Payload and signature segments are not decoded,
interpreted, or independently size-checked.

# `check_chain`

```elixir
@spec check_chain(
  BoundedAuthorityProtocol.V1.ChainInput.t(),
  BoundedAuthorityProtocol.V1.ExpectedChain.t()
) :: {:ok, BoundedAuthorityProtocol.V1.ChainFacts.t()} | {:error, :invalid}
```

Checks a nonempty range of raw consumption-chain rows (`BoundedAuthorityProtocol.V1.ChainInput`)
against the caller's predecessor, head, and chain identity
(`BoundedAuthorityProtocol.V1.ExpectedChain`).

Returns `{:ok, %BoundedAuthorityProtocol.V1.ChainFacts{}}` or `{:error, :invalid}`. A
consistent chain proves consistency only; it cannot show that a validly shortened history
omitted nothing (usage rule 11).

# `check_envelope`

```elixir
@spec check_envelope(
  BoundedAuthorityProtocol.V1.Credentials.t(),
  BoundedAuthorityProtocol.V1.ExpectedRequest.t()
) :: {:ok, BoundedAuthorityProtocol.V1.EnvelopeFacts.t()} | {:error, :invalid}
```

Verifies a raw grant and holder proof together.

`credentials` is a `BoundedAuthorityProtocol.V1.Credentials` with the exact grant and proof
compact values as received. `expected_request` is a `BoundedAuthorityProtocol.V1.ExpectedRequest`
with the trusted issuer, the expected issuer and audience, and the values your service
derived from the request: method, normalized target URI, invocation id, operation, typed
cast arguments, evaluation time, clock skew, proof maximum age (at most 300 seconds), and the
nonce mode (`:not_required` or `{:required, nonce}`).

The grant is re-verified, the proof signature is checked under the holder key the grant
names, every binding is compared, and the arguments must satisfy every selector of the
operation. Returns `{:ok, %BoundedAuthorityProtocol.V1.EnvelopeFacts{authorization: :not_evaluated}}`
or `{:error, :invalid}`. Replay reservation of the invocation id stays with the caller.

# `verify_anchored_export`

```elixir
@spec verify_anchored_export(
  BoundedAuthorityProtocol.V1.ArchivedObject.t(),
  BoundedAuthorityProtocol.V1.HistoricalKeyChain.t(),
  BoundedAuthorityProtocol.V1.ExpectedAnchoredExport.t()
) ::
  {:ok, BoundedAuthorityProtocol.V1.AnchoredExportFacts.t()}
  | {:error, :invalid}
```

Verifies one anchored export as a whole: the `BoundedAuthorityProtocol.V1.ArchivedObject`
bytes and object version, its chunks to exact end of file, its digest, its boundary anchors,
and the complete ordered `BoundedAuthorityProtocol.V1.HistoricalKeyChain`, against the
caller's `BoundedAuthorityProtocol.V1.ExpectedAnchoredExport`.

Returns `{:ok, %BoundedAuthorityProtocol.V1.AnchoredExportFacts{authorization: :not_evaluated}}`
or `{:error, :invalid}`. Supply the expectations from your own records, never from the
archive being verified (usage rule 10).

# `verify_grant`

```elixir
@spec verify_grant(
  binary(),
  BoundedAuthorityProtocol.V1.TrustedIssuer.t(),
  BoundedAuthorityProtocol.V1.ExpectedGrant.t()
) :: {:ok, BoundedAuthorityProtocol.V1.GrantFacts.t()} | {:error, :invalid}
```

Verifies one raw compact grant.

`trusted_issuer` is a `BoundedAuthorityProtocol.V1.TrustedIssuer` with the exact `kid` and
the raw 32-byte public key your service already trusts. `expected_grant` is a
`BoundedAuthorityProtocol.V1.ExpectedGrant` with the issuer, the audience, the evaluation
time, the clock skew (at most 60 seconds), and the limits.

Returns `{:ok, %BoundedAuthorityProtocol.V1.GrantFacts{authorization: :not_evaluated}}` or
`{:error, :invalid}`.

# `verify_historical_anchor`

```elixir
@spec verify_historical_anchor(
  binary(),
  BoundedAuthorityProtocol.V1.HistoricalPublicKey.t(),
  BoundedAuthorityProtocol.V1.ExpectedAnchor.t()
) :: {:ok, BoundedAuthorityProtocol.V1.AnchorFacts.t()} | {:error, :invalid}
```

Verifies one raw compact boundary anchor against one exact
`BoundedAuthorityProtocol.V1.HistoricalPublicKey` and the caller's
`BoundedAuthorityProtocol.V1.ExpectedAnchor`.

Returns `{:ok, %BoundedAuthorityProtocol.V1.AnchorFacts{trust: :not_evaluated}}` or
`{:error, :invalid}`.

# `verify_key_transition`

```elixir
@spec verify_key_transition(
  binary(),
  BoundedAuthorityProtocol.V1.HistoricalPublicKey.t(),
  BoundedAuthorityProtocol.V1.HistoricalPublicKey.t(),
  BoundedAuthorityProtocol.V1.ExpectedKeyTransition.t()
) ::
  {:ok, BoundedAuthorityProtocol.V1.KeyTransitionFacts.t()} | {:error, :invalid}
```

Verifies one raw compact historical-key transition, signed by `current_key`, that
authenticates `next_key` (both `BoundedAuthorityProtocol.V1.HistoricalPublicKey`), against
the caller's `BoundedAuthorityProtocol.V1.ExpectedKeyTransition`.

Returns `{:ok, %BoundedAuthorityProtocol.V1.KeyTransitionFacts{trust: :not_evaluated}}` or
`{:error, :invalid}`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
