For the complete documentation index, see llms.txt. This page is also available as Markdown.

Messages

This page defines the protocol message taxonomy and the normative wire-format rules for exchanging messages between two paired participants. Pairings include Owner ↔ Helper, Owner ↔ Replica, and Replica ↔ Replica. All protocol messages (except ContactMessage) are carried inside a DeRecMessage envelope.

Message Taxonomy

ContactMessage

ContactMessage is the sole protocol message that is not wrapped in DeRecMessage; it is transferred out-of-band (e.g., QR code).

Fields

Field
Description

mlkemEncapsulationKey eciesPublicKey

Together provide public encryption material to established a trusted session.

channelId

Channel identifier for the pairing session

nonce

Pairing session nonce

transportProtocol

Endpoint and transport to reach the sender

timestamp

Message creation time

Constraints

  • nonce MUST match the nonce carried in subsequent PairRequestMessage and PairResponseMessage for the same pairing session

  • channelId identifies the channel expected for subsequent protocol messages sent to the contact creator

  • transportProtocol MUST be used to deliver the first PairRequestMessage

DeRecMessage Envelope

All protocol messages other than ContactMessage are sent as a serialized DeRecMessage.

Fields

Field
Description

protocolVersionMajor protocolVersionMinor

Protocol major and miner version

sequence

Monotonic message sequence number

channelId

Channel identifier

timestamp

Envelope creation time

message

Encrypted serialized MessageBody

Notes

  • The message field contains encrypted bytes

  • After decryption, message MUST deserialize into DeRecMessage.MessageBody

  • Exactly one inner message type is carried per envelope

PairRequestMessage & PairResponseMessage

First message sent from pairing initiator to pairing responder after out-of-band ContactMessage transfer.

SenderKind

SenderKind identifies the logical role of the sender. Pairings form by matching complementary kinds:

Initiator
Responder

Owner

Helper

ReplicaSource

ReplicaDestination

  • Owner — initiates ProtectSecret, VerifyShares, Discovery, RecoverSecret, and Unpair. There is no protocol-level distinction between an Owner pairing for the first time and an Owner re-pairing in recovery mode.

  • Helper — accepts the Owner-initiated flows above.

  • ReplicaSource — initiates a replica pair handshake and the initial Secret sync. Once the Destination has received its first Secret, both sides converge to operationally equivalent Owner-identity devices.

  • ReplicaDestination — accepts the inbound replica pair handshake and receives the initial Secret sync. Same operational footing as the Source thereafter.

Each peer's SenderKind is recorded on the channel at pairing time and the protocol enforces flow directionality against it. A Helper attempting to initiate ProtectSecret is rejected with a role-mismatch error. Inbound messages are gated the other way around — a StoreShareRequest is only honored on a channel where this node is the receiver (Helper or ReplicaDestination).

Fields — PairRequestMessage

Field
Description

senderKind

Role of initiator

mlkemCiphertext

ML-KEM ciphertext for responder bootstrap

eciesPublicKey

Initiator ECIES public key

channelId

Channel identifier chosen by initiator

communicationInfo

Application-defined identification info

nonce

Pairing session nonce

parameterRange

Supported operating range

transportProtocol

Endpoint and transport to reach initiator

timestamp

Message creation time

Fields — PairResponseMessage

Field
Description

result

Pairing result

senderKind

Role of responder

communicationInfo

Application-defined identification info

nonce

Pairing session nonce

parameterRange

Supported operating range

timestamp

Message creation time

CommunicationInfo

CommunicationInfo carries application-defined identifying metadata about a participant — the kind of human-readable context the receiver can display to help the user identify and confirm the counterparty.

Field
Description

communicationInfoEntries

List of key-value entries. Each entry is one piece of identifying information for the sender.

Each entry's key is an application-defined identifier (e.g. "name", "email", "phone"), and exactly one of stringValue / bytesValue MUST be set. The protocol does not interpret or validate keys or values — the entries are opaque to DeRec.

Applications SHOULD use common keys where possible ("name", "email", "phone", etc.) to improve cross-implementation UX, but no global registry is enforced.

Reserved derec.* namespace

The protocol never inspects keys or values in CommunicationInfo except for entries whose key is under the derec.* namespace, which are reserved for the protocol itself.

Currently defined:

  • derec.replica_id — decimal-encoded u64. Carries the sender's stable replica identity on replica-mode pairing envelopes. Auto-injected on outbound replica pairings and auto-extracted on inbound, populating Channel.replica_id. Per-pairing-kind validation: replica pairings (ReplicaSource / ReplicaDestination) MUST carry it; non-replica pairings (Owner / Helper) MUST NOT.

PrePairRequestMessage & PrePairResponseMessage

The PrePair exchange is a plaintext round-trip that fetches the actual ML-KEM and ECIES public keys when the initial ContactMessage used contactMode == HashedKeys — a mode where the ContactMessage carries only a contactBindingHash (48 bytes) instead of the public keys themselves, to fit in size-constrained out-of-band channels like QR codes.

The scanner MUST validate the received keys against the contactBindingHash from the ContactMessage before proceeding to send a regular PairRequestMessage.

Fields — PrePairRequestMessage

Field
Description

nonce

Echoed from the original ContactMessage.nonce. Mismatches indicate a stale or spoofed request; the receiver MUST refuse to serve the keys.

transportProtocol

Transport endpoint at which the sender expects to receive the PrePairResponseMessage.

timestamp

Message creation time.

Fields — PrePairResponseMessage

Field
Description

result

Operation result. On Ok, the two key fields below are present. On any non-Ok status, they MUST be absent.

mlkemEncapsulationKey

Serialized ML-KEM-768 encapsulation key. Present only on Ok.

eciesPublicKey

Serialized ECIES public key. Present only on Ok.

nonce

Echoed from the request for session correlation.

timestamp

Message creation time.

Envelope and security

Both messages are carried inside a DeRecMessage envelope, but the envelope's message field carries the serialized PrePair*Message bytes unencrypted — no shared symmetric key exists at this point in the handshake.

Scanner-side validation on receipt of PrePairResponseMessage:

  1. Confirm result.status == Ok. On non-Ok, surface a typed error and do not proceed.

  2. Recompute SHA-384(mlkemEncapsulationKey || eciesPublicKey || u64_be(nonce) || u64_be(channelId)) and verify it matches the original ContactMessage.contactBindingHash.

  3. Only on a match, proceed to construct and send a normal PairRequestMessage.

ParameterRange

Fields

Field
Description

minShareSize maxShareSize

Minimum and maximum accepted share size

minTimeBetweenVerifications maxTimeBetweenVerifications

Minimum and maximum accepted verification interval

minTimeBetweenShareUpdates maxTimeBetweenShareUpdates

Minimum and maximum accepted share update interval

minUnresponsiveDeletionTimeout maxUnresponsiveDeletionTimeout

Minimum and maximum deletion timeout for unresponsive peer

minUnresponsiveDeactivationTimeout maxUnresponsiveDeactivationTimeout

Minimum and maximum deactivation timeout for unresponsive peer

TransportProtocol

Fields

Field
Description

uri

Transport endpoint

protocol

Transport type

Validation

The library validates every TransportProtocol at the wire-bytes ingress; peers shipping a mismatched scheme are refused.

  • protocol carries a discriminant — currently only Https (0).

  • uri MUST start with the scheme matching the declared protocol: Protocol::Https requires the URI to begin with https://. Plaintext (http://) and other schemes (ws://, …) are rejected.

  • uri length is capped at 2048 bytes.

  • uri MUST NOT contain control characters — bytes < 0x20 or = 0x7F (NUL, embedded newlines, terminal escape codes) are rejected.

  • Unknown protocol discriminants are rejected at the conversion boundary.

Validation failures are reported through the protocol-level error mechanism and rejected at the wire-bytes ingress before any further processing.

UnpairRequestMessage & UnpairResponseMessage

Requests that the other party ceases the DeRec relationship and deletes relationship data, subject to regulations or service agreements.

Fields — UnpairRequestMessage

Field
Description

memo

Human-readable reason

timestamp

Message creation time

Fields — UnpairResponseMessage

Field
Description

result

Operation result

timestamp

Message creation time

StoreShareRequestMessage & StoreShareResponseMessage

This message has two uses, distinguished by shareAlgorithm:

  • To a Helper (shareAlgorithm = 0): the share field carries one committed share for the Secret at the given version. The Helper persists it indexed by (secretId, version, replicaId) and does not introspect the bytes.

  • To a Replica (shareAlgorithm = 1): the share field carries the full encoded Secret payload — a snapshot of the entire Secret at the given version. The receiving Replica installs it locally. See the Secret payload section below for the structure.

Fields — StoreShareRequestMessage

Field
Description

share

Opaque share bytes (committed share for Helpers; encoded Secret for Replicas — see shareAlgorithm)

shareAlgorithm

Share encoding identifier — 0 = CommittedDeRecShare, 1 = full Secret payload

secretId

Identifier of the protected Secret

version

Version of the Secret being distributed

keepList

Versions to retain at the receiver

versionDescription

Optional version label

timestamp

Message creation time

replyTo

Optional transport endpoint to which the receiver should send its response. See replyTo and routing replies.

replicaId

Optional stable per-device identifier of the writing device. See the callout below.

Fields — StoreShareResponseMessage

Field
Description

result

Operation result

secretId

Echoes the Secret identifier from the request

version

Echoes the version from the request

timestamp

Message creation time

replicaId is the stable per-device identifier of the writer. Absent (the default) means a non-replica Owner produced it; present means a Replica device is announcing its identity. Helpers persist replicaId alongside the share so distinct replicas writing the same (secretId, version) coexist instead of overwriting each other. The protocol does not authenticate the claim — conflict resolution is the application's responsibility (see Conflict resolution); the protocol just surfaces the metadata.

Secret payload

When shareAlgorithm = 1, the share field carries the serialized Secret message — a snapshot of the entire protected Secret at the given version. Replica receivers decode it and install the result as their local Secret.

Field
Description

helpers

Snapshot of every paired Helper for this Owner identity, including the per-Helper Shared Key, transport endpoint, and channel id. This is what makes any Replica able to talk to those Helpers as the Owner identity.

secrets

The user-facing data the Owner identity is protecting — one entry per logical secret.

replicas

Snapshot of every paired Replica device under the same Owner identity, including the replica-group Shared Key that all Replicas use to communicate among themselves.

owner_replica_id

The replica_id of the device that produced this version of the Secret. Used by receivers to attribute origin and to detect concurrent-write conflicts.

See Secret for the conceptual treatment, including how version increments propagate and how the replicas field carries the group's Shared Key.

CommittedDeRecShare (share algorithm 0)

When shareAlgorithm = 0 in a StoreShareRequestMessage, the share field carries the serialized CommittedDeRecShare message — one committed share bound to a Merkle-root commitment that lets the requester detect tampering during recovery.

Field
Description

deRecShare

Serialized DeRecShare for this specific Helper. See below.

commitment

Merkle root of the shares distributed in this round. SHA-384 hash. Every Helper receives the same commitment for a given (secretId, version) round.

merklePath

Bottom-up Merkle path from this share's leaf to the root, as a sequence of SiblingHash { isLeft, hash } entries. The isLeft flag records whether the sibling is the left child at each level.

merklePath lets the recovery-time verifier reconstruct the Merkle root from deRecShare alone and cross-check it against commitment. Distinct shares in the same round produce distinct leaves but a common root, so any tampering with a single Helper's stored bytes surfaces as a mismatch.

DeRecShare

DeRecShare is the inner share content wrapped by CommittedDeRecShare.deRecShare.

Field
Description

encryptedSecret

The encoded Secret payload for this (secretId, version) round, encrypted with a per-round AES-256 key.

x

Random 256-bit x coordinate for this Helper's Shamir point. Encoded as two's-complement big-endian bytes.

y

Corresponding y coordinate — y = f(x), where f is the sharing polynomial whose evaluation at 0 is the encryption key for encryptedSecret.

secretId

Identifier of the protected Secret. Serialized inside the share so it is covered by the signature on the enclosing envelope.

version

Version number of the share. A Helper is entitled to ignore any StoreShareRequestMessage whose version is less than or equal to the last one it accepted.

Sharing is done in GF(p) where p is the smallest 256-bit prime. The polynomial degree determines the recovery threshold — t distinct (x, y) pairs are needed to interpolate and reconstruct the key at x = 0.

DeRecSecret

DeRecSecret is the Owner-side representation of the material distributed to Helpers in one share-algorithm-0 round — the bytes to be protected, the sharing configuration, and the roster of Helpers with the parameters each Helper agreed to at pairing time.

Field
Description

secretData

The bytes to be protected in this round. Encoded verbatim.

creationTime

Timestamp for this snapshot. Used for auditing, version tracking, and observability.

helperThresholdForRecovery

Minimum number of Helpers required to reconstruct the Secret — the t of the threshold sharing scheme.

helperThresholdForConfirmingShareReceipt

Minimum number of Helpers that must acknowledge receipt of a new share version before older versions may be safely deleted.

helpers

Roster of Helpers participating in this share round, each with a HelperSpecificInfo. Length defines the total number of shares.

Changes to secretData, the Helper set, or any threshold produce a new version — the current Secret snapshot is what the next share round distributes.

HelperSpecificInfo

HelperSpecificInfo is one Helper's entry in DeRecSecret.helpers.

Field
Description

helper

SHA-384 hash of the Helper's public encryption key. Stable identifier for the Helper within the context of this Secret without exposing the raw key.

helperParams

The ParameterRange agreed with this Helper at pairing time — the intersection of both parties' supported ranges. Used to validate that shares stay within negotiated operational constraints.

VerifyShareRequestMessage & VerifyShareResponseMessage

Owner challenges Helper to prove it still retains a particular share version.

Fields — VerifyShareRequestMessage

Field
Description

version

Share version being challenged

nonce

Challenge nonce

timestamp

Message creation time

Fields — VerifyShareResponseMessage

Field
Description

result

Operation result

version

Share version

nonce

Echoed challenge nonce

hash

SHA-384(committedDeRecShare || nonce)

timestamp

Message creation time

GetSecretIdsVersionsRequestMessage & GetSecretIdsVersionsResponseMessage

Used during recovery and routine inventory to request the list of secret IDs and stored versions a Helper currently holds for the requester. The replicaId field on each returned VersionEntry is also the surface that lets the requester detect conflicts between sibling Replicas — see Conflict resolution.

Fields — GetSecretIdsVersionsRequestMessage

Field
Description

timestamp

Message creation time

Fields — GetSecretIdsVersionsResponseMessage

Field
Description

result

Operation result

secretList

List of secrets and versions

timestamp

Message creation time

Fields — VersionList

Field
Description

secretId

Secret identifier

versions

Stored share versions, each as a VersionEntry

Fields — VersionList.VersionEntry

Field
Description

version

Numeric version identifier

versionDescription

Human-readable description supplied by the Owner at share-storage time

replicaId

Optional stable per-device identifier of the replica that produced this version

replicaId on VersionEntry is copied from StoreShareRequestMessage.replicaId at share-storage time. Two distinct replicas can independently produce the same numeric version for the same secretId (each replica observes the current latest version and increments it); the Helper persists both side-by-side, and this field is what lets the requester tell them apart. Absent means a non-replica Owner produced the version. Resolution of duplicates is the application's responsibility — the protocol just surfaces the metadata.

replyTo and routing replies

Every request message — StoreShareRequest, VerifyShareRequest, GetSecretIdsVersionsRequest, GetShareRequest, UnpairRequest — carries an optional replyTo: TransportProtocol field. The field exists to handle a routing asymmetry: Helpers learn the Owner's transport endpoint at pair time and never pair separately with Replicas, so a Replica that sends a request to a Helper using the per-Helper Shared Key from the synced Secret cannot rely on the Helper's default routing to reach back.

  • If replyTo is present, the receiver routes its response to that endpoint and does NOT persist it as a channel-state change.

  • If replyTo is absent, the receiver falls back to the transport endpoint it learned at pair time.

Owner-driven requests can leave replyTo unset and rely on the default routing. Replica-driven requests must populate it with the Replica's own transport, or the response will never reach them. See Helpers know the Owner, not the replicas for the conceptual treatment.

GetShareRequestMessage & GetShareResponseMessage

Requests a specific share by (secretId, shareVersion) from a Helper.

Fields — GetShareRequestMessage

Field
Description

secretId

Secret identifier

shareVersion

Requested version

timestamp

Message creation time

Fields — GetShareResponseMessage

Field
Description

result

Operation result

committedDeRecShare

Returned share payload

shareAlgorithm

Share encoding identifier

timestamp

Message creation time

UpdateChannelInfoRequestMessage & UpdateChannelInfoResponseMessage

UpdateChannelInfo is a maintenance flow that lets either party of an already-paired channel propagate post-pairing changes to their CommunicationInfo and/or TransportProtocol without re-pairing. Both fields on the request are optional and independent, but at least one MUST be present.

Unlike every other Owner-initiated flow, UpdateChannelInfo is symmetric — either party may initiate on any paired channel (Owner ↔ Helper, Owner ↔ Replica, Replica ↔ Replica).

Fields — UpdateChannelInfoRequestMessage

Field
Description

communicationInfo

Updated communication info for the sender, or absent to leave unchanged. Presence — including with an empty entries list — instructs the receiver to replace its stored map for this channel with the supplied one. Absence leaves the stored map untouched.

transportProtocol

Updated transport endpoint for the sender, or absent to leave unchanged. Presence instructs the receiver to route the response and all subsequent messages on this channel to the new endpoint.

timestamp

Message creation time.

Replacement semantics for communicationInfo: updates, additions, and deletions are all expressed by the difference between the receiver's current stored map and the supplied one. There is no per-entry delta payload.

Fields — UpdateChannelInfoResponseMessage

Field
Description

result

Operation result. On Ok, the receiver has persisted the supplied updates. On any non-Ok status, the receiver's state is unchanged.

timestamp

Message creation time.

Endpoint changeover discipline

When transportProtocol is updated, the receiver routes both its UpdateChannelInfoResponseMessage and every subsequent message on this channel to the new endpoint. This creates a hard dependency the initiator's application MUST satisfy:

  1. Bring up the new endpoint and start listening on it before issuing the request.

  2. Keep the old endpoint operational during the changeover — peers that have not yet processed the update still route to the old address, and in-flight messages from any peer may also target it.

  3. Retire the old endpoint only after every targeted peer has responded (or been observed to route to the new endpoint), plus a grace window for in-flight traffic.

ErrorResponseMessage

Sent by a receiver who receives a bad message back to the sender.

Fields

Field
Description

result

Error result

timestamp

Message creation time

DeRecResult

All response messages carry a DeRecResult with a status and optional human-readable memo.

Fields

Field
Description

status

Result status

memo

Optional message

Last updated