# FOG Messaging
Status: Draft 0.1
Date: 2026-08-08
## 1. Purpose
This document defines `FOG-MESSAGING`, the Composer-to-Composer messaging
contract for FOG native asynchronous services.
It fixes the contact model, private vouchers, message-envelope framing,
authentication boundary, state ownership, atomic send and receive behavior,
fragmentation, retries, Composer-local deduplication, authenticated commit
acknowledgments, session renewal, compromise response, and conformance gates.
It also records a non-active cryptographic integration candidate named
`FOG-MSG-CANDIDATE-PQXDH-TR-MLKEM768-1`. The candidate combines the published
PQXDH asynchronous handshake with the published Triple Ratchet construction
and its ML-KEM Braid component. It is an evaluation target, not
`FOG-MESSAGING-1`, has no numeric profile identifier, is not authorized for a
public network, and does not by itself establish a deployed security claim.
The key words MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, and MAY describe
normative requirements in the sense of BCP 14 when they appear in uppercase.
## 2. Scope
`FOG-MESSAGING` owns:
- pairwise contact identities without global usernames;
- private contact identity cards and single-use receive vouchers;
- initiation roles and asynchronous session establishment;
- the fixed message envelope carried inside an encrypted storage record;
- authenticated application frames and their fixed plaintext layout;
- message-level state transitions and key lifecycle requirements;
- Composer-local retry, deduplication, fragment, and acknowledgment state;
- normal session renewal and identity-change behavior;
- recovery behavior after compromise or stale backup restore;
- the common messaging interface used by `fog-drop`, `fog-mailbox`, and
`fog-im`;
- parser limits, failure behavior, and conformance evidence.
This document does not own:
- KEMSphinx packet geometry, routes, SURBs, or packet replay state;
- mailbox capability derivation, storage record encryption, replication,
retention, tombstones, or empty-read behavior;
- adjacent-link Noise framing;
- entry capsules, return rendezvous, or transfer bundles;
- the concrete cover, retry, polling, or delay distributions;
- local Composer state encryption, update verification, or physical transfer;
- multi-device synchronization or group messaging.
Those contracts belong to `FOG-SPHINX-PROFILES`, `FOG-STORAGE`, `FOG-WIRE`,
the entry and rendezvous specifications, the cover profile, `FOG-COMPOSER`,
`FOG-SX`, and future `FOG-GROUP` work.
## 3. Security Boundary
The sender Composer constructs a messaging envelope before any online role
receives the work. The intended recipient Composer is the only role allowed to
authenticate and decrypt that envelope.
The full messaging envelope MUST be the protected plaintext of a fixed-size
`FOG-STORAGE` record. Its prefix and ratchet header are therefore not visible
to the blind relay, entry, mixes, courier, or storage replicas. KEMSphinx and
Noise add independent routing and adjacent-link protections but do not replace
message-level protection.
FOG-MESSAGING does not hide endpoint compromise, user behavior, screenshots,
malicious plaintext chosen by a contact, or disclosure by an intended
recipient. Fixed envelope size also does not hide timing, polling, retries, or
conversation activity unless the external traffic profile supplies sufficient
cover and scheduling.
### 3.1 Asynchronous dead-drop model
`fog-drop` is one cryptographic digital dead drop. `fog-mailbox` and `fog-im`
use sequences of rotating capability-addressed dead drops in FOG-STORAGE. The
sender and recipient do not need to be online together, and the recipient
Composer does not connect directly to a sender or replica.
A blind relay MAY transmit bounded opaque polling work prepared earlier by the
Composer and queue opaque results for later controlled import. It cannot hold
mailbox capability roots, derive new semantic operations, decrypt results, or
turn relay receipt into a message acknowledgment. FOG-MESSAGING is
asynchronous and does not promise real-time delivery.
## 4. Protocol Invariants
### MSG-INV-01: No global contact identifier
A contact is represented by a private pairwise identity and local user label.
The core protocol MUST NOT require a global username, phone number, email
address, public user directory, or stable network account.
### MSG-INV-02: Contact authentication begins out of band
A voucher cannot authenticate the human or organization that delivered it.
Before a contact is shown as verified, users MUST compare the complete
profile-bound fingerprint through an independently authenticated channel.
### MSG-INV-03: One voucher, one initiator, one session
A receive voucher authorizes exactly one designated initiator and exactly one
initial session. A valid first initialization consumes it. A second different
initialization using the same voucher is rejected.
### MSG-INV-04: Persist before export or release
The Composer MUST durably commit a send-side ratchet transition and its exact
immutable envelope before exporting it. It MUST durably commit a receive-side
ratchet transition and authenticated plaintext before rendering the message or
exporting an acknowledgment.
### MSG-INV-05: Retry does not advance the ratchet
A retry reuses the exact previously committed end-to-end envelope. It MUST NOT
derive a second message key or advance the ratchet again. FOG-STORAGE owns the
immutable box and courier-request generations used to place that envelope.
Every network retransmission uses new KEMSphinx packet material, route
randomness, entry material, SURB, rendezvous, and reply material.
### MSG-INV-06: Online roles receive no social identifier
Message IDs, session IDs, fragment IDs, application IDs, acknowledgment state,
ratchet headers, and contact identities remain inside the encrypted storage
record. Couriers MAY deduplicate opaque storage requests but MUST NOT receive a
FOG-MESSAGING identifier.
### MSG-INV-07: Acknowledgment means durable commit
An authenticated message acknowledgment means only that the receiving
Composer authenticated, decrypted, validated, and durably committed an
envelope. It MUST NOT mean that a human read, displayed, accepted, or acted on
the content.
### MSG-INV-08: Fixed external behavior
Drop, mailbox, private-message, chat, data, control, retry, and acknowledgment
operations use the same consensus-authorized storage and packet classes.
Application type MUST NOT select external geometry or an immediate response.
### MSG-INV-09: No automatic downgrade
A contact and session use one exact messaging profile. Unknown, retired, or
incompatible profiles stop processing. A failure MUST NOT activate an older
handshake, classical-only mode, smaller envelope, direct route, or plaintext.
### MSG-INV-10: Recovery never clones a live ratchet
Copying Composer files, restoring a stale backup, or adding a second device
MUST NOT create two active copies of one ratchet. Restored conversation state
enters recovery and requires a new authenticated session.
### MSG-INV-11: Ephemeral sessions do not resume
A RAM-only Ephemeral Composer MUST NOT resume an identity, ratchet, voucher,
capability sequence, deduplication window, acknowledgment stream, or pending
delivery after shutdown. Continuing `fog-mailbox` and `fog-im` sessions require
the Persistent Composer state contract defined by `FOG-COMPOSER`.
## 5. Contact Model
### 5.1 Pairwise identity
Each relationship has a unique Composer-generated contact root. Reusing one
root across unrelated contacts is forbidden because it creates an avoidable
cross-contact correlation handle.
A pairwise contact identity contains separate public keys for:
- the contact root signature, which binds the contact card, exact profile, and
authorized handshake identity;
- the exact identity key required by the selected asynchronous handshake.
The contact root does not sign ordinary message bodies. Message authenticity
after session establishment comes from the selected authenticated ratchet.
FOG does not claim deniability until the complete selected construction and
its FOG integration have been reviewed for that property.
Human-readable contact names, notes, avatars, and address-book groups are
Composer-local plaintext. They MUST NOT appear in identity cards, vouchers,
envelopes, capabilities, logs, or external bundles.
### 5.2 Contact identity card
A `ContactIdentityCard` is a private out-of-band object with these ordered
semantic fields:
```text
[
object_type,
format_version,
messaging_profile_offer,
pairwise_contact_root_public_key,
handshake_identity_public_key,
identity_binding,
sequence,
not_before,
not_after,
contact_root_signature
]
```
`identity_binding` MUST cover the exact handshake identity, primitive suite,
profile offer, card sequence, validity interval, and a domain separator. The
card MUST NOT contain a mailbox read capability or global lookup name.
The displayed verification fingerprint MUST commit to the canonical complete
card, the FOG network identifier, and the proposed messaging profile. The UI
MUST require a complete comparison or authenticated QR scan, not a short
user-selected substring.
### 5.3 Receive voucher
A `ReceiveVoucher` is a private single-use capability issued by the contact
that will answer the initial handshake. Its ordered semantic fields are:
```text
[
object_type,
format_version,
voucher_id,
issuer_identity_card,
intended_initiator_root_commitment,
messaging_profile_offer,
handshake_prekey_bundle,
initial_mailbox_write_grant,
storage_profile_requirement,
sequence,
not_before,
not_after,
flags,
issuer_contact_root_signature
]
```
The voucher ID MUST contain at least 256 bits from the Composer CSPRNG. The
root commitment MUST bind the complete intended initiator contact root and the
voucher domain. A claim-bearing pairwise voucher MUST NOT be bearer-capable.
The prekey bundle MUST contain every one-time classical and post-quantum prekey
required by the active profile. A claim-bearing FOG profile MUST NOT use a
last-resort reusable post-quantum prekey. Exhaustion stops new session
creation until a fresh voucher is exchanged.
`initial_mailbox_write_grant` authorizes deposits only into one dedicated
pairwise inbound stream. The selected storage construction MAY let its writer
derive the read capability and tombstone boxes in that same stream, in which
case the UI and security model MUST state that authority explicitly. The grant
MUST NOT expose any other inbound or outbound stream, capability registry,
identity key, recovery key, local database, or storage backup. FOG-STORAGE
defines its exact construction and rotation.
The issuer MUST atomically persist the voucher record, all corresponding
private prekeys, the write-grant state, validity, sequence, and consumption
status before the voucher can leave the Composer. Export failure does not
permit a second voucher object with the same ID or private prekeys.
### 5.4 Targeted contact ceremony
The initial pairwise ceremony is:
1. Both parties privately exchange `ContactIdentityCard` objects.
2. They verify the complete profile-bound fingerprints out of band.
3. They explicitly choose one session initiator.
4. The responder creates one `ReceiveVoucher` targeted to the chosen
initiator root.
5. The initiator imports and validates the voucher, then prepares one initial
session envelope.
6. That encrypted initial envelope carries a fresh return mailbox write grant
for the responder.
7. The responder consumes the voucher only while atomically committing the
valid initial session.
The initiator MUST NOT create a second initial envelope from the same voucher.
Until the first authenticated response is committed, only the initial content
envelope may be outstanding. Later user messages remain in a local application
queue. This bound prevents multiple independent initial sessions and limits
replay ambiguity.
### 5.5 Bearer and one-way drop vouchers
An explicitly marked bearer voucher provides weaker authentication. The first
holder able to complete a valid initialization wins. Such a contact MUST be
displayed as `ACTIVE_UNVERIFIED`, and the UI MUST state that possession of the
voucher does not identify a person.
`fog-drop` MAY use a separate one-time sealed drop voucher when the receiver
does not need an authenticated sender or continuing conversation. Each drop
requires an independent voucher and independent storage grant. A drop profile
MUST NOT claim ratchet forward secrecy, mutual contact authentication, or
post-compromise recovery that it does not implement.
## 6. Encoding of Contact Objects
Contact objects use `FOG-MSG-CBOR-1`, a restricted deterministic CBOR profile:
- the top-level and nested structures are arrays with fixed field order;
- integers are unsigned and use their shortest encoding;
- byte strings have exact profile-defined lengths;
- text is forbidden except for a narrowly bounded protocol-owned ASCII label
if a later exact profile requires one;
- maps, floats, tags, indefinite lengths, compression, duplicate fields,
unknown fields, and trailing bytes are forbidden;
- one complete object is at most 65,536 bytes;
- parsers re-encode a valid object and require byte-for-byte equality.
Signatures cover a domain separator and the canonical array with the signature
field omitted. The algorithm is selected by the exact messaging profile, not
by an unauthenticated field supplied to a generic verifier.
Contact objects are confidential social-graph material even though they carry
public keys. They MUST NOT be published in consensus, a public directory,
telemetry, logs, crash reports, or ordinary support bundles.
## 7. Messaging Profile Registry
Every active messaging profile is an immutable mapping from one non-zero
unsigned 32-bit identifier to exact dependencies and behavior. An identifier
MUST NOT be reused after any byte layout, primitive, limit, state transition,
or failure rule changes.
An exact profile record includes at least:
```text
[
messaging_profile_id,
contact_encoding_id,
contact_root_signature_suite_id,
handshake_specification_and_revision,
handshake_primitive_suite_id,
ratchet_specification_and_revision,
ratchet_primitive_suite_id,
exporter_kdf_id,
envelope_format_id,
envelope_header_area_length,
envelope_ciphertext_area_length,
ratchet_aead_id,
storage_profile_compatibility_id,
maximum_skip,
maximum_pending_messages,
maximum_fragment_count,
maximum_complete_message_length,
maximum_concurrent_fragment_groups,
retry_profile_id,
retention_profile_id,
parser_limits_profile_id,
conformance_vector_set_id
]
```
The accepted FOG consensus authorizes usable profile IDs. Contact cards and
vouchers narrow that authenticated set but cannot activate an absent profile.
No party negotiates by trial, and no message advertises alternatives.
An incomplete candidate does not receive a numeric ID. An implementation MUST
NOT accept a candidate name where an active numeric profile ID is required.
## 8. Fixed Message Envelope
### 8.1 Storage relationship
One `MessageEnvelope` is the fixed-size plaintext of one compatible encrypted
FOG-STORAGE record. FOG-STORAGE MUST authenticate the entire fixed record,
including all messaging padding, before exposing it to FOG-MESSAGING.
For a profile with fixed ratchet header area `H` and fixed ratchet ciphertext
area `C`:
```text
message_envelope_length = 64 + H + C
```
Activation requires an explicit compatibility record proving:
```text
message_envelope_length
<= storage_record_plaintext_capacity
storage_operation_length
<= compatible KEMSphinx user payload capacity
```
The current 4,096-byte KEMSphinx candidate user payload is not automatically
the messaging capacity. FOG-STORAGE still owns request framing, capability
material, encryption overhead, and replica operation geometry.
### 8.2 Envelope prefix
The exact 64-byte prefix is:
```text
offset length field
0 2 envelope_format_version
2 1 envelope_kind
3 1 flags
4 4 messaging_profile_id
8 8 conversation_generation
16 32 lookup_id
48 2 actual_ratchet_header_length
50 14 reserved
```
All integers are unsigned network byte order. `flags` and `reserved` are zero
in the first format. Unknown kinds, non-zero reserved values, invalid profile
IDs, impossible lengths, and trailing bytes are rejected.
`envelope_kind` is `INIT` or `RATCHET`. For `INIT`, `lookup_id` is the voucher
ID. For `RATCHET`, it is the session ID. Both remain hidden from online roles
by FOG-STORAGE encryption.
The INIT handshake transcript MUST authenticate the proposed session ID,
voucher ID, conversation generation, envelope prefix, complete padded header
area, both contact identity bindings, and both mailbox write-grant contexts.
The RATCHET associated data MUST authenticate the prefix and complete padded
header area.
### 8.3 Header and ciphertext areas
The ratchet header is serialized exactly as required by the active profile and
right-padded with zero bytes to `H`. Its unpadded size must equal
`actual_ratchet_header_length`. A parser MUST verify every padding byte after
successful authentication.
An INIT header MUST carry the exact upstream initialization data and one
proposed 256-bit session ID generated by the initiator CSPRNG. That session ID
is unique to the new conversation generation and becomes the `lookup_id` of
later RATCHET envelopes after the responder authenticates and commits INIT.
The fixed ciphertext area `C` contains one AEAD-protected fixed plaintext
frame. The active ratchet derives the message key and nonce according to its
unmodified specification and profile binding. FOG MUST NOT reuse a nonce with
a key, improvise a second encryption layer with the same key, or expose a raw
message key to an application module.
The fixed prefix and complete padded header area are associated data. A
message is not valid until the ratchet authentication, frame parsing, profile,
generation, sequence, and application-state checks all succeed.
### 8.4 Encrypted plaintext frame
The fixed frame header is exactly 144 bytes:
```text
length field
2 frame_version
1 frame_kind
1 application_id
2 flags
2 reserved
8 conversation_generation
8 send_index
16 message_id
8 ack_base
32 ack_bitmap
16 fragment_group_id
2 fragment_index
2 fragment_count
4 complete_content_length
2 content_kind
2 content_length
32 complete_content_digest
2 control_code
2 reserved_2
N content_and_zero_padding
```
`N` is fixed by the profile. `content_length` selects the initial content
bytes; every remaining byte is zero and is checked after AEAD authentication.
Compression is forbidden.
`frame_kind` is `DATA`, `ACK_ONLY`, or `CONTROL`. `application_id` identifies
`DROP`, `MAILBOX`, or `IM` only inside the encrypted frame. Unknown values,
flag bits, controls, or content kinds are critical and cause rejection.
`send_index` begins at 1 for each direction and conversation generation and
increases without wrap. `message_id` is an independent random 128-bit value
for one envelope. It is not used by an online role or as a cryptographic key.
Version 1 content kinds are strict UTF-8 text, bounded opaque bytes, and
protocol control. The renderer MUST NOT interpret HTML, scripts, office
documents, archives, executable formats, active links, remote resources, or
embedded previews. Opaque bytes receive a neutral locally generated filename
and require explicit user action before export.
Typing state, presence, last-seen state, delivery timestamps, read receipts,
remote avatars, automatic URL fetches, and external MIME resolution are not
part of the first profile.
## 9. Fragmentation and Reassembly
A semantic message larger than one frame is split by FOG-MESSAGING before
ratchet encryption. Every fragment is an independent ratchet message with its
own `send_index`, random `message_id`, message key, fixed envelope, storage
record, KEMSphinx packet, and outer reply material.
Fragments of one semantic message share:
- one random 128-bit `fragment_group_id`;
- the exact `fragment_count`;
- a zero-based `fragment_index`;
- `complete_content_length`;
- a profile-selected digest of the complete unfragmented content;
- the same application and content kind.
The first profile MUST impose limits no larger than:
- 256 fragments per semantic message;
- 1,048,576 complete content bytes;
- 16 incomplete fragment groups per contact;
- one profile-defined total incomplete-byte budget per Composer.
Exact active limits MAY be lower. They MUST be identical for all native
applications using the profile.
The Composer MUST durably commit all accepted fragments and verify count,
indexes, length, kind, and complete digest before releasing any part to the
application. Missing, conflicting, expired, oversized, or invalid groups are
discarded without partial rendering and without a distinguishable network
response.
## 10. Session State Machine
### 10.1 Contact and session states
The minimum states are:
- `INVITED`: a validated identity card or unconsumed voucher exists;
- `INIT_READY`: one initial envelope may be built by the chosen initiator;
- `INIT_SENT`: the immutable initial envelope is committed and may be retried;
- `ACTIVE_UNVERIFIED`: the session authenticates a key but no authentic human
fingerprint comparison is recorded;
- `ACTIVE_VERIFIED`: the pairwise identity and session are verified;
- `RENEWING`: an authenticated transition to a new profile or generation is
in progress;
- `FROZEN_IDENTITY_CHANGE`: an unexpected contact identity change blocks
sends and ordinary receives;
- `RECOVERY_REQUIRED`: restored, inconsistent, rolled-back, or suspected
compromised state cannot continue;
- `CLOSED`: the local relationship is intentionally terminated.
State movement is monotonic except for a reviewed renewal that creates a new
conversation generation. Reopening `CLOSED`, `FROZEN_IDENTITY_CHANGE`, or
`RECOVERY_REQUIRED` requires an explicit new contact ceremony or recovery
protocol, not a file edit or network response.
### 10.2 Initial session
The initiator validates the voucher, active consensus profile, targeted root
commitment, validity, sequence, prekey signatures, one-time prekey presence,
and storage compatibility before creating an INIT envelope.
The responder performs all parsing and cryptographic work against staged
state. A valid INIT must bind the selected voucher, both pairwise identities,
the proposed random session ID, conversation generation, return mailbox write
grant, and initial content. Only then may the responder atomically:
1. mark the voucher consumed by the exact initial-envelope digest;
2. commit the resulting session and receive state;
3. commit the inbox content and acknowledgment state;
4. delete the consumed one-time private prekeys;
5. permit a scheduled authenticated response.
An exact retry maps to the already committed result. A different INIT for a
consumed voucher is rejected. Failure before the atomic commit leaves the
voucher usable and does not delete its private prekeys.
### 10.3 Established session
An established session has one sending and one receiving direction per
conversation generation. The active profile owns the precise ratchet header,
message-key derivation, skipped-key handling, and post-compromise state.
Application modules never mutate a ratchet directly. They submit bounded
semantic content to the messaging transaction and receive authenticated
committed content from it.
## 11. Atomic State Transitions
### 11.1 Send transaction
For each new envelope, the Composer MUST:
1. validate local contact, profile, queue, fragment, and generation bounds;
2. clone or transactionally stage the current sending ratchet;
3. derive exactly one message key and construct the exact fixed envelope;
4. atomically persist the new ratchet state, immutable envelope, send index,
application queue transition, retry metadata, and outbox status;
5. erase the message key and discarded staged state;
6. only after successful commit, make the envelope eligible for export.
A crash before step 4 produces no exportable envelope. A crash after step 4
recovers the exact envelope without deriving another message key.
### 11.2 Receive transaction
For each imported storage record, the Composer MUST:
1. authenticate and decrypt the fixed FOG-STORAGE record;
2. parse the 64-byte prefix and select one local invitation or session;
3. check the keyed exact-envelope deduplication store;
4. stage the handshake or ratchet transition without modifying live state;
5. authenticate, decrypt, and strictly validate the complete frame;
6. validate generation, sequence, replay window, fragments, application
bounds, and acknowledgment summary;
7. atomically persist new ratchet state, dedup state, inbox or reassembly
state, receive window, and peer acknowledgment effects;
8. erase message, skipped, and discarded staged keys according to the profile;
9. only after commit, release complete content or schedule an acknowledgment.
Authentication, parsing, policy, resource, or commit failure discards staged
state and releases no plaintext. The network-facing outcome stays within the
same coarse storage and cover class.
### 11.3 Local durability requirement
Ratchet state, outbox state, voucher consumption, one-time prekey deletion,
dedup insertion, and inbox commit form security-critical transactions. A
profile is not conforming if its storage engine can acknowledge one of these
effects while losing the others after power failure.
FOG-COMPOSER defines structural encrypted local storage, commit-before-effect,
and honest rollback-assurance levels. This specification requires transaction
ordering but does not activate a database or external-anchor profile.
## 12. Retry and Outbox Semantics
The minimum outbox states are:
- `PENDING`: committed and eligible for first export;
- `EXPORTED`: exported at least once and waiting for authenticated commit ACK;
- `ACKED`: covered by an authenticated peer acknowledgment;
- `EXPIRED`: retry age or attempt limit was reached;
- `CANCELLED`: locally cancelled before export where profile rules permit;
- `UNCERTAIN`: local import or durability evidence is insufficient and
automatic state advancement is unsafe.
An immutable message envelope in `EXPORTED` is never reconstructed.
FOG-STORAGE atomically creates one immutable box record and one immutable
courier request generation. Retransmissions within that generation reuse its
exact storage bytes for courier deduplication, while every transmission uses a
fresh KEMSphinx packet, SURB, private reply token, route, rendezvous, and entry
capsule. A later storage request generation may reencrypt the same box only
under the bounded FOG-STORAGE transition and nonce rules.
The retry profile defines maximum attempts, maximum age, backoff classes,
jitter source, queue budget, and expiry. It MUST be consensus-authenticated and
validated by simulation. A real retry MUST wait for its scheduled traffic slot
and MUST NOT create an immediate application-specific burst.
Expiry means delivery is unknown, not that the peer certainly failed to
commit. The UI MUST distinguish `ACKED`, `UNACKNOWLEDGED`, and local processing
failure without claiming network certainty.
## 13. Composer-Local Deduplication
The exact envelope deduplication identifier is stored only inside the
Composer:
```text
dedup_id = MAC(K_dedup, profile_domain || HASH(message_envelope))
```
The active profile defines the exporter that derives `K_dedup`, the hash, MAC,
domains, and key rotation. The formula is a protocol interface, not permission
to select arbitrary primitives.
For INIT, the voucher record also binds the first committed envelope digest.
For RATCHET, the session-local dedup store is checked before attempting a
ratchet transition. This permits an exact retry to be recognized after its
message key has been deleted.
Dedup entries persist for at least the maximum retry age plus the profile's
recovery margin. Count and byte limits are mandatory. Eviction MUST NOT select
entries based on application type or message content. If safe deduplication
cannot be guaranteed, the session enters a coarse local error or recovery
state rather than accepting a conflicting replay.
Message deduplication MUST NOT be delegated to a courier or replica. Their
separate bounded request replay rules operate on opaque storage operations and
must not receive `message_id`, `send_index`, or `session_id`.
## 14. Authenticated Commit Acknowledgments
### 14.1 Acknowledgment window
Every encrypted plaintext frame contains one fixed acknowledgment summary for
the peer-to-local direction:
- `ack_base` is the largest contiguous peer `send_index` durably committed;
- bitmap bit 0 represents `ack_base + 1`;
- bitmap bit 255 represents `ack_base + 256`;
- a set bit means that exact out-of-order index is durably committed.
Indexes at or below `ack_base` are acknowledged. A sender updates its outbox
only after the summary arrives inside an authenticated, committed ratchet
message of the expected session and generation.
### 14.2 ACK_ONLY
If no data or control message is due, the Composer MAY create `ACK_ONLY` in a
normal scheduled message slot. It has the same envelope and external geometry
as all other frames and advances the ratchet exactly once.
Receiving `ACK_ONLY` MUST NOT by itself trigger an acknowledgment. A later
ordinary frame may naturally summarize its committed index, but there is no
immediate ACK-of-ACK exchange. An ACK_ONLY envelope need not remain in an
ack-wait outbox after its bounded export policy completes.
A duplicate data envelope may cause the next scheduled summary to repeat its
acknowledgment, but MUST NOT cause an immediate distinguishable reply.
### 14.3 Meaning and fragment behavior
`MESSAGE_COMMIT_ACK` is the only version 1 message delivery acknowledgment. It
means successful Composer commit. Network acceptance, courier success, replica
quorum, relay import, display, read state, and user action are different facts
and MUST NOT be presented as this acknowledgment.
Acknowledging a fragment means that fragment was committed. It does not mean
the complete semantic message was reassembled or displayed.
## 15. Limits and Malicious Contacts
Every active profile defines exact equal limits for all native applications.
The first profile MUST NOT exceed:
- `maximum_skip`: 1,024 ratchet message keys;
- `maximum_pending_messages`: a profile-fixed count and byte budget per
contact and Composer;
- `maximum_fragment_count`: 256;
- `maximum_complete_message_length`: 1,048,576 bytes;
- `maximum_concurrent_fragment_groups`: 16 per contact;
- one 65,536-byte contact object;
- unsigned 64-bit generations and sequence counters with no wrap.
Skipped message keys are retained only within the exact ratchet bound and are
securely deleted after use or expiry. The parser validates sizes before
allocation and performs bounded cryptographic work per imported record.
A malicious contact can send authenticated abusive content and attempt state,
CPU, storage, fragment, skip-window, or notification exhaustion. FOG therefore
requires per-contact and global quotas, explicit mute and close controls,
bounded notifications, no active rendering, and no response amplification.
Cryptographic authentication is not content safety.
## 16. Session Renewal and Profile Transition
A normal renewal begins only inside an authenticated active ratchet. The old
session carries a contact-root-signed offer that commits to the exact new
profile, new conversation generation, fresh one-time initialization material,
and storage transition context.
Renewal is two-phase:
1. both sides durably commit and authenticate the same transition offer;
2. both sides confirm the new generation before retiring the old sending
state.
Old and new generations have distinct session IDs, ratchet state, dedup keys,
storage grants, and outboxes. Messages MUST NOT be decrypted under both. There
is no fallback from a failed new profile to an older one.
An unexpected contact root or handshake identity change is not ordinary
renewal. The session enters `FROZEN_IDENTITY_CHANGE`, displays both old and new
profile-bound fingerprints locally, and requires fresh out-of-band
verification.
## 17. Backup, Restore, and Compromise
### 17.1 Backup boundary
An independently encrypted backup MAY contain pairwise contact roots, verified
fingerprints, local labels, user-approved history, and metadata needed to
start recovery. Backup keys remain separate from Composer state-encryption and
messaging keys.
An initial profile MUST treat active ratchet state, skipped message keys,
unconsumed one-time prekeys, live vouchers, retry material, and live mailbox
capabilities as non-resumable after a potentially stale restore. Restored
conversations enter `RECOVERY_REQUIRED` and cannot send or accept ordinary
ratchet traffic until a fresh authenticated session is established.
This sacrifices seamless restore to prevent two restored copies from reusing
message keys, accepting conflicting histories, or impersonating one live
device state.
### 17.2 Suspected compromise
Post-compromise ratcheting does not defeat an attacker that remains active on
the Composer, controls randomness, or controls the long-term identity. On
suspected endpoint or identity compromise, the user MUST replace the affected
device or image, create new identity material, revoke or abandon old storage
capabilities where possible, and reauthenticate contacts out of band.
No UI may promise that sending a few more messages automatically removes an
active attacker.
### 17.3 Multi-device exclusion
The first profile supports one active Composer instance per pairwise identity.
File copying, shared storage, or concurrent import on two devices is forbidden.
Multi-device use requires a separate protocol with explicit device identity,
session convergence, revocation, and metadata analysis.
## 18. Group Messaging Exclusion
Pairwise fan-out is not a group security protocol. It leaks different delivery
and membership patterns, lacks one coherent group epoch, and does not by
itself define removal, update, or post-compromise semantics.
Group messaging is deferred to a future `FOG-GROUP` specification. That work
SHOULD evaluate Messaging Layer Security under RFC 9420 and its architecture
considerations in RFC 9750, while adapting identity and delivery only through
explicit FOG trust-boundary analysis. The initial `fog-im` profile is pairwise
only.
## 19. Candidate Cryptographic Integration
### 19.1 Candidate definition
`FOG-MSG-CANDIDATE-PQXDH-TR-MLKEM768-1` evaluates:
- PQXDH revision 3 for asynchronous initial shared-secret establishment;
- Double Ratchet specification revision 4 Triple Ratchet integration;
- ML-KEM Braid specification revision 1 using ML-KEM-768;
- the exact classical ratchet and authenticated-encryption dependencies
required by those maintained specifications;
- a separate FOG pairwise contact-root signature that binds, but does not
replace or weaken, the exact PQXDH identity and prekey checks;
- FOG fixed framing, atomic storage rules, voucher policy, and outer
FOG-STORAGE protection defined in this document.
The candidate requires one-time classical and signed one-time ML-KEM prekeys.
FOG deliberately excludes the reusable last-resort PQ prekey from a
claim-bearing profile. Loss of fresh vouchers therefore affects availability.
### 19.2 Claims deliberately withheld
The candidate does not yet establish:
- post-quantum authentication, because the reviewed PQXDH authentication claim
still depends on the specified classical authentication assumptions;
- complete FOG post-quantum security merely from ML-KEM inclusion;
- deniability;
- post-compromise recovery under message loss or continued active compromise;
- safe backup, multi-device, or group behavior;
- compatibility with the calculated 4,096-byte KEMSphinx payload;
- implementation or side-channel safety.
### 19.3 Activation gates
Before promotion to an active numeric profile, FOG MUST freeze and verify:
1. exact upstream specification revisions and all primitive identifiers;
2. one maintained implementation strategy with constant-time and secret
deletion review;
3. byte-exact PQXDH, Triple Ratchet, ML-KEM Braid, FOG envelope, and contact
object serialization;
4. transcript and domain separation, including the outer pairwise root
binding;
5. exact header area, ciphertext area, content capacity, and storage geometry;
6. loss, reordering, skip-window, retry, crash, restore, and compromise state
transitions;
7. deterministic positive and negative vectors at every boundary;
8. fuzzing and resource tests for all untrusted inputs;
9. primitive, integration, side-channel, and lifecycle review;
10. independent security review before a public security claim.
FOG MUST preserve the upstream constructions rather than silently changing
their KDF inputs, message-key combination, epoch behavior, or prekey checks.
Any required departure creates a separately analyzed candidate.
## 20. Key and Secret Lifecycle
| Material | Owner | Persistence | Required destruction or transition |
| --- | --- | --- | --- |
| Pairwise contact root private key | one Composer identity | long-term encrypted state; separately encrypted backup MAY be allowed | replace on compromise or explicit identity migration |
| Handshake identity private key | one Composer pairwise identity | long-term encrypted state | replace with contact root on compromise |
| Signed classical prekey | voucher issuer Composer | bounded by voucher and profile validity | delete after expiry and all bound voucher states retire |
| Classical one-time prekey | voucher issuer Composer | persisted before voucher export | delete atomically after valid INIT commit |
| ML-KEM one-time prekey | voucher issuer Composer | persisted before voucher export | delete atomically after valid INIT commit |
| Initiator ephemeral handshake secrets | initiator Composer | transaction only | erase after committed initial state and envelope |
| Handshake shared secrets | both Composers | transaction only | erase after ratchet and exporter state is derived |
| Classical ratchet private key | one session direction | encrypted mutable session state | erase when the reviewed ratchet transition permits |
| ML-KEM Braid state | one session | encrypted mutable session state | erase retired epochs and session on close or recovery |
| Chain key | one session direction | encrypted mutable session state | replace and erase on every chain advance |
| Message key | one envelope | transaction only | erase after durable send ciphertext or receive commit |
| Skipped message key | receiving Composer | bounded encrypted state | erase on use, expiry, bound overflow, or session retirement |
| Session dedup key | one session generation | encrypted mutable state | erase after retry and recovery retention ends |
| Pairwise inbound stream writer grant | authorized remote contact | deposits, reads where derivable, and tombstones only in that dedicated stream | abandon and replace through FOG-STORAGE rules after compromise or closure |
| Pairwise inbound stream reader state | receiving Composer; writer MAY derive under selected profile | read, verify, and advance that dedicated stream | never expose access to any other stream or capability registry |
| Local state-encryption key | Composer profile | protected local state | separate from message, storage, and backup keys |
| Backup key | user recovery domain | outside backup ciphertext | never derive from a contact or ratchet key |
Secret values, plaintext, capabilities, contact objects, fingerprints,
session IDs, message IDs, ratchet headers, and envelope digests MUST NOT enter
logs, metrics, command arguments, crash reports, or support artifacts.
## 21. Failure Behavior
Externally observable failures are coarse and non-amplifying. Online roles do
not learn whether a storage record held an INIT, data fragment, duplicate,
ACK_ONLY, invalid ratchet message, or cover object.
The Composer MAY show a local reason such as invalid voucher, verification
needed, incompatible profile, authentication failure, resource limit, stale
state, or recovery required. It MUST NOT automatically send an error message
to an unverified or unauthenticated source.
Unknown-critical fields, malformed UTF-8, invalid padding, overflow, counter
wrap, conflicting fragment metadata, excessive skips, expired vouchers,
identity changes, state rollback, and storage commit uncertainty fail closed.
Failure MUST NOT alter packet size, route length, polling rate, or security
profile outside the authenticated cover policy.
## 22. Conformance and Test Requirements
Before the local PoC, FOG-MESSAGING requires tests or vectors for:
- canonical contact card and voucher encoding and rejection;
- profile-bound fingerprint derivation and targeted voucher checks;
- issue-before-export and consume-once voucher transactions;
- exact 64-byte prefix and 144-byte frame header;
- every envelope kind, frame kind, application ID, and content kind;
- prefix, header-padding, frame-padding, transcript, and generation binding;
- send crash before commit and after commit;
- receive crash before commit and after commit;
- exact retry without a second ratchet advance;
- immutable storage request retransmission with fresh KEMSphinx, SURB,
rendezvous, route, and entry material;
- exact duplicate, conflicting INIT, replay, old generation, and excessive
skip handling;
- ACK base and bitmap boundaries, reordering, loss, duplicate data, and no
ACK-of-ACK trigger;
- fragment loss, duplication, conflict, timeout, digest failure, and quota
exhaustion;
- malicious contact CPU, memory, disk, notification, and parser bounds;
- normal renewal, profile transition, identity change, stale restore, and
suspected compromise;
- secure deletion instrumentation where the platform can provide evidence;
- candidate upstream and FOG integration vectors;
- absence of prohibited fields in logs, telemetry, storage requests, and
external bundles;
- identical external geometry and scheduling class across native apps,
retries, ACKs, and cover operations.
Tests MUST include deterministic positive and negative vectors, property
tests, fuzzing, transaction fault injection, power-loss simulation, and
cross-implementation checks before profile activation.
## 23. Threat and Architecture Traceability
| Requirement | Primary controls |
| --- | --- |
| `ARC-002` | networkless Composer owns every contact, ratchet, plaintext, and envelope transition |
| `ARC-004` | message envelope remains independent from KEMSphinx and Noise |
| `ARC-006` | fixed storage and packet classes hide native application selection from core roles |
| `ARC-007` | pairwise, ratchet, storage, transport, node, and backup keys have distinct owners and purposes |
| `ARC-008` | strict object, envelope, queue, fragment, skip, retry, and parser bounds |
| `ARC-009` | authentication, state, and profile failures stop without bypass or downgrade |
| `IF-01` | only a committed immutable envelope may enter a Composer export bundle |
| `IF-02` | imported storage results are untrusted until storage and messaging validation commit |
| `IF-08` | courier receives only an opaque storage operation, never messaging fields |
| `IF-09` | storage carries a fixed encrypted record whose plaintext is the message envelope |
| `TM-NET-02` | directional pairwise stream grants, external schedule, encrypted messaging identifiers, no immediate ACK |
| `TM-NET-04` | exact-envelope dedup, consume-once voucher, fresh outer retry material |
| `TM-NET-05` | authenticated ratchet and transcript, strict padding and generation binding |
| `TM-NET-06` | consensus-authorized immutable profile and application-independent behavior |
| `TM-ENDPOINT-01` | networkless state owner, transaction ordering, recovery freeze |
| `TM-ENDPOINT-03` | no live-ratchet backup restore or file-copy multi-device state |
| `TM-APP-01` | authenticated complete reassembly, bounded content, no active rendering |
| `TM-OPS-01` | no contact, capability, identifier, digest, payload, or ratchet data in telemetry |
| `TM-CRYPTO-01` | exact lifecycle table, purpose separation, secure deletion requirements |
| `TM-CRYPTO-02` | fixed profiles, no trial negotiation, fallback, or candidate activation |
| `TM-AVAIL-01` | bounded retries, skip windows, fragments, queues, contact objects, and non-amplifying errors |
## 24. Open Dependencies
The structural messaging contract is fixed, but these dependencies remain
open before an active profile or daemon:
- exact primitive suite, dependency revisions, and maintained implementation
for `FOG-MSG-CANDIDATE-PQXDH-TR-MLKEM768-1`;
- license, API-support, Rust integration, panic, secret-lifecycle, and formal
artifact review for the selected messaging implementation path;
- exact contact-root signature and fingerprint representation;
- byte-exact candidate serialization and integration vectors;
- exact fixed `H`, `C`, content capacity, and lower active limits;
- activation evidence and byte-exact geometry for the structural FOG-STORAGE
contract and its non-active BACAP/Pigeonhole candidate;
- authenticated retry, cover, polling, and expiration distributions;
- activation evidence for the structural FOG-COMPOSER vault, database,
external-anchor, recovery, and update-consumer profiles;
- identity revocation and fresh-session recovery ceremony;
- future multi-device and group protocols.
No implementation convenience may silently resolve these dependencies.
## 25. Primary References
- Signal PQXDH specification, revision 3:
- Signal Double Ratchet specification, revision 4:
- Signal ML-KEM Braid specification:
- Signal Sesame asynchronous session management specification:
- NIST FIPS 203, Module-Lattice-Based Key-Encapsulation Mechanism Standard:
- Katzenpost contact voucher specification:
- Katzenpost contact voucher narration:
- RFC 9420, The Messaging Layer Security Protocol:
- RFC 9750, The Messaging Layer Security Architecture:
- FOG threat model: `FOG-THREAT-MODEL.md`
- FOG architecture: `FOG-ARCHITECTURE.md`
- FOG Sphinx profile framework: `FOG-SPHINX-PROFILES.md`
- FOG storage protocol: `FOG-STORAGE.md`
- FOG Composer protocol: `FOG-COMPOSER.md`
- FOG cryptographic suite evaluation: `FOG-CRYPTO-SUITES.md`
These references supply maintained constructions and design lessons. They do
not make the FOG integration secure by inheritance. FOG still requires exact
profiles, compatibility calculations, vectors, tests, lifecycle analysis,
simulation, implementation review, and independent security review.