The C2SP tlog-checkpoint profile: a note text carrying a transparency log's Merkle tree head.
<origin>
<tree size>
<base64 root hash>
[extension lines...]A checkpoint is the text of a signed note. Obtain one by verifying a
note with SignedNote.open/2 and passing the note's text to
from_text/1; produce one for signing with to_text!/1 followed by
SignedNote.sign/2.
iex> {:ok, checkpoint} =
...> SignedNote.Checkpoint.from_text(
...> "example.com/behind-the-sofa\n20852163\nCsUYapGGPo4dkMgIAUqom/Xajj7h2fB2MPA3j2jxq2I=\n"
...> )
iex> checkpoint.origin
"example.com/behind-the-sofa"
iex> checkpoint.tree_size
20852163Both directions validate. Rendering a checkpoint whose origin or
extension lines contain a newline would frame a different checkpoint
than the struct describes — evil\n999\n... as an origin would reparse
with a tree size of 999 — so to_text/1 rejects it rather than emitting
text whose meaning does not match its source.
Summary
Types
A parsed checkpoint: origin identity, tree size, raw root hash bytes, and any opaque extension lines.
Functions
Parses a note text as a checkpoint.
Renders the checkpoint as a note text, returning {:ok, text} or
{:error, reason}.
Renders the checkpoint as a note text, raising SignedNote.Error if the
checkpoint cannot be represented.
Types
@type t() :: %SignedNote.Checkpoint{ extension_lines: [String.t()], origin: String.t(), root_hash: binary(), tree_size: non_neg_integer() }
A parsed checkpoint: origin identity, tree size, raw root hash bytes, and any opaque extension lines.
Functions
@spec from_text(String.t()) :: {:ok, t()} | {:error, SignedNote.Error.t()}
Parses a note text as a checkpoint.
The text must contain at least three non-empty lines — origin, decimal tree size without leading zeros, base64 root hash — followed by optional extension lines, which the specification requires to be non-empty.
@spec to_text(t()) :: {:ok, String.t()} | {:error, SignedNote.Error.t()}
Renders the checkpoint as a note text, returning {:ok, text} or
{:error, reason}.
iex> checkpoint = %SignedNote.Checkpoint{
...> origin: "log.example/1",
...> tree_size: 42,
...> root_hash: <<0::256>>
...> }
iex> {:ok, text} = SignedNote.Checkpoint.to_text(checkpoint)
iex> {:ok, ^checkpoint} = SignedNote.Checkpoint.from_text(text)
iex> String.starts_with?(text, "log.example/1\n42\n")
true
Renders the checkpoint as a note text, raising SignedNote.Error if the
checkpoint cannot be represented.