# FOG Wire Protocol Status: Draft 0.1 Date: 2026-08-08 ## 1. Purpose This document defines `FOG-WIRE-1`, the authenticated transport protocol used between adjacent online FOG roles. It refines the following baselines: - `FOG-THREAT-MODEL.md`, especially `TM-NET-01`, `TM-NET-03`, `TM-NET-04`, `TM-NET-05`, `TM-NET-06`, `TM-PKI-02`, `TM-ROLE-01`, `TM-ROLE-02`, `TM-ROLE-03`, `TM-OPS-01`, `TM-CRYPTO-01`, `TM-CRYPTO-02`, and `TM-AVAIL-01`; - `FOG-ARCHITECTURE.md`, especially `ARC-003`, `ARC-004`, `ARC-005`, `ARC-006`, `ARC-007`, `ARC-008`, `ARC-009`, `IF-03`, `IF-04`, `IF-06`, `IF-07`, `IF-08`, `IF-09`, `IF-10`, and `IF-11`; - `FOG-PKI.md`, especially consensus-authorized profiles, endpoints, role-specific epoch public keys, monotonic profile state, and hard expiry. FOG is not implemented. Requirements in this document are protocol targets, not statements about deployed security. 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-WIRE-1` owns: - the version-1 TCP carrier profile; - selection of one exact consensus-authorized Noise profile; - responder-only authentication for relay-to-entry links; - mutual authentication for node, authority, storage, and observer links; - binding of network, epoch, consensus, role, peer, key, and link context to the Noise handshake; - handshake and transport framing; - fixed-size encrypted records and bounded logical-message fragmentation; - command allowlists by link context and direction; - per-record cipher-state rekey and bounded fresh handshakes; - epoch and profile transition behavior; - connection ownership, timeouts, backoff, overload, and failure behavior; - key lifecycle, privacy-safe diagnostics, and conformance requirements. `FOG-WIRE-1` does not own: - message-level end-to-end encryption; - KEMSphinx packet construction, routing, replay tags, or SURBs; - the entry submission capsule or return rendezvous; - storage capability semantics or replica durability; - application acknowledgments, retries, or deduplication; - mix delays, route selection, cover rates, or retrieval schedules; - consensus construction or profile authorization; - local administration, release updates, public mirror HTTP, or Composer transfer formats. Noise protects one adjacent connection. It does not hide IP endpoints, TCP connection existence, timing, direction, duration, or byte volume. It does not replace KEMSphinx or end-to-end message encryption. ## 3. Security Goals FOG-WIRE MUST provide, for one accepted connection: - authentication of every required online endpoint before application work; - anonymity at the Noise identity layer for a blind relay connecting to an entry; - confidentiality and integrity of transport records after the handshake; - forward secrecy to the extent supplied by the selected reviewed Noise or PQNoise profile and correct erasure of ephemeral state; - exact authorization of role adjacency and command direction; - authenticated agreement on the network, epoch, consensus, profile, roles, node identifiers, and transport-key identifiers; - no silent suite, pattern, carrier, role, or version downgrade; - fixed ciphertext length for records within one profile; - bounded parsing, allocation, reassembly, cryptographic work, and queues; - coarse non-amplifying remote failures; - deterministic profile and epoch retirement. FOG-WIRE does not provide: - anonymity against an observer who can correlate connection timing or volume; - protocol camouflage or censorship resistance; - authentication of a blind relay as a stable user or network identity; - application-message authenticity or recipient authentication; - KEMSphinx replay protection; - delivery, liveness, or availability against sustained denial of service; - safe operation after compromise of both endpoint processes; - post-quantum security merely because one profile contains a post-quantum primitive. ## 4. Protocol Invariants ### WIRE-INV-01: One authenticated profile Every connection uses exactly one immutable wire profile authorized by the accepted consensus. Peers MUST NOT negotiate a list, guess a profile, or fall back after failure. ### WIRE-INV-02: One link context Every connection has exactly one link context that fixes the initiator role, responder role, authentication mode, permitted direction, and command allowlist. ### WIRE-INV-03: No early application data Every Noise handshake payload is empty in version 1. No FOG command is sent, accepted, buffered as trusted, or acted upon before the handshake completes and peer authorization succeeds. Session resumption, PSK resumption, and 0-RTT application data are forbidden. ### WIRE-INV-04: Consensus is the authority Operator configuration MAY restrict peers further, but MUST NOT add a peer, role, endpoint, key, context, or profile absent from the accepted consensus or trusted authority manifest. ### WIRE-INV-05: Fixed encrypted records All post-handshake records on one connection have the exact ciphertext length defined by its profile. Application type and semantic body length MUST NOT change that record length. ### WIRE-INV-06: Strict ordered state Version 1 runs over ordered TCP. Noise transport nonces and cipher-state rekeys advance exactly once per successfully sent or received record. A missing, duplicated, unauthentic, or out-of-order record terminates the connection. ### WIRE-INV-07: Purpose-separated keys Noise static keys, node identity keys, authority vote keys, KEMSphinx keys, entry capsule keys, replica-envelope keys, message keys, queue-sealing keys, and release keys are distinct. One key MUST NOT serve two of these purposes. ### WIRE-INV-08: Bounded work before trust Preface, handshake, frame, record, fragment, connection, and queue work is bounded before allocation proportional to attacker-controlled input. A peer cannot request an arbitrary algorithm, record size, or reassembly budget. ### WIRE-INV-09: Failure does not widen access Failure never causes plaintext transport, direct client fallback, layer skipping, a new role edge, an older profile, a previous epoch, or a generic RPC path. ### WIRE-INV-10: Traffic scheduling is external but mandatory FOG-WIRE supplies fixed records and a padding record. The authenticated cover profile decides when records are emitted. A local keepalive choice MUST NOT silently replace or modify that traffic schedule. ## 5. Terminology and Byte Order Terms used in this specification: - **wire profile**: immutable mapping from one numeric profile identifier to an exact carrier, Noise protocol name, record geometry, limits, timers, socket behavior, and compatible command-shape registry; - **link context**: immutable role adjacency and directional command policy; - **preface**: the fixed 32-byte cleartext connection header; - **prologue**: the canonical byte string supplied to both Noise handshake states and authenticated by the handshake transcript; - **record**: one fixed-size plaintext structure encrypted as one Noise transport message; - **logical message**: one command body carried in one or more DATA records; - **current consensus**: the accepted consensus valid for new work at the local uncertainty interval; - **staged consensus**: a valid future consensus that is not yet active for new work. All integer fields defined directly by FOG-WIRE are unsigned and encoded in network byte order, most significant byte first. Byte arrays have the exact declared length. Reserved and padding bytes MUST be zero after decryption or when transmitted in cleartext. TCP is a byte stream. An implementation MUST NOT assume that one write equals one read, that one TCP segment equals one FOG frame, or that a partial read is an error. ## 6. Version-1 Carrier The only version-1 carrier is TCP as specified by RFC 9293. The version-1 carrier profile: - uses one direct TCP connection between descriptor-authorized endpoints; - does not add TLS, DTLS, QUIC, HTTP, WebSocket, SOCKS, a service mesh, or a proxy protocol header; - treats IPv4 and IPv6 endpoint forms only as permitted by the active profile; - binds the responder key through Noise rather than DNS or X.509; - does not trust TCP source address as a cryptographic identity; - uses no in-band carrier autodetection; - uses no TCP urgent data; - treats a half-close as connection termination after bounded output drain. A future carrier requires a new immutable wire profile and explicit profile transition. One port MUST NOT auto-detect TCP FOG-WIRE, TLS, QUIC, or any foreign protocol. FOG-WIRE has a visible fixed preface and does not claim to be indistinguishable from unrelated network traffic. Noise ephemeral public keys may also be fingerprintable. Deployment documentation MUST state this residual risk. ## 7. Wire Profile Registry Every `wire_profile_id` is a non-zero unsigned 32-bit integer whose meaning is permanent. Reusing an identifier for changed bytes or behavior is forbidden. An exact wire profile definition contains at least: ```text [ wire_profile_id, wire_version, carrier_id, noise_protocol_name, authentication_mode, permitted_link_context_ids, record_plaintext_size, command_shape_registry_id, maximum_session_records_per_direction, maximum_session_ciphertext_bytes_per_direction, maximum_session_age, preface_timeout, handshake_timeout, fragment_timeout, drain_timeout, reconnect_backoff_profile_id, socket_behavior_profile_id ] ``` The registry definition is part of the normative protocol release and is identified by an immutable digest in release metadata. Consensus `active_profile_ids` authorizes identifiers, not operator-provided profile bodies. Implementations MUST reject an authorized identifier they do not implement exactly. The profile fixes one complete Noise protocol name. Pattern, KEM or DH, cipher, hash, hybrid combiner, key encoding, handshake message count, and handshake message sizes are not independently negotiated. The initial concrete classical or hybrid post-quantum Noise names and reviewed library remain a pre-PoC selection gate. FOG MUST use a published reviewed construction and maintained implementation. It MUST NOT create a FOG-specific KEM, cipher, combiner, Noise token, or handshake extension. ## 8. Authentication Modes ### 8.1 Entry mode `ENTRY-AUTH-1` has these semantics: - the initiator is a blind relay with no static Noise identity in the handshake; - the responder is an entry whose exact epoch Noise public key is known from accepted consensus; - the initiator authenticates the responder; - the responder learns no stable relay identity from Noise; - both sides use fresh handshake ephemeral state; - all handshake payloads are empty; - application work begins only after the complete handshake. A classical profile MAY realize these semantics with the Noise `NK` pattern while mandating an empty first handshake payload. A post-quantum or hybrid profile MUST name one reviewed equivalent construction explicitly. Support in the abstract Noise framework is not authorization to improvise a conversion. The entry authenticates each later submission capsule under `FOG-SPHINX-PROFILES`. A TCP address, client certificate, username, API token, or reusable relay key MUST NOT become a durable FOG user identity. ### 8.2 Mutual node mode `NODE-MUTUAL-1` has these semantics: - initiator and responder have distinct role-local static Noise keys; - each endpoint knows the exact expected peer public key before connecting; - each endpoint proves possession of its corresponding private key; - both endpoints use fresh handshake ephemeral state; - all handshake payloads are empty; - application work begins only after complete mutual authentication. A classical profile MAY realize these semantics with Noise `KK`. A post-quantum or hybrid profile MUST name a reviewed mutually authenticated equivalent construction. The peer keys come from the same accepted consensus, except authority wire keys, which come from root-certified authority wire-key certificates referenced by that consensus. ### 8.3 No optional authentication Version 1 has no anonymous-to-mutual upgrade, dummy identity, user password, bearer token, client certificate, or authentication extension inside the handshake. A link context determines one mode before parsing begins. ## 9. Link Context Registry Version 1 reserves these link contexts: | ID | Name | Initiator | Responder | Mode | Connection use | | --- | --- | --- | --- | --- | --- | | 1 | `RELAY_ENTRY` | relay | entry | `ENTRY-AUTH-1` | submission and bounded return traffic | | 2 | `ENTRY_LAYER1` | entry | layer-1 mix | `NODE-MUTUAL-1` | forward and reply KEMSphinx packets | | 3 | `LAYER1_LAYER2` | layer-1 mix | layer-2 mix | `NODE-MUTUAL-1` | forward and reply KEMSphinx packets | | 4 | `LAYER2_LAYER3` | layer-2 mix | layer-3 mix | `NODE-MUTUAL-1` | forward and reply KEMSphinx packets | | 5 | `LAYER3_TERMINAL` | layer-3 mix | courier or authorized native service | `NODE-MUTUAL-1` | terminal request and anonymous reply | | 6 | `COURIER_STORE` | courier | store | `NODE-MUTUAL-1` | bounded replica operations | | 7 | `STORE_STORE` | lower `node_id` store | higher `node_id` store | `NODE-MUTUAL-1` | replica synchronization only | | 8 | `NODE_AUTHORITY` | admitted node | authority | `NODE-MUTUAL-1` | descriptor submission only | | 9 | `AUTHORITY_AUTHORITY` | lower `authority_id` | higher `authority_id` | `NODE-MUTUAL-1` | PKI protocol objects only | | 10 | `ROLE_OBSERVER` | reporting role | observer | `NODE-MUTUAL-1` | approved aggregate reports only | The numeric ordering rule gives contexts 7 and 9 one canonical connector and prevents persistent duplicate connections. Numeric comparison is over the complete identifier bytes. The reply direction MAY use an already authenticated adjacency connection in the reverse record direction. It does not authorize a new topology edge. The packet profile determines which peer is the next authorized reply hop. Adding a context or changing its role pair, direction, mode, or command set requires a new wire version or immutable profile transition. Operator configuration cannot create a custom context. ## 10. Peer Authorization Before starting a mutual handshake, each side MUST derive an expected peer record from one accepted network view: ```text ExpectedPeer = ( network_id, epoch, consensus_hash, link_context_id, local_role, remote_role, local_identifier, remote_identifier, local_wire_key_id, remote_wire_key_id, local_endpoint, remote_endpoint ) ``` For node links, both node descriptors MUST be valid for the selected epoch, their effective consensus roles and layers MUST match the link context, and their Noise keys MUST have the `node Noise transport` purpose. For authority links, the authority identifier and dedicated wire public key MUST validate through an `AuthorityWireKeyCertificate` signed by the matching offline authority root. An authority vote key MUST NOT be used as a Noise key. For `RELAY_ENTRY`, the relay identifier and key identifier are absent and are encoded as all-zero values in the prologue. The entry descriptor, role, endpoint, profile, and epoch key are still validated exactly. Authorization MUST be repeated before exposing a completed handshake to the role implementation. A peer removed, suspended, revoked, reassigned, expired, or no longer adjacent under consensus MUST NOT continue new work merely because an old TCP connection remains open. ## 11. Cleartext Preface Every connection begins with exactly one 32-byte `WirePreface`: ```text struct WirePreface { byte magic[8]; // ASCII "FOGWIRE1" uint16 wire_version; // 1 uint32 wire_profile_id; uint16 link_context_id; uint64 epoch; uint16 flags; // 0 byte reserved[6]; // all zero } ``` The preface is transmitted by the initiator and is not encrypted. The responder MUST read all 32 bytes within the profile preface timeout and then: 1. compare `magic` to the exact eight ASCII bytes `FOGWIRE1`; 2. require `wire_version` equal to 1; 3. require zero flags and reserved bytes; 4. require a locally implemented profile authorized for the declared epoch; 5. require the context to be permitted by that profile and listener; 6. require the declared epoch to be current or explicitly staged under Section 23; 7. select exactly one handshake state without probing alternatives. The preface does not negotiate. The responder sends no selection, supported list, retry profile, or downgrade hint. Any failure closes the TCP connection without protocol bytes. ## 12. Authenticated Noise Prologue Both peers construct the exact same byte string before initializing Noise: ```text WirePrologue = EncodeFixed( "FOG-WIRE-PROLOGUE-1", WirePreface, network_id, consensus_hash, initiator_role_id, responder_role_id, initiator_identifier, responder_identifier, initiator_wire_key_id, responder_wire_key_id ) ``` `EncodeFixed` is concatenation of the exact-width fields in the order shown. The domain string is 19 ASCII bytes without a terminator. `network_id`, `consensus_hash`, each identifier, and each key identifier use the hash output length pinned by the trusted PKI suite. Role identifiers are unsigned 16-bit integers. Absent relay identifier and key fields are all zero. No field is length-prefixed because every width is determined by the trusted PKI suite and wire version. Implementations MUST publish byte-exact prologue fixtures for every context. The complete `WirePrologue` is supplied as the Noise prologue input. It is authenticated by the resulting handshake transcript but is not secret and is not treated as extra key material. The implementation MUST retain the final Noise handshake hash as a connection-local channel-binding value until connection teardown. It MUST NOT log, publish, or reuse that value as a cross-session identifier. ## 13. Handshake Framing and State Machine Each Noise handshake message is carried as: ```text struct HandshakeFrame { uint16 noise_message_length; byte noise_message[noise_message_length]; } ``` Rules: - `noise_message_length` MUST be non-zero and at most 65,535; - the profile MUST define the exact handshake message count and expected length of every message; - a received length different from the profile expectation terminates the connection before proportional allocation; - every Noise handshake payload is zero bytes; - trailing, extra, duplicated, or out-of-order handshake frames terminate the connection; - the complete handshake MUST finish within the profile handshake timeout; - a Noise parse, decapsulation, DH, AEAD, key, or authentication failure terminates the connection; - no application-specific error is sent during the handshake; - ephemeral private state and incomplete cipher state are erased on success, failure, or timeout to the extent supported by the runtime. The initiator state machine is: ```text TCP_CONNECTED -> SEND_PREFACE -> NOISE_HANDSHAKE -> VERIFY_RESPONDER_OR_BOTH_PEERS -> TRANSPORT -> DRAIN_OR_FAIL -> CLOSED ``` The responder state machine is: ```text TCP_ACCEPTED -> READ_AND_VALIDATE_PREFACE -> NOISE_HANDSHAKE -> VERIFY_INITIATOR_IF_REQUIRED -> TRANSPORT -> DRAIN_OR_FAIL -> CLOSED ``` There is no resumption state and no transition from a failed state back to handshake on the same TCP connection. ## 14. Transport Framing After a successful handshake, each record is carried as: ```text struct TransportFrame { uint16 ciphertext_length; byte ciphertext[ciphertext_length]; } ``` For one connection, `ciphertext_length` is constant and equals: ```text record_plaintext_size + 16 ``` The 16 bytes are the Noise AEAD authentication overhead. The concrete profile MUST use a Noise cipher function with this standard overhead. `record_plaintext_size` MUST be a multiple of 256 between 4,096 and 65,280 bytes inclusive. Consequently every ciphertext fits the standard Noise 65,535-byte message limit. FOG-WIRE does not adopt a larger non-standard Noise message limit. A receiver MUST validate the two-byte length against the one expected constant before allocating or reading the ciphertext. Zero, short, overlong, or profile-mismatched lengths terminate the connection. TCP split and coalescing are transparent to this framing. One implementation MUST interoperate when every framing byte arrives in a separate TCP read and when multiple frames arrive in one read. ## 15. Record Plaintext Every decrypted record has this exact layout: ```text struct WireRecord { uint8 record_version; // 1 uint8 record_type; uint16 flags; // 0 byte message_id[16]; uint16 command_id; uint16 command_version; uint32 fragment_index; uint32 fragment_count; uint32 total_length; uint16 fragment_length; uint16 reserved; // 0 byte fragment_and_padding[record_plaintext_size - 40]; } ``` The 40-byte header is fixed. Version 1 defines: | `record_type` | Name | Meaning | | --- | --- | --- | | 0 | `PADDING` | traffic-schedule record, discarded after validation | | 1 | `DATA` | one fragment of one allowed logical command | | 2 | `CLOSE` | authenticated planned close or fresh-handshake rotation | Unknown versions, types, flags, commands, command versions, or non-zero reserved values are critical errors and terminate the connection. ### 15.1 Padding record For `PADDING`, every field after `flags`, including all payload bytes, MUST be zero. The receiver validates and discards it. It MUST pass through the same Noise decrypt, length check, record rekey, accounting, and aggregate metrics path as DATA records before semantic discard. ### 15.2 Close record For `CLOSE`, `command_id` is a coarse close class: - 0: planned normal close; - 1: authenticated fresh-handshake or epoch rotation. Every other header field after `flags`, except `command_id`, and all payload bytes MUST be zero. Failure, authentication, parsing, overload, or authorization errors do not send a CLOSE reason. They close the socket without protocol bytes. ### 15.3 Data record For `DATA`: - `message_id` MUST be a fresh non-zero 16-byte value generated from the operating-system CSPRNG and unique within the connection; - `command_id` and `command_version` MUST be allowed for the context, direction, role pair, epoch, and command-shape registry; - `fragment_count` MUST be from 1 through 4,096; - `fragment_index` MUST be less than `fragment_count`; - `total_length` MUST be no greater than 8 MiB and within the lower context-specific limit; - `fragment_length` MUST be no greater than `record_plaintext_size - 40`; - every non-final fragment MUST fill the complete fragment capacity; - the final fragment length MUST equal the remaining declared body length; - `fragment_count` MUST equal the unique count implied by total length and fragment capacity; - unused `fragment_and_padding` bytes MUST be zero. Zero-length command bodies use exactly one DATA record with zero fragment length and are valid only when the command registry explicitly permits them. `message_id` is a connection-local reassembly handle, not a capability, account, authentication token, delivery identifier, storage key, or cross-session replay defense. A narrower command protocol owns idempotency and deduplication across reconnects. ## 16. Logical-Message Reassembly A receiver MAY interleave fragments from multiple logical messages only within all profile bounds. It MUST: 1. authenticate and validate each complete record before reading its fields; 2. reject duplicate fragment indices and inconsistent repeated metadata; 3. avoid allocation based only on `fragment_count` or `total_length`; 4. allocate or spool incrementally within a fixed connection budget; 5. accept at most eight incomplete logical messages per connection; 6. accept at most 16 MiB of incomplete reassembly state per connection; 7. expire incomplete messages at the profile fragment timeout; 8. erase partial bodies on timeout, connection loss, parse failure, or role rejection; 9. invoke command handling only after exact complete reassembly and final shape validation. A context profile MAY lower the 8 MiB message limit, eight-message count, or 16 MiB buffer budget. It MUST NOT raise them in wire version 1. Implementations SHOULD stream authenticated fragments into bounded role-local temporary storage when a command legitimately exceeds the in-memory budget. Temporary files MUST be private to the role account, unlinked or randomly named without peer identifiers, size-bounded, and deleted on every terminal path. ## 17. Command Registry and Direction Version 1 reserves these command families: | ID | Command | Body owner | | --- | --- | --- | | 1 | `PACKET_SUBMIT` | future `FOG-ENTRY-CAPSULE` specification | | 2 | `PACKET_FORWARD` | `FOG-SPHINX-PROFILES` fixed KEMSphinx packet | | 3 | `PACKET_RETURN` | `FOG-SPHINX-PROFILES` bounded relay return object | | 256 | `REPLICA_REQUEST` | `FOG-STORAGE` | | 257 | `REPLICA_RESPONSE` | `FOG-STORAGE` | | 258 | `REPLICA_SYNC` | `FOG-STORAGE` | | 512 | `DESCRIPTOR_UPLOAD` | `FOG-PKI` | | 513 | `DESCRIPTOR_RESULT` | `FOG-PKI` | | 514 | `AUTHORITY_OBJECT` | `FOG-PKI` | | 768 | `AGGREGATE_REPORT` | `FOG-OBSERVABILITY` | The directional allowlist is: | Context | Initiator to responder | Responder to initiator | | --- | --- | --- | | `RELAY_ENTRY` | `PACKET_SUBMIT` | `PACKET_RETURN` | | `ENTRY_LAYER1` | `PACKET_FORWARD` | `PACKET_FORWARD` | | `LAYER1_LAYER2` | `PACKET_FORWARD` | `PACKET_FORWARD` | | `LAYER2_LAYER3` | `PACKET_FORWARD` | `PACKET_FORWARD` | | `LAYER3_TERMINAL` | `PACKET_FORWARD` | `PACKET_FORWARD` | | `COURIER_STORE` | `REPLICA_REQUEST` | `REPLICA_RESPONSE` | | `STORE_STORE` | `REPLICA_SYNC` | `REPLICA_SYNC` | | `NODE_AUTHORITY` | `DESCRIPTOR_UPLOAD` | `DESCRIPTOR_RESULT` | | `AUTHORITY_AUTHORITY` | `AUTHORITY_OBJECT` | `AUTHORITY_OBJECT` | | `ROLE_OBSERVER` | `AGGREGATE_REPORT` | none | `PACKET_FORWARD` in the reverse record direction is permitted only when the packet profile validates the receiving peer as the next hop of an anonymous reply. It does not allow arbitrary reverse RPC. Every narrower specification MUST define exact body bytes, version, maximum size, expected padded size class, request-to-response relation, idempotency, semantic timeout, and behavior after reconnect. Unknown commands never reach role code. There is no vendor, experimental, operator-private, or generic-RPC command range in version 1. ## 18. Command Shapes and Length Privacy The active `command_shape_registry_id` maps each allowed command to: - one exact logical body length; or - a small ordered set of public padded length classes; - an exact record count for each class; - a context-specific maximum in-flight count; - whether zero-length bodies are valid. The sender pads the semantic object inside the owning protocol before passing it to FOG-WIRE. FOG-WIRE zero-fills only unused space in the last fixed record. It does not invent application padding classes. All native KEMSphinx applications use the same `PACKET_SUBMIT`, `PACKET_FORWARD`, and `PACKET_RETURN` shapes for an active packet profile. `fog-drop`, `fog-mailbox`, and `fog-im` MUST NOT select distinguishable wire sizes. `FOG-SPHINX-PROFILES.md` defines the exact packet and return bodies. For the non-active `FOG-SPHINX-CANDIDATE-MLKEM768-X25519-1`, `PACKET_FORWARD` is 16,150 bytes and `PACKET_RETURN` is 10,204 bytes. These calculated candidate lengths do not authorize the suite. A wire profile paired with it must map each body to one deterministic fixed record count and reject any other logical length. The number and timing of records remain observable. A command shape is a declared traffic class, not perfect length hiding. The simulator and owning protocol MUST evaluate whether a class creates an unacceptable fingerprint. ## 19. Cipher-State Rekey After encrypting and queueing each complete transport record, the sender MUST call Noise `CipherState.Rekey()` on the outbound cipher state exactly once. After successfully authenticating, decrypting, and validating the fixed ciphertext length of each complete record, the receiver MUST call `CipherState.Rekey()` on the inbound cipher state exactly once before processing the record semantically. If authentication fails, the receiver does not advance or retry. It destroys both transport cipher states and closes the connection. Noise rekey changes the current cipher key through a one-way function and does not reset the Noise nonce. It does not add a fresh DH or KEM secret. Therefore it MUST NOT be described as a replacement for a fresh authenticated handshake. The separate inbound and outbound cipher states are never combined and half-duplex Noise mode is forbidden. ## 20. Fresh Handshake and Session Limits A connection MUST be replaced with a completely new TCP and Noise handshake at the earliest of: - the profile maximum records sent in either direction; - the profile maximum ciphertext bytes sent in either direction; - the profile maximum session age; - activation of a new required epoch key or wire profile; - peer role, topology adjacency, endpoint, key, revocation, or authorization change; - any uncertainty about cipher-state synchronization; - local secret-state restoration or process restart. Version-1 absolute ceilings are: | Item | Absolute ceiling | | --- | --- | | records per direction per session | 2^32 | | ciphertext bytes per direction per session | 1 TiB | | session age | 24 hours | Every concrete profile MUST select lower exact values based on cipher bounds, cover rate, connection churn, epoch schedule, and measurements. The connector MAY establish one authenticated replacement in parallel. It MUST NOT send new logical messages on the replacement until the handshake and authorization complete. The old connection drains only already admitted messages for the profile drain timeout, then closes. Logical-message retry across the replacement belongs to the command owner. A transport reconnect MUST NOT silently report application success, replay an unknown partial command, or create a second delivery without that command's idempotency rule. ## 21. Connection Ownership and Multiplicity There is at most one active data connection for one tuple: ```text ( network_id, epoch, wire_profile_id, link_context_id, initiator_identifier, responder_identifier ) ``` `RELAY_ENTRY` uses one relay-local connection instance in place of an initiator identifier. A relay MAY maintain the small temporary entry set permitted by its authenticated cover profile, but the count MUST NOT vary immediately with one user message. One additional authenticated connection MAY exist only as a staged replacement. Any other duplicate is closed after authentication without moving work to it. The canonical initiator in Section 9 owns reconnect attempts. A responder does not open a reverse substitute connection. Nodes SHOULD maintain required adjacency connections independently of application queue occupancy when the cover profile requires it. ## 22. Traffic Scheduling, Padding, and Liveness The active cover profile specifies connection maintenance and a record-emission schedule for each privacy-relevant context. It may choose a measured constant, Poisson, or other reviewed schedule, but all operators in that profile use the same authenticated parameters. At each scheduled emission opportunity: - the role sends one queued DATA record allowed by the scheduling policy; or - it sends one PADDING record when no eligible DATA record is selected. DATA arrival MUST NOT cause an undeclared immediate write that bypasses the schedule. PADDING MUST NOT be disabled locally while a profile claims traffic normalization. FOG-WIRE defines no `PING` or `PONG`. TCP itself has no sufficient application liveness guarantee. Health is inferred from authenticated traffic, socket failure, connection age, and separate coarse loop protocols. If a deployment enables operating-system TCP keepalive only for dead-resource cleanup, its exact settings MUST be profile-bound and it MUST NOT be counted as cover traffic or anonymity evidence. Failure of required cover generation moves the role into the degraded or stop state defined by the cover profile. It does not switch to activity-triggered records. The first matrix in `FOG-SIMULATION.md` does not select this schedule. It shows that high packet cover can improve simple timing and intersection proxies without supplying adequate local mixing at short mean delay. Exact record schedules still require queue, liveness, loop, polling, reply, and load models. ## 23. Epoch and Profile Transitions The preface declares one epoch. The Noise prologue binds the exact consensus hash for that epoch. Peers MUST NOT combine a descriptor, key, topology edge, profile, or parameter from a different consensus body. During an authenticated transition: - a listener MAY accept the current epoch and one preannounced next epoch; - a next-epoch connection MAY complete and remain staged before `valid_after`; - no next-epoch application work is sent or accepted before `valid_after`; - an old profile remains usable only through its signed overlap interval; - minimum accepted profile state prevents rollback after promotion; - a previous epoch connection stops accepting new work at its exact boundary; - bounded draining MUST end before key or consensus hard expiry; - expired private keys are erased after the owning protocol's last required grace window; - same-epoch consensus conflict freezes new connections and new work. A listener chooses the exact state directly from the preface and local accepted PKI state. It does not try current and old parsers in sequence. At consensus `valid_until`, new handshakes and new logical messages under that consensus stop. Existing connections MAY only perform an explicitly specified bounded drain that does not outlive key authorization. ## 24. Timeouts and Reconnect Backoff Every concrete wire profile defines exact values for: - TCP connect timeout; - 32-byte preface timeout; - complete Noise handshake timeout; - fixed frame read and write timeout; - incomplete logical-message fragment timeout; - output drain timeout; - maximum session age; - reconnect initial delay, multiplier, ceiling, and jitter distribution. Version-1 absolute maxima are: | Timer | Maximum | | --- | --- | | preface | 30 seconds | | complete handshake | 60 seconds | | incomplete frame | 60 seconds | | incomplete logical message | 5 minutes | | drain | 30 seconds | | reconnect ceiling | 15 minutes | Timeouts are monotonic-duration measurements, not peer-provided wall-clock values. A timeout closes the connection without detailed remote error. Reconnect uses bounded exponential backoff with CSPRNG-derived full jitter. The attempt schedule MUST NOT use node identifiers, message identifiers, queue length, or application type as its random seed. Immediate deterministic fallback to another route, layer, or older profile is forbidden. ## 25. Backpressure and Resource Limits Before the local PoC, every role profile MUST set lower operational limits within these version-1 absolute bounds: | Resource | Absolute bound | | --- | --- | | accepted unauthenticated connections per listener | 1,024 | | concurrent handshakes per listener | 256 | | active connections per authenticated peer/context | 1 plus 1 staged | | encrypted record plaintext | 65,280 bytes | | logical message | 8 MiB | | fragments per logical message | 4,096 | | incomplete messages per connection | 8 | | incomplete reassembly bytes per connection | 16 MiB | | queued complete logical messages per connection | 1,024 | | command versions accepted per command | 2 during signed transition | Implementations MUST also bound total process connections, file descriptors, handshake CPU, KEM decapsulations, per-source unauthenticated attempts, queue bytes, write backlog, temporary files, and aggregate reassembly memory. When a queue is full, the role follows the owning command's shedding policy. It MUST NOT allocate without bound, skip authentication, enlarge a packet, send an error larger than the triggering record, or route around a required layer. Entry-mode abuse control MAY use coarse short-lived network-source limits and valid entry capsules, but MUST NOT create a stable cross-entry user account. Safe anonymous admission and rate control remain an explicit open security decision. ## 26. Socket Behavior The immutable socket behavior profile fixes at least: - TCP no-delay behavior; - keepalive enablement and exact cleanup timers if used; - send and receive buffer ceilings; - user-space write batching; - address-family policy; - dual-stack binding behavior; - connection and accept backlog limits; - maximum pending output bytes; - graceful close and reset behavior. Operators MUST NOT tune these settings independently when they affect record timing, batching, connection duration, or public traffic claims. The initial values require trace measurement on supported operating systems because TCP segmentation does not preserve FOG record boundaries. Proxy-protocol headers, transparent proxy source metadata, TLS termination, and service-mesh sidecars are forbidden on core FOG-WIRE listeners. A deployment requiring a network proxy is a separate analyzed profile and trust boundary. ## 27. Failure Behavior | Condition | Remote behavior | Local behavior | | --- | --- | --- | | invalid or unsupported preface | close without bytes | coarse counter | | unauthorized epoch, profile, context, role, or endpoint | close without bytes | coarse authorization class | | handshake length or timeout failure | close without bytes | coarse handshake class | | Noise authentication or decapsulation failure | close without bytes | one aggregate crypto-failure class | | transport length mismatch | close without bytes | coarse frame class | | Noise record authentication failure | close without bytes | destroy both cipher states | | unknown or malformed record | close without bytes | coarse record class | | forbidden command or direction | close without bytes | coarse policy class | | reassembly bound or timeout | close without bytes | erase partial state | | queue saturation after authentication | owning protocol's uniform bounded outcome or close | aggregate overload class | | planned rotation | one fixed CLOSE record if schedule permits | bounded drain and fresh handshake | | TCP loss or half-close | no protocol error | erase partial state and back off | Peers never receive parser offsets, expected keys, supported profiles, consensus hashes, role policy, capacity, queue depth, or cryptographic error details. The implementation is not required to make all local computation paths constant-time with respect to public invalid framing. Cryptographic libraries MUST provide their required side-channel protections, and remotely observable failure behavior MUST remain non-amplifying and free of detailed oracles. ## 28. Logging and Metrics FOG-WIRE MUST NOT log or export: - plaintext or ciphertext record bodies; - entry capsules or KEMSphinx packets; - `message_id`, Noise handshake hash, traffic key, ephemeral key, or private key material; - per-record timestamps or direction traces; - queue contents, logical-message hashes, capabilities, or reply material; - source IP addresses for anonymous relay-to-entry sessions in persistent application logs; - detailed handshake failure stages associated with one remote endpoint. Role-local debugging MAY use coarse enumerated failure classes in a protected short-retention log, without input bytes or secret values. Claim-bearing profiles disable packet and record tracing. Metrics are delayed aggregates over declared windows and minimum populations. Allowed examples include total completed handshakes by context, aggregate failure class, coarse connection age bucket, aggregate queue occupancy bucket, and total padding-to-data ratio. The observer interface receives aggregates, not event streams. ## 29. Key and Secret Lifecycle | Key or secret | Generator | Authorized use | Lifetime and overlap | Compromise response | Backup | | --- | --- | --- | --- | --- | --- | | Node Noise epoch private key | owning node OS CSPRNG | one node role and authorized adjacent links | profile-bounded current key, staged next key, exact grace only | revoke key or node, stop new handshakes, rotate | no routine backup | | Entry Noise epoch private key | entry OS CSPRNG | authenticate `RELAY_ENTRY` and authorized node links | profile-bounded current and staged next | revoke entry key, stop sessions, rotate | no routine backup | | Authority wire private key | authority wire service OS CSPRNG or controlled ceremony | authority mutual links only | root-certified bounded interval, staged next key | root-signed revocation and replacement | SHOULD NOT restore into a concurrent service | | Handshake ephemeral private state | connection endpoint OS CSPRNG | one Noise handshake | until handshake completion or failure | close connection, erase state | none | | Noise transport cipher states | Noise handshake `Split()` | one direction of one connection | until close or fresh handshake | destroy connection state and reauthenticate | none | | Reassembly message identifier | sending role OS CSPRNG | one logical message within one connection | until completion or timeout | discard partial message | none | | Queue-sealing key | owning role local secret facility | local queued ciphertext at rest | deployment-rotated, independent of Noise | quarantine queue, rotate, discard unverifiable state | profile-specific encrypted backup | Static private keys MUST be readable only by the one role service that owns them. They MUST NOT appear in environment templates, command arguments, container images, shared volumes, logs, crash dumps, examples, test fixtures, or support bundles. Best-effort zeroization is required for ephemeral, cipher-state, and partial plaintext buffers. Documentation MUST NOT claim complete erasure where the language runtime, allocator, kernel, swap, hibernation, or hardware cannot guarantee it. ## 30. Implementation Requirements An implementation MUST: - use a maintained reviewed Noise or PQNoise library; - use the operating-system CSPRNG for static keys, ephemeral state, message identifiers, and jitter; - keep protocol framing separate from role command parsing; - enforce all limits before proportional allocation; - use checked integer arithmetic for lengths, counts, and record totals; - reject trailing bytes and unknown-critical values; - authenticate complete records before semantic parsing; - prevent one role package from importing another role implementation; - expose one typed context-specific command interface, not raw generic RPC; - disable core dumps and secret-bearing panic or exception output; - hold static private keys in the minimum role-local protected facility; - destroy connection state on every authentication or synchronization error; - keep the Composer implementation free of FOG-WIRE listeners and sockets. The protocol implementation MUST NOT: - implement cryptographic primitives or hybrid combiners locally; - let configuration specify an arbitrary Noise protocol string; - use a shared transport key for multiple nodes or roles; - retry decryption with old keys or profiles; - accept a DNS result, TCP source, or TLS certificate instead of PKI key validation; - expose a generic byte tunnel to applications; - compress records or logical messages at the wire layer; - silently fragment an over-limit command; - persist session cipher state for resumption. ### 30.1 Required module boundaries The future implementation keeps these responsibilities independently testable: ```text role executable and role package -> TCP listener or connector adapter -> FOG-WIRE session state machine -> profile and link-context authorization -> pure preface, prologue, frame, record, and fragment codecs -> reviewed Noise or PQNoise library ``` The pure codecs MUST be testable without opening a socket or starting a role. The session state machine owns no role database and invokes only a typed context-specific command boundary after complete validation. The TCP adapter owns socket IO but does not parse role command bodies. Role handlers own semantic commands but never access Noise cipher state or unvalidated record bytes. Profile registry, framing, session, and command-shape code MUST NOT be placed in a generic `utils` package. Role packages MUST NOT call each other to bypass the wire contract or share one transport singleton. Separate executables are justified by the established security and state-isolation boundaries, not by independent feature scaling. ## 31. Conformance and Adversarial Tests Before the local PoC, FOG-WIRE MUST have tests for: - byte-exact preface encoding and every rejected field variation; - byte-exact prologue construction for every context and PKI hash length; - entry responder authentication with no relay static identity; - mutual node and authority authentication; - wrong network, consensus, epoch, context, role, node, endpoint, key, and profile binding; - empty handshake payload enforcement and attempted early application data; - handshake messages at expected, short, long, zero, and 65,535-byte bounds; - TCP reads split at every byte boundary and coalesced across multiple frames; - fixed ciphertext length and standard Noise maximum enforcement; - every record type and zero-reserved or zero-padding rule; - unknown record, command, command version, close class, and direction; - valid single- and multi-fragment reassembly; - duplicate, missing, reordered, inconsistent, expired, excessive, and sparse fragments; - exact 8 MiB, 4,096-fragment, 8-message, and 16 MiB boundaries; - per-record outbound and inbound rekey synchronization; - authentication failure before and after a rekey boundary; - fresh-handshake record, byte, age, epoch, topology, and profile boundaries; - current and next epoch staging with no early forwarding; - old-profile retirement and rollback refusal; - duplicate connection collision and one staged replacement; - queue saturation, slow reads, slow writes, half-close, reset, and timeout; - reconnect backoff and jitter bounds under repeated failure; - listener floods and expensive-handshake concurrency limits; - log and metrics schema rejection of prohibited fields; - parser mutation, coverage-guided fuzzing, differential framing tests, race tests, and allocation tests; - firewall validation for every link context and forbidden role edge. Tests generate ephemeral private fixtures at runtime. Private keys MUST NOT be committed as example or conformance data. Public canonical framing fixtures MAY be committed when they contain no secret material. Independent implementations MUST consume the same public framing and transcript-binding corpus before interoperability is claimed. ## 32. Claim Gates ### 32.1 Functional PoC The local PoC MUST demonstrate: - one fixed reviewed Noise profile; - responder-only relay-to-entry and mutual node authentication; - complete traversal of entry and three mix layers; - fixed records, padding records, fragmentation, and strict command policy; - per-record rekey and bounded fresh handshakes; - current and next epoch transition; - malformed, unauthorized, flooded, and disconnected peers; - no multi-role key, state, account, or writable-volume sharing. This demonstrates functional correctness only. It does not demonstrate a production anonymity set or global-observer resistance. `FOG-LOCAL-POC.md` fixes the container and network boundary for this gate. It uses one internal pairwise network per permitted data-plane adjacency, no host ports, and no shared role state. Its current validated plan is not a FOG-WIRE implementation and deliberately contains no invented wire profile or port. ### 32.2 Operator alpha Alpha additionally requires: - fixed reviewed hybrid post-quantum or explicitly documented classical profile selection; - interoperability between independently built endpoints; - fuzz, race, load, fault, and side-channel review results; - measured TCP record segmentation and timing on supported systems; - measured handshake cost and anonymous-entry flood behavior; - six independently operated mixes and profile-conformant cover scheduling; - rehearsed key compromise, revocation, epoch transition, and rollback tests; - public residual-risk documentation for visible endpoints and traffic. ### 32.3 Production transport claims Production claims additionally require: - independent protocol and implementation review; - reviewed library and dependency provenance; - published concrete suite, geometry, limits, and conformance results; - verified erasure and crash behavior within stated platform limits; - sustained adversarial load and resource-exhaustion testing; - evidence that profile rollout does not create persistent minority fingerprints; - claim wording that distinguishes link confidentiality, forward secrecy, post-quantum assumptions, and metadata privacy. ## 33. Requirements Traceability | Requirement | FOG-WIRE control | | --- | --- | | `TM-NET-01` | fixed records, command shapes, profile-bound scheduling, padding records | | `TM-NET-03` | bounded retries, no bypass, cover-profile failure gate | | `TM-NET-04` | ordered Noise nonces, no resumption, command-owned cross-session deduplication | | `TM-NET-05` | authenticated records, strict failure, no action before validation | | `TM-NET-06` | immutable profiles, exact context, no negotiation or autodetection | | `TM-PKI-02` | epoch and consensus binding, monotonic profile transition, hard expiry | | `TM-ROLE-01` | anonymous relay mode, authenticated entry, no durable relay account | | `TM-ROLE-02` | exact layer adjacency, mutual node authentication, no layer skip | | `TM-ROLE-03` | courier/store contexts, command separation, fixed replica shapes | | `TM-OPS-01` | prohibited log fields, delayed aggregate metrics | | `TM-CRYPTO-01` | purpose-separated keys, lifecycle table, fresh handshakes | | `TM-CRYPTO-02` | consensus-authorized exact Noise name, no fallback, bounded overlap | | `TM-AVAIL-01` | strict frame, fragment, handshake, connection, queue, and CPU bounds | | `ARC-003` | no data-plane bypass or new failure edge | | `ARC-004` | Noise limited to adjacent transport protection | | `ARC-005` | peers and profiles derived from one complete accepted consensus | | `ARC-006` | fixed records and application-independent packet commands | | `ARC-007` | one private-key owner and one purpose | | `ARC-008` | exact versioned framing and absolute parser limits | | `ARC-009` | coarse non-amplifying remote failure | | `IF-03` | authenticated bounded descriptor command | | `IF-04` | authenticated authority object command | | `IF-06` | anonymous relay initiator, authenticated entry, fixed submission command | | `IF-07` | mutually authenticated exact layer adjacency | | `IF-08` | fixed authenticated layer-3 to terminal transport | | `IF-09` | dedicated courier/store and store/store contexts | | `IF-10` | reverse-direction packet only over authorized adjacency | | `IF-11` | aggregate-only observer command | ## 34. Open Pre-Implementation Selections The version-1 protocol structure is fixed, but these selections block daemon implementation: - the exact reviewed classical or hybrid post-quantum Noise protocol names; - the reviewed Noise or PQNoise library and supported implementation language; - X-Wing is the leading KEM to evaluate, but no exact FOG entry or mutual post-quantum handshake profile is selected; - exact `wire_profile_id` assignments and immutable registry digests; - record plaintext size and command-shape pairings; - fresh-handshake record, byte, and age thresholds; - concrete timeouts, reconnect distribution, and socket behavior profile; - lower role-specific connection, queue, CPU, and memory limits; - packet, PKI, and observer command body specifications, plus byte-exact `FOG-STORAGE` command-to-record mappings; - cover-record schedules and degraded-mode thresholds from simulation; - safe anonymous entry admission and flood-control policy; - authority wire-key certificate rollout and revocation ceremony. Implementations MUST NOT resolve these selections through undocumented local defaults. Each selected value becomes an immutable reviewed profile with test vectors and a signed transition path. ## 35. References - FOG threat model: `FOG-THREAT-MODEL.md` - FOG architecture: `FOG-ARCHITECTURE.md` - FOG public key infrastructure: `FOG-PKI.md` - FOG storage protocol: `FOG-STORAGE.md` - FOG cryptographic suite evaluation: `FOG-CRYPTO-SUITES.md` - FOG traffic and topology simulation: `FOG-SIMULATION.md` - FOG local Podman PoC: `FOG-LOCAL-POC.md` - Noise Protocol Framework: - Katzenpost mix network wire protocol: - Post Quantum Noise: - CFRG X-Wing KEM Internet-Draft: - RFC 9293, Transmission Control Protocol: - RFC 8174, Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words: These references inform Noise handshake semantics, standard message bounds, rekey behavior, TCP stream framing, post-quantum construction review, and adjacent mix-link experience. They do not make a concrete FOG profile secure without exact selection, conformance testing, operational measurement, and independent review.