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

Canonical leaf encoding for the `mosslet/key-history/v1` conformance instance.

This is the worked example of the engine's application-agnostic leaf layer: a
signed, hash-chained key-history entry. The three functions here reproduce —
**byte-for-byte** — the canonical encoding, the intra-chain entry hash, and
the RFC 6962 leaf hash, identically across the Rust core, the browser WASM
SDK, and this NIF. That cross-target parity is what lets a monitor recompute
and compare a log built by browser clients.

## Branding your own key history (recommended)

`mosslet/key-history/v1` is the reference conformance instance, not a label
you should reuse. For your own application, brand the chain with your own
`<namespace>/key-history/v1` label via
`key_history_entry_hash_with_context/2` — for example
`"mosskeys/key-history/v1"`. That domain-separates your entry hashes and lets
auditors tell whose key history a chain is, while the canonical bytes and the
RFC 6962 leaf hash stay brand-independent, so cross-target parity is
preserved. The `key_history_v1_*` functions remain for reproducing the frozen
reference vectors exactly.

## Entry

An entry is a map with base64-encoded binary fields:

    %{
      seq: 0,                     # u64 sequence number
      ts_ms: 1_700_000_000_000,   # u64 unix-ms timestamp
      enc_x25519: "...",          # base64 X25519 encryption public key
      enc_pq: "...",              # base64 ML-KEM encryption public key
      signing_pub: "...",         # base64 hybrid signing public key
      prev_entry_hash: nil        # base64 prev entry_hash, or nil for genesis
    }

## Canonical layout

    u32_be(VERSION=1)
      || u64_be(seq) || u64_be(ts_ms)
      || lp(enc_x25519) || lp(enc_pq) || lp(signing_pub) || lp(prev_entry_hash)

where `lp(x) = u32_be(byte_size(x)) || x` and a `nil` `prev_entry_hash`
encodes as `lp(<<>>)`.

## Return shape

Each function returns `{:ok, value_b64} | {:error, reason}`, with a `!`
variant that returns the value directly and raises on invalid input.

# `entry`

```elixir
@type entry() :: %{
  :seq =&gt; non_neg_integer(),
  :ts_ms =&gt; non_neg_integer(),
  :enc_x25519 =&gt; String.t(),
  :enc_pq =&gt; String.t(),
  :signing_pub =&gt; String.t(),
  optional(:prev_entry_hash) =&gt; String.t() | nil
}
```

A `mosslet/key-history/v1` entry with base64-encoded binary fields.
`:prev_entry_hash` is `nil` for the genesis entry.

# `key_history_entry_hash_with_context`

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

Context-parameterized intra-chain entry hash — the **recommended** way to
compute a key-history `entry_hash`.

Brand the chain with your own `<namespace>/key-history/v1` label (for example
`"mosskeys/key-history/v1"`) instead of inheriting the reference
`mosslet/key-history/v1` instance. Tailoring the label to your namespace
domain-separates your entry hashes and lets an auditor tell whose key history
a chain belongs to, while the canonical bytes and the RFC 6962 leaf hash stay
brand-independent — a monitor recomputes them identically regardless of label.

`context` is a `<namespace>/<record-type>/v<major>` label, validated before it
crosses the NIF boundary; a malformed label returns `{:error, reason}`.
Passing exactly `"mosslet/key-history/v1"` reproduces
`key_history_v1_entry_hash/1` byte-for-byte.

## Example

    {:ok, hash} =
      MetamorphicLog.Leaf.key_history_entry_hash_with_context(
        "mosskeys/key-history/v1",
        entry
      )

# `key_history_entry_hash_with_context!`

```elixir
@spec key_history_entry_hash_with_context!(String.t(), entry()) :: String.t()
```

Bang form of `key_history_entry_hash_with_context/2`.

# `key_history_v1_canonical_bytes`

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

Canonical byte encoding of a `key-history/v1` `entry`, base64-encoded.

## Example

    {:ok, canon} = MetamorphicLog.Leaf.key_history_v1_canonical_bytes(entry)

# `key_history_v1_canonical_bytes!`

```elixir
@spec key_history_v1_canonical_bytes!(entry()) :: String.t()
```

Bang form of `key_history_v1_canonical_bytes/1`.

# `key_history_v1_entry_hash`

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

Intra-chain SHA3-512 entry hash of `entry`, base64-encoded.

This is the value a later entry references as its `:prev_entry_hash`, chaining
the history together.

# `key_history_v1_entry_hash!`

```elixir
@spec key_history_v1_entry_hash!(entry()) :: String.t()
```

Bang form of `key_history_v1_entry_hash/1`.

# `key_history_v1_rfc6962_leaf_hash`

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

RFC 6962 leaf hash — `SHA-256(0x00 || canonical_bytes)` — of `entry`,
base64-encoded. This is the value placed in the Merkle tree.

# `key_history_v1_rfc6962_leaf_hash!`

```elixir
@spec key_history_v1_rfc6962_leaf_hash!(entry()) :: String.t()
```

Bang form of `key_history_v1_rfc6962_leaf_hash/1`.

---

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