Amarula.Protocol.Signal.SessionRecord (amarula v0.1.0)

View Source

Signal session storage, ported from node_modules/libsignal/src/session_record.js.

A SessionRecord holds one or more session entries keyed by the base key. Each entry is a plain map:

%{
  registration_id: integer | nil,
  current_ratchet: %{
    root_key: binary,
    ephemeral_key_pair: %{public: binary, private: binary},
    last_remote_ephemeral_key: binary,
    previous_counter: integer
  },
  index_info: %{
    created: integer, used: integer,
    remote_identity_key: binary,
    base_key: binary, base_key_type: 1|2,  # OURS | THEIRS
    closed: integer   # -1 = open
  },
  chains: %{ base64_pubkey => chain },
  pending_pre_key: map | nil
}

The record is a map: %{sessions: %{base64_basekey => entry}}. We persist via :erlang.term_to_binary, so unlike the JS impl no base64-of-buffers is needed for storage — but chains/sessions are still keyed by base64 strings to match libsignal's lookup-by-pubkey semantics exactly.

Summary

Functions

Close a session (mark it with a closed timestamp). Idempotent.

A fresh session entry shell (libsignal createEntry — chains start empty).

First open (closed == -1) session, or nil.

Look up a session by base key. Raises if it resolves to one of our own base keys.

All sessions, most-recently-used first (by index_info.used).

An empty record.

Drop oldest closed sessions while over CLOSED_SESSIONS_MAX.

Store/replace a session, keyed by its index_info.base_key.

Types

t()

@type t() :: %{sessions: %{optional(binary()) => map()}}

Functions

add_chain(entry, key, chain)

@spec add_chain(map(), binary(), map()) :: map()

base_key_ours()

base_key_theirs()

chain_receiving()

chain_sending()

close_session(record, session)

@spec close_session(t(), map()) :: t()

Close a session (mark it with a closed timestamp). Idempotent.

closed?(session)

@spec closed?(map()) :: boolean()

create_entry()

@spec create_entry() :: map()

A fresh session entry shell (libsignal createEntry — chains start empty).

delete_chain(entry, key)

@spec delete_chain(map(), binary()) :: map()

get_chain(entry, key)

@spec get_chain(map(), binary()) :: map() | nil

get_open_session(record)

@spec get_open_session(t()) :: map() | nil

First open (closed == -1) session, or nil.

get_session(record, key)

@spec get_session(t(), binary()) :: map() | nil

Look up a session by base key. Raises if it resolves to one of our own base keys.

get_sessions(record)

@spec get_sessions(t()) :: [map()]

All sessions, most-recently-used first (by index_info.used).

have_open_session?(record)

@spec have_open_session?(t()) :: boolean()

new()

@spec new() :: t()

An empty record.

put_chain(entry, key, chain)

@spec put_chain(map(), binary(), map()) :: map()

remove_old_sessions(record)

@spec remove_old_sessions(t()) :: t()

Drop oldest closed sessions while over CLOSED_SESSIONS_MAX.

set_session(record, session)

@spec set_session(t(), map()) :: t()

Store/replace a session, keyed by its index_info.base_key.