# 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:
  <https://noiseprotocol.org/noise.html>
- Katzenpost mix network wire protocol:
  <https://katzenpost.network/docs/specs/wireprotocol/>
- Post Quantum Noise:
  <https://eprint.iacr.org/2022/539>
- CFRG X-Wing KEM Internet-Draft:
  <https://datatracker.ietf.org/doc/draft-connolly-cfrg-xwing-kem/>
- RFC 9293, Transmission Control Protocol:
  <https://www.rfc-editor.org/rfc/rfc9293.html>
- RFC 8174, Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words:
  <https://www.rfc-editor.org/rfc/rfc8174.html>

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.
