summaryrefslogtreecommitdiffstats
path: root/docs/FOG-MESSAGING.md
blob: 69b1ffd543a2e4ba53d68c9d8dc56a5fb9bff2bd (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
# 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.

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

## 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:
  <https://signal.org/docs/specifications/pqxdh/>
- Signal Double Ratchet specification, revision 4:
  <https://signal.org/docs/specifications/doubleratchet/>
- Signal ML-KEM Braid specification:
  <https://signal.org/docs/specifications/mlkembraid/>
- Signal Sesame asynchronous session management specification:
  <https://signal.org/docs/specifications/sesame/>
- NIST FIPS 203, Module-Lattice-Based Key-Encapsulation Mechanism Standard:
  <https://csrc.nist.gov/pubs/fips/203/final>
- Katzenpost contact voucher specification:
  <https://katzenpost.network/docs/specs/contactvoucher/>
- Katzenpost contact voucher narration:
  <https://katzenpost.network/docs/specs/contact_voucher_narration/>
- RFC 9420, The Messaging Layer Security Protocol:
  <https://www.rfc-editor.org/rfc/rfc9420.html>
- RFC 9750, The Messaging Layer Security Architecture:
  <https://www.rfc-editor.org/rfc/rfc9750.html>
- 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.