SignedNote.Checkpoint (signed_note v1.0.0)

Copy Markdown View Source

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
20852163

Both 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

t()

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

t()

@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

from_text(text)

@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.

to_text(checkpoint)

@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

to_text!(checkpoint)

@spec to_text!(t()) :: String.t()

Renders the checkpoint as a note text, raising SignedNote.Error if the checkpoint cannot be represented.