# `Raxol.Agent.Journal.Chain`
[🔗](https://github.com/DROOdotFOO/raxol/blob/v2.8.0/lib/raxol/agent/journal/chain.ex#L1)

The hash chain of a `chain: true` journal (schema_version 1.2.0).

Every record of a chained journal carries two extra keys:

  * `"prev_hash"` -- the `"hash"` of the record before it; the first record
    (id 1) links to `genesis/0`, 64 zeros. One chain spans every segment: the
    first record of segment N links to the last record of segment N-1.
  * `"hash"` -- lowercase hex SHA-256 of the record's canonical JSON with the
    `"hash"` key removed (so it covers `"prev_hash"`, which is what chains it).

## Canonical JSON

The canonical form of a decoded JSON value is: objects with keys sorted by
byte order at every depth, arrays in order, no whitespace, strings and
integers as `Jason` encodes them with `escape: :json`. It is defined over the
DECODED record, not the bytes a writer happened to emit, so any JSON decoder
can recompute it. Floats are refused at append (`{:error, {:float, path}}`):
their text form is the one thing in JSON with no single canonical spelling,
and money in a chained journal is a decimal string.

A chained record line on disk is exactly the canonical JSON of the record,
`"hash"` included. The reader checks that too, so a byte change that decodes
to the same value (a hex-digit case flip inside a `\u` escape, say) is still
a break.

The chain detects corruption, truncation and naive edits. It carries no
key: anyone who can rewrite the journal directory can rewrite the chain.

# `canonical`

```elixir
@spec canonical(term()) :: iodata()
```

Canonical JSON (see the moduledoc) of a decoded JSON value, as iodata.

# `check`

```elixir
@spec check(map(), String.t(), binary()) :: :ok | :broken
```

Check one decoded record against its predecessor's hash and the raw line it
was decoded from. `:ok` only when the record links to `prev_hash`, its
`"hash"` matches its content, and `raw` is its canonical encoding.

# `genesis`

```elixir
@spec genesis() :: String.t()
```

The `prev_hash` of the first record of a chain: 64 zeros.

# `seal`

```elixir
@spec seal(map(), String.t()) ::
  {:ok, iodata(), String.t()}
  | {:error,
     {:float, [String.t() | non_neg_integer()]} | {:unencodable, String.t()}}
```

Seal a stamped record onto the chain after `prev_hash`.

`record` may hold anything `Jason` encodes (atoms, structs with an encoder);
it is normalized through one encode/decode round trip first, so the hash is
computed over exactly what a reader will decode. Returns the line to write
(canonical JSON, no trailing newline) and the record's hash.

---

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