# `MetamorphicLog.Policy`
[🔗](https://github.com/moss-piglet/metamorphic_log/blob/main/lib/metamorphic_log/policy.ex#L1)

Signed **namespace policy** verification and declared-vs-observed enforcement.

A namespace policy is a signed, in-log, versioned record declaring the
cryptographic posture a namespace operates under: checkpoint signature suite
and security level, the CONIKS commitment hash, the VRF mode, and the
directory route (CONIKS vs the experimental IETF KEYTRANS backend and its
cipher suite). Because the policy is itself a log leaf, the posture is
auditable and tamper-evident.

`verify/1` checks the policy's self-signature and returns the declared
posture. The `enforce_*` functions then assert that an **observed** artifact
(a checkpoint signing key, a checkpoint signature, a VRF suite id, a
commitment hash, or a directory backend id) matches what the verified policy
**declares** — the "declared == observed" invariant that stops an operator
from silently downgrading.

The signed policy envelope is **base64-encoded**.

# `directory_backend`

```elixir
@type directory_backend() :: :coniks | :keytrans | 0..65535
```

A directory backend identifier (§3.3 `DirectoryBackendId`, a `u16`).

Pass either a well-known atom or the raw id:

* `:coniks` — the shipped, frozen CONIKS prefix-tree backend (`0x0001`)
* `:keytrans` — the experimental IETF KEYTRANS combined-tree backend
  (`0xF004`, `KEYTRANS_EXP_V04`; movable — the id tracks the draft)

# `t`

```elixir
@type t() :: %MetamorphicLog.Policy{
  checkpoint_suite: :hybrid | :hybrid_matched | :pure_cnsa2,
  commitment_hash: :sha3_256 | :sha3_512,
  created_at: non_neg_integer(),
  directory_mode: :coniks | :keytrans,
  effective_from: non_neg_integer(),
  keytrans_suite:
    :metamorphic_hybrid_exp | :kt128_sha256_p256 | :kt128_sha256_ed25519,
  namespace: String.t(),
  policy_hash: String.t(),
  policy_schema_version: non_neg_integer(),
  rfc6962_leaf_hash: String.t(),
  security_level: :cat3 | :cat5,
  vrf_mode: :classical | :hybrid_output | :pure_pq_experimental
}
```

A verified namespace policy.

* `:security_level` — `:cat3` | `:cat5`
* `:checkpoint_suite` — `:hybrid` | `:hybrid_matched` | `:pure_cnsa2`
* `:commitment_hash` — `:sha3_256` | `:sha3_512`
* `:vrf_mode` — `:classical` | `:hybrid_output` | `:pure_pq_experimental`
* `:directory_mode` — `:coniks` | `:keytrans` (the Layer-3 directory route)
* `:keytrans_suite` — `:metamorphic_hybrid_exp` | `:kt128_sha256_p256` |
  `:kt128_sha256_ed25519` (only meaningful when `:directory_mode` is
  `:keytrans`; the default suite otherwise)
* `:policy_hash`, `:rfc6962_leaf_hash` — base64-encoded

# `enforce_checkpoint_signature`

```elixir
@spec enforce_checkpoint_signature(String.t(), String.t()) ::
  :ok | {:error, String.t()}
```

Enforce that a checkpoint `signature_b64` matches the verified policy's
declared checkpoint posture. Returns `:ok` or `{:error, reason}`.

# `enforce_checkpoint_signing_key`

```elixir
@spec enforce_checkpoint_signing_key(String.t(), String.t()) ::
  :ok | {:error, String.t()}
```

Enforce that a checkpoint signing key (`public_key_b64`) matches the verified
policy's declared checkpoint posture. Returns `:ok` or `{:error, reason}`.

# `enforce_commitment_hash`

```elixir
@spec enforce_commitment_hash(String.t(), :sha3_256 | :sha3_512) ::
  :ok | {:error, String.t()}
```

Enforce that an observed `commitment_hash` (`:sha3_256` | `:sha3_512`) matches
the policy's declaration. Returns `:ok` or `{:error, reason}`.

# `enforce_directory_backend`

```elixir
@spec enforce_directory_backend(String.t(), directory_backend()) ::
  :ok | {:error, String.t()}
```

Enforce that an **observed** directory backend matches the one the verified
policy declares (from its `:directory_mode` / `:keytrans_suite` axis).

A CONIKS-route policy must be served by the CONIKS backend; a KEYTRANS-route
policy by the KEYTRANS combined-tree backend. Any disagreement — including a
route that declares a reserved/not-built suite — is a hard rejection.

`observed` is a `directory_backend/0`: `:coniks`, `:keytrans`, or a raw
`u16`. Returns `:ok` or `{:error, reason}`.

## Example

    :ok = MetamorphicLog.Policy.enforce_directory_backend(signed_b64, :coniks)

# `enforce_vrf_suite_id`

```elixir
@spec enforce_vrf_suite_id(String.t(), 0..255) :: :ok | {:error, String.t()}
```

Enforce that an observed VRF `suite_id` matches the policy's declared VRF
mode. Returns `:ok` or `{:error, reason}`.

# `sign`

```elixir
@spec sign(
  params :: Enumerable.t(),
  secret_key_b64 :: String.t()
) :: {:ok, String.t()} | {:error, String.t()}
```

Sign a namespace policy and return its base64-encoded signed envelope, the
producer counterpart to `verify/1`.

`params` is a keyword list or map declaring the posture to attest. The enum
fields take the **same atoms `verify/1` returns**, so a verified policy can be
re-signed by feeding its struct fields straight back in:

* `:namespace` (required) — the namespace identity string
* `:policy_schema_version` (required) — non-negative integer
* `:security_level` (required) — `:cat3` | `:cat5`
* `:checkpoint_suite` (required) — `:hybrid` | `:hybrid_matched` | `:pure_cnsa2`
* `:commitment_hash` (required) — `:sha3_256` | `:sha3_512`
* `:vrf_mode` (required) — `:classical` | `:hybrid_output` | `:pure_pq_experimental`
* `:directory_mode` (required) — `:coniks` | `:keytrans`
* `:keytrans_suite` (required only when `:directory_mode` is `:keytrans`) —
  `:metamorphic_hybrid_exp` | `:kt128_sha256_p256` | `:kt128_sha256_ed25519`;
  ignored for the CONIKS route (defaults to `:metamorphic_hybrid_exp`)
* `:effective_from` (required) — non-negative integer timestamp
* `:created_at` (required) — non-negative integer timestamp
* `:prev_policy_hash` (optional) — base64 of the previous policy's 64-byte
  hash to chain revisions, or `nil` for the first policy

`secret_key_b64` is the base64 metamorphic-crypto composite secret key. ML-DSA
signing is hedged, so the envelope bytes are not reproducible, but the result
verifies deterministically via `verify/1`.

Returns `{:ok, signed_b64}` or `{:error, reason}` (unknown enum value,
malformed namespace, a `prev_policy_hash` that is not 64 bytes, or a signing
failure).

## Example

    {:ok, signed} =
      MetamorphicLog.Policy.sign(
        [
          namespace: "metamorphic.app/log",
          policy_schema_version: 1,
          security_level: :cat3,
          checkpoint_suite: :hybrid,
          commitment_hash: :sha3_256,
          vrf_mode: :classical,
          directory_mode: :coniks,
          effective_from: 0,
          created_at: 0
        ],
        secret_key_b64
      )

# `verify`

```elixir
@spec verify(signed_b64 :: String.t()) :: {:ok, t()} | {:error, String.t()}
```

Verify a signed policy envelope and return the declared posture as a
`%MetamorphicLog.Policy{}` struct.

Returns `{:ok, %Policy{}}` or `{:error, reason}`.

## Example

    {:ok, %MetamorphicLog.Policy{checkpoint_suite: :hybrid}} =
      MetamorphicLog.Policy.verify(signed_b64)

---

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