# FOG Sphinx Profiles

Status: Draft 0.1

Date: 2026-08-08

## 1. Purpose

This document defines `FOG-SPHINX-PROFILES`, the authenticated profile
framework for fixed-size KEMSphinx packets used by FOG.

It fixes the structural packet contract, route shape, routing commands, SURB
rules, replay behavior, wire ownership, SDK boundary, transition behavior,
and conformance evidence required before a concrete packet suite can be
activated.

It also records one calculated but non-active candidate geometry named
`FOG-SPHINX-CANDIDATE-MLKEM768-X25519-1`. The candidate is an engineering
input for benchmarks and review. It is not yet `FOG-SPHINX-1`, is not
authorized for a public network, and does not make a deployed security claim.

The key words MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, and MAY describe
normative requirements in the sense of BCP 14 when they appear in uppercase.

## 2. Scope

`FOG-SPHINX-PROFILES` owns:

- immutable packet-profile identifiers and their consensus authorization;
- fixed KEMSphinx packet geometry and exact route length;
- the mapping from PKI nodes to 32-byte packet node identifiers;
- per-hop routing command sequences and command padding;
- forward payload and SURB slots;
- single-use reply blocks and private reply tokens;
- replay-tag derivation, durable replay-state behavior, and retirement;
- packet lifetime and epoch binding;
- the logical bodies carried by FOG-WIRE packet commands;
- parser limits, uniform failures, and key lifecycle;
- conformance-vector and SDK requirements;
- isolation requirements for foreign Sphinx-family bridges.

This document does not own:

- message-level end-to-end encryption;
- contact, retry, acknowledgement, or application deduplication semantics;
- entry-capsule encryption and blind-relay entry selection;
- rendezvous capability allocation and offline return import;
- storage capabilities or replica protocols;
- adjacent-link Noise framing or its record size;
- the concrete delay distribution and cover rate;
- consensus production, signatures, or topology assignment.

Those contracts belong to FOG-MESSAGING, FOG-STORAGE, the entry-capsule and
return-rendezvous specifications, FOG-WIRE, the cover profile, and FOG-PKI.

## 3. Security Boundaries

KEMSphinx protects per-hop routing information and transforms the packet at
every hop. It does not replace message-level encryption. The terminal receives
the fixed outer FOG payload and MUST still treat the native message or storage
operation as end-to-end encrypted unless the owning application protocol
explicitly defines a different public object.

FOG-WIRE authenticates adjacent links but does not replace KEMSphinx. A valid
Noise connection is only a carrier for one packet whose KEMSphinx
authentication, route command, replay status, epoch, and geometry must still
be validated.

Fixed packet size alone does not provide anonymity against timing,
intersection, n-1, volume, active tagging, or global observation. Delay,
traffic volume, cover traffic, topology, endpoint safety, and operator
independence remain necessary parts of any anonymity claim.

## 4. Protocol Invariants

### SPHINX-INV-01: One exact profile per epoch context

A packet is processed under exactly one packet profile selected by the
accepted consensus and the authenticated FOG-WIRE epoch context. A node MUST
NOT guess a profile from packet length, try a list of suites, negotiate a
suite inside a packet, or fall back after failure.

### SPHINX-INV-02: Four KEMSphinx hops

Every core forward route and every core reply route has exactly four
KEMSphinx hops. A shorter or longer core route is invalid even if an
underlying Sphinx library can pad a variable path to the same header length.

### SPHINX-INV-03: Entry is outside the forward mix route

The forward entry is not a KEMSphinx hop. The blind relay submits an
entry-bound capsule; the entry releases the already constructed packet only
to its bound layer-1 node.

### SPHINX-INV-04: One transform per hop

An accepted KEMSphinx hop performs one and only one unwrap. No role unwraps
two layers, forwards a packet without the required unwrap, or changes a route
outside the authenticated command obtained from that unwrap.

### SPHINX-INV-05: Fixed external geometry

Every packet using one packet profile has one exact byte length. Forward,
reply, padding, loop, drop, mailbox, and asynchronous-message packets use the
same geometry. Source applications do not select packet sizes.

### SPHINX-INV-06: Durable replay before action

After successful KEM decapsulation and header authentication, a hop MUST
atomically check and durably insert its replay identifier before scheduling,
forwarding, terminal delivery, or returning success to an upstream worker.

### SPHINX-INV-07: Single-use replies

A SURB, its private decryption token, its `surb_id`, and the associated return
rendezvous are single-use. Uncertain delivery burns them. Retrying with the
same reply material is forbidden.

### SPHINX-INV-08: No packet-layer fragmentation identity

KEMSphinx packets contain no application message identifier, fragment index,
or fragment count. Application fragmentation occurs inside the fixed opaque
user payload under the owning end-to-end protocol.

### SPHINX-INV-09: Purpose-separated keys and state

Mix KEM keys, terminal KEM keys, Noise keys, entry-capsule keys, replay-state
protection keys, SURB private tokens, message keys, and storage keys are
distinct. Sharing one private key or replay database across roles is
forbidden.

### SPHINX-INV-10: Failure never changes topology or profile

Failure cannot remove a layer, substitute the entry as a mix, route directly
to storage, activate an old profile, send plaintext, or hand a foreign packet
to a core parser.

## 5. Terminology and Encoding

Terms used in this specification:

- **packet profile**: permanent mapping from a numeric identifier to exact
  KEMSphinx primitives, geometry, commands, epoch rules, limits, and SDK
  behavior;
- **packet epoch context**: the epoch and accepted consensus already bound by
  the FOG-WIRE connection carrying the packet;
- **packet node ID**: fixed 32-byte KEMSphinx routing identifier derived from
  one consensus-authorized node and one packet profile;
- **forward route**: layer 1, layer 2, layer 3, then a courier or native
  terminal;
- **reply route**: layer 3, layer 2, layer 1, then an entry return
  rendezvous;
- **SURB**: a public Single Use Reply Block given to a terminal;
- **private reply token**: Composer-only payload decryption material paired
  with one SURB;
- **raw replay tag**: profile-domain-separated digest of the current hop KEM
  ciphertext;
- **stored replay ID**: keyed local representation committed to durable replay
  state.

All integers defined directly by FOG are unsigned and encoded in network byte
order. Fixed arrays have exactly the declared length. Reserved and padding
bytes are zero after decryption. Parsers reject non-zero reserved bytes,
truncation, trailing bytes, duplicate commands, reordered commands, unknown
commands, and lengths other than the active profile's exact values.

## 6. Packet Profile Registry

Every `packet_profile_id` is a non-zero unsigned 32-bit integer whose meaning
is permanent. An identifier MUST NOT be reused for changed bytes, algorithms,
command rules, route shape, limits, or behavior.

An exact profile record contains at least:

```text
[
  packet_profile_id,
  kemsphinx_format_id,
  kem_suite_id,
  primitive_suite_id,
  packet_node_id_hash_id,
  replay_hash_id,
  replay_store_mac_id,
  additional_data,
  hop_count,
  route_shape_id,
  command_registry_id,
  kem_ciphertext_length,
  header_mac_length,
  payload_tag_length,
  sprp_key_material_length,
  per_hop_routing_info_length,
  routing_info_length,
  header_length,
  surb_length,
  sphinx_plaintext_header_length,
  user_forward_payload_length,
  forward_payload_length,
  packet_length,
  delay_profile_id,
  maximum_packet_lifetime,
  epoch_drain_limit,
  parser_limits_profile_id,
  sdk_contract_id,
  conformance_vector_set_id
]
```

Registry metadata MAY label a reviewed profile `alpha`, `active`, `draining`,
or `retired`, but that mutable label is not part of the immutable profile
meaning. Only the accepted consensus authorizes actual use. An incomplete
candidate does not receive a numeric `packet_profile_id`.

The profile record is published as an immutable release artifact and its
identifier is listed by FOG-PKI. Consensus `active_profile_ids` selects the
exact record. Numeric parameters are not operator-tunable.

One packet profile MUST be compatible with the FOG-WIRE command-shape
registry active on every link that carries it. A consensus containing an
incompatible pair is invalid.

## 7. Epoch and Profile Selection

The packet bytes do not carry a negotiable profile identifier or epoch. The
authenticated FOG-WIRE connection context supplies:

- network identity;
- epoch;
- accepted consensus hash;
- link context;
- wire profile;
- the one packet profile mapped to that epoch and command shape.

For a given link context and packet class, one epoch maps to exactly one
packet profile. Old-epoch packets continue only on old-epoch connections
during the bounded drain interval. A node MUST NOT trial-decrypt an old
packet with several epoch keys.

A Composer MUST NOT create a packet unless:

```text
current_time_upper_bound
  + maximum_route_delay
  + relay_and_entry_queue_budget
  + wire_delivery_budget
  < packet_epoch_hard_expiry
```

The same check applies to a SURB's expected round trip. A packet or SURB that
cannot complete within its epoch budget is not created.

## 8. Core Route Shape

### 8.1 Forward route

The only core forward shape is:

```text
entry capsule -> L1 -> L2 -> L3 -> terminal
```

The entry capsule is outside this four-hop KEMSphinx packet. Its specification
must cryptographically bind the exact packet to the exact L1 packet node ID
without revealing that ID to the blind relay.

The four KEMSphinx hops are:

| Hop | Required role | Required routing result |
|---:|---|---|
| 0 | mix layer 1 | delay, then layer 2 |
| 1 | mix layer 2 | delay, then layer 3 |
| 2 | mix layer 3 | delay, then selected terminal |
| 3 | courier or native terminal | terminal recipient and payload |

### 8.2 Reply route

The only core reply shape is:

```text
terminal uses SURB -> L3 -> L2 -> L1 -> entry rendezvous
```

The four KEMSphinx hops are:

| Hop | Required role | Required routing result |
|---:|---|---|
| 0 | mix layer 3 | delay, then layer 2 |
| 1 | mix layer 2 | delay, then layer 1 |
| 2 | mix layer 1 | delay, then entry |
| 3 | entry | return rendezvous plus `surb_id` |

The reply sender learns the public first-hop packet node ID contained in the
SURB. It does not learn later hops, the destination relay, or the Composer's
private reply token.

### 8.3 Eligibility checks

The Composer builds routes only from one accepted full consensus. Every hop
must have the required role, exact layer, active packet profile, current epoch
KEM key, endpoint reachability through the next role, and no effective
revocation. The route must also pass the consensus topology and diversity
rules.

Each online hop independently verifies that the authenticated upstream role,
its own role and layer, the next packet node ID, and the next FOG-WIRE context
match the consensus. A valid header is not sufficient authorization for a
role-invalid edge.

## 9. Packet Node IDs

KEMSphinx routing uses a fixed 32-byte `packet_node_id`. It is not a private
key and is not a substitute for the PKI `node_id`.

For profile `P`, it is derived as:

```text
packet_node_id = HASH32(
  "FOG-SPHINX-NODE-ID-1" ||
  u32be(length(network_id)) ||
  network_id ||
  u32be(P.packet_profile_id) ||
  u32be(length(node_id)) ||
  node_id
)
```

`HASH32` is fixed by `packet_node_id_hash_id`. Consensus validation derives
all active packet node IDs and rejects any duplicate. Nodes build an immutable
epoch-local lookup table from packet node ID to the exact authorized role,
layer, KEM key ID, and link endpoint.

Packet node IDs may be logged only in coarse configuration validation. They
MUST NOT be logged per packet.

## 10. Routing Commands

The core profile uses the maintained Sphinx command model with these exact
tags:

| Tag | Command | Body length | Meaning |
|---:|---|---:|---|
| `0x00` | `NULL` | 0 | terminates command parsing; remaining bytes are zero |
| `0x01` | `NEXT_NODE` | 64 | 32-byte next node ID and 32-byte next header MAC |
| `0x02` | `RECIPIENT` | 32 | opaque terminal recipient or return capability |
| `0x03` | `SURB_REPLY` | 16 | random single-use `surb_id` |
| `0x80` | `NODE_DELAY` | 4 | unsigned delay in profile-defined units |

Unknown tags are invalid. No vendor command range is accepted by core nodes.

### 10.1 Intermediate mix command sequence

Every nonterminal mix hop contains exactly:

```text
NODE_DELAY || NEXT_NODE || NULL || zero padding || next KEM ciphertext
```

The implementation may build commands in an internal representation, but the
encrypted routing bytes and parser result must preserve this semantic order.
There is exactly one delay and one next node. Zero delay is valid only if the
active cover profile explicitly includes it in the same public distribution
used for real and cover packets.

The `NODE_DELAY` value is validated before queue insertion. Values outside the
active delay profile, arithmetic overflow, or a deadline past epoch expiry
cause a uniform drop.

### 10.2 Forward terminal command sequence

The terminal hop contains exactly:

```text
RECIPIENT || NULL || zero padding || unused zero KEM slot
```

`RECIPIENT` is a random or derived 32-byte capability defined by the terminal
contract. It must not be a human address, username, public mailbox name, or
application-specific string. The terminal validates it before acting on the
payload.

### 10.3 Reply terminal command sequence

The entry terminal hop contains exactly:

```text
RECIPIENT || SURB_REPLY || NULL || zero padding || unused zero KEM slot
```

Here `RECIPIENT` is an opaque short-lived return-rendezvous capability and
`SURB_REPLY` selects the Composer's private reply token. The entry atomically
consumes the rendezvous before queueing the returned opaque payload.

### 10.4 Terminal padding

The current maintained KEMSphinx geometry reserves one KEM ciphertext-sized
tail in every per-hop routing block, including the terminal block. The
terminal tail is all zero before routing encryption. FOG does not apply the
possible one-ciphertext optimization until a new separately identified
profile has complete vectors and interoperability review.

## 11. Forward Plaintext Block

After the terminal KEMSphinx unwrap and payload-tag validation, a forward
payload has this exact layout:

```text
offset  length  field
0       1       surb_flag
1       1       reserved
2       S       surb_slot
2+S     U       user_payload
```

`reserved` is zero. `S` is the profile's exact `surb_length`. `U` is the exact
`user_forward_payload_length`.

`surb_flag` is:

- `0x00`: no usable SURB; the entire `surb_slot` is zero after decryption;
- `0x01`: `surb_slot` contains one valid SURB for the same packet profile and
  epoch.

All other values are invalid. The slot always exists and always occupies the
same bytes. Application code receives exactly `U` opaque bytes and an optional
validated public SURB. It does not receive short lengths from the packet
layer.

The 4,096-byte candidate user payload is owned internally by the next
protocol. FOG-MESSAGING or FOG-STORAGE defines authentication, actual body
length, padding, fragmentation, retries, deduplication, and acknowledgements
inside those bytes.

## 12. SURB Contract

### 12.1 Public SURB

The public SURB encoding is exactly:

```text
prebuilt_kemsphinx_header[header_length] ||
first_hop_packet_node_id[32] ||
reply_payload_key_material[sprp_key_material_length]
```

The packet profile and epoch are supplied by the containing authenticated
context and the local SURB object. They are not inserted into the opaque SURB
and do not change its size.

The prebuilt reply path has exactly four hops and ends in the entry terminal
commands described in section 10.3.

### 12.2 Private reply token

The Composer stores, separately from the public SURB:

```text
[
  token_version,
  packet_profile_id,
  epoch,
  surb_id,
  hard_expiry,
  status,
  reverse_order_payload_key_material
]
```

For four hops, `reverse_order_payload_key_material` contains five fixed key
and IV pairs: one per KEMSphinx hop plus the final reply payload pair. In the
candidate geometry this secret field is 320 bytes.

The token is secret Composer state. It is encrypted and integrity-protected
at rest under the Composer state profile, excluded from logs and routine
backups unless the backup design explicitly protects it, and erased after
successful use, expiry, cancellation, or uncertain duplicate handling.

### 12.3 Single-use state machine

The only valid state transitions are:

```text
AVAILABLE -> COMMITTED -> CONSUMED
AVAILABLE -> EXPIRED
COMMITTED -> BURNED
```

The terminal changes a public SURB from `AVAILABLE` to `COMMITTED` before
building or submitting a reply packet. A confirmed local construction error
before any packet or KEM operation may return it to `AVAILABLE`; after packet
construction or any send attempt, uncertainty results in `BURNED`.

The Composer atomically changes the private token to `CONSUMED` before
releasing successfully authenticated plaintext to an application. A second
return with the same `surb_id` is discarded without another decryption
attempt.

Applications needing retries provide multiple independently generated SURBs.
They never clone a SURB or its private token.

### 12.4 Tagging and compulsion limits

SURBs do not remove active-tagging or reply-compulsion risk. Implementations
MUST apply the same fixed packet schedule, reply size, and terminal queue
policy to successful replies, errors, and cover outcomes. A terminal cannot
send arbitrary immediate diagnostic replies outside the cover schedule.

## 13. Replay Protection

### 13.1 Raw replay tag

For an authenticated hop, the raw tag is:

```text
raw_replay_tag = HASH(
  "FOG-SPHINX-REPLAY-TAG-1" ||
  network_id ||
  u32be(packet_profile_id) ||
  u64be(epoch) ||
  kem_key_id ||
  current_hop_kem_ciphertext
)
```

Lengths for variable fields are fixed by the active PKI and packet profiles.
`HASH` and its output length are fixed by `replay_hash_id`.

The current hop KEM ciphertext is the public ciphertext at the front of the
current KEMSphinx header, not a future ciphertext hidden in routing data.

### 13.2 Stored replay ID

The durable database stores:

```text
stored_replay_id = MAC(
  replay_state_key_epoch,
  "FOG-SPHINX-REPLAY-STORE-1" || raw_replay_tag
)
```

This local keyed representation reduces direct correlation between a stolen
database and previously captured packet headers. It is not a substitute for
disk, process, or host protection.

`replay_state_key_epoch` is independently generated per node, packet profile,
and epoch. It is not derived from a KEMSphinx private key and is never shared
with another role or node.

### 13.3 Processing order

A hop processes an incoming packet in this order:

1. verify authenticated FOG-WIRE context, exact command body, and packet
   length;
2. enforce current epoch, profile, role, layer, upstream, and local key ID;
3. parse only fixed header offsets;
4. decapsulate the current KEM ciphertext;
5. derive hop keys and verify the current header MAC;
6. derive the raw replay tag and stored replay ID;
7. atomically check and durably insert the stored replay ID;
8. parse and validate the exact routing command sequence;
9. validate next-hop authorization and delay bounds;
10. transform the packet exactly once;
11. queue the transformed packet or terminal payload;
12. erase per-hop shared secrets and temporary keys.

An unauthenticated random ciphertext is not inserted. A header that
successfully authenticates is inserted before later command validation, even
when the command, next hop, delay, or terminal capability is invalid. This
prevents repeated authenticated malformed work from bypassing replay state.

### 13.4 Durable database

Replay insertion is a crash-consistent transaction. The packet is not made
eligible for forwarding until the write-ahead record or equivalent durable
commit succeeds. Group commit is permitted only if scheduling and forwarding
wait for the corresponding durable barrier.

A keyed in-memory filter MAY avoid many exact lookups, but it is only a front
cache. A positive filter result is confirmed against the exact durable set;
therefore filter false positives do not discard valid packets.

Replay databases are separated by node, role, packet profile, epoch, and KEM
key ID. They are excluded from telemetry, snapshots shared across nodes, and
ordinary backups. Aggregate counts may be exported only under the observer
privacy profile.

### 13.5 Restart and loss

On restart, a node verifies database integrity, replay-state key availability,
epoch ownership, and committed sequence state before accepting packets. It
rebuilds any in-memory filter from the exact durable set.

Missing, rolled-back, corrupt, or unverifiable replay state is fail-closed.
The node stops packet processing for that profile and epoch. It does not start
with an empty cache. Service can resume only from safely restored monotonic
state or a new epoch with fresh KEM and replay-state keys.

### 13.6 Retirement

An epoch replay database and its local key are retained until:

```text
epoch_hard_expiry
  + maximum_packet_lifetime
  + maximum_clock_uncertainty
  + crash_recovery_margin
```

has passed. Retirement erases the replay-state key and removes the database
through the deployment's recoverable secure-deletion policy. No node accepts
new work merely because an old database still exists.

## 14. KEMSphinx Hop Processing

Every mix worker has fixed-size buffers from its active geometry. It does not
allocate based on decrypted command values.

For a valid intermediate hop it:

- decrypts the current routing block;
- obtains the authenticated delay and next node;
- shifts the routing information according to KEMSphinx;
- copies the hidden next-hop KEM ciphertext into the public KEM field;
- applies one payload permutation;
- commits replay state;
- enters the bounded delay queue;
- emits the exact transformed packet on the authorized next link.

For a valid terminal it:

- verifies the terminal command sequence;
- verifies the final payload integrity tag;
- validates the fixed forward plaintext block for a forward packet, or
  returns the still SURB-protected payload and `surb_id` for a reply packet;
- consumes any terminal or rendezvous capability atomically;
- hands only the bounded opaque object to the owning role contract.

No mix exposes whether failure was KEM, MAC, replay, command, delay, route,
queue, payload tag, recipient, or epoch. Remote behavior is the same uniform
drop class and is subject to the cover schedule.

## 15. Delay Contract

`NODE_DELAY` contains a 32-bit count in the unit fixed by the active delay
profile. Implementations convert using checked arithmetic and a monotonic
clock.

The delay profile fixes at least:

```text
[
  delay_profile_id,
  unit_nanoseconds,
  maximum_encoded_delay,
  maximum_per_hop_delay,
  maximum_route_delay,
  sampling_distribution_id,
  quantization_rule,
  queue_deadline_rule,
  cover_schedule_profile_id
]
```

The Composer samples every mix delay from this authenticated distribution.
Operators do not alter it locally. Mixes validate the encoded value but do not
resample it. Queue pressure does not convert delayed traffic to immediate
traffic; overload follows the profile's uniform drop or shutdown behavior.

The initial distribution and numeric limits remain simulation outputs. No
anonymity or latency claim follows from this structural specification.

`FOG-SIMULATION.md` records an initial 500 ms versus 5 second exponential-delay
sensitivity comparison. It selects neither value. The longer value improved
local pool overlap under one high-cover scenario while increasing modeled
latency by roughly an order of magnitude; formal end-to-end and operational
evidence remains open.

## 16. Padding and Fragmentation

### 16.1 Packet padding

Routing blocks, forward SURB slots, user payloads, reply payloads, and unused
fields always occupy their full profile lengths. Plain structural padding is
zero before the applicable cryptographic layer. Random bytes are used only
where the selected reviewed construction requires randomness.

Application code MUST NOT create a shorter KEMSphinx payload. It supplies an
exact fixed-size inner envelope whose internal padding is authenticated by the
owning end-to-end protocol.

### 16.2 Application fragmentation

Messages larger than `user_forward_payload_length` are fragmented by
FOG-MESSAGING or FOG-STORAGE before KEMSphinx construction. Fragment metadata
is inside the end-to-end protected envelope. Each fragment becomes an
independent fixed-size KEMSphinx packet with independent route randomness,
KEM ciphertexts, replay tags, and optional SURB.

KEMSphinx does not retransmit or deduplicate fragments. Reusing a packet for a
retry is forbidden. A retry constructs a fresh packet under the application
protocol's idempotency rules.

### 16.3 FOG-WIRE fragmentation

`PACKET_FORWARD` and the packet portion released from `PACKET_SUBMIT` are one
logical FOG-WIRE message with an exact KEMSphinx packet body. FOG-WIRE may
split that logical message across its fixed DATA records when the active wire
profile requires it.

Wire fragments are link-local and are fully reassembled, bounded, and
authenticated before KEMSphinx parsing. They are never individually queued,
replayed, forwarded, or stored as KEMSphinx packets.

## 17. FOG-WIRE Command Bodies

This document owns these logical command shapes:

```text
PACKET_FORWARD_BODY = kemsphinx_packet[packet_length]

PACKET_RETURN_BODY =
  surb_id[16] ||
  returned_payload[payload_tag_length + forward_payload_length]
```

`PACKET_FORWARD_BODY` is used for entry-to-L1, mix-to-mix, and L3-to-terminal
links. Link context and direction determine which role pair is legal.

`PACKET_RETURN_BODY` is created only after the entry completes the final reply
hop and atomically consumes the return rendezvous. The returned payload remains
protected by the Composer's private reply token. The relay cannot decrypt or
modify it successfully.

`PACKET_SUBMIT_BODY` remains owned by the entry-capsule specification because
it must hide and bind the first internal hop from the blind relay.

A compatible FOG-WIRE command-shape registry has compile-time exact body
limits for these commands. Generic byte-string RPCs are not conforming.

## 18. Candidate Geometry

### 18.1 Status

The following profile is a calculated candidate:

```text
name               = FOG-SPHINX-CANDIDATE-MLKEM768-X25519-1
packet_profile_id  = UNASSIGNED
candidate_status   = geometry-only
```

The candidate name is permanently bound to this calculated geometry. It is
not a complete packet profile because final primitive IDs, dependency
revisions, delay limits, replay lifetime, and conformance vector IDs are still
unresolved. A numeric packet profile ID is assigned only after every field in
section 6 is frozen. It will not reuse an identifier from another candidate.

This candidate MUST NOT be placed in a claim-bearing public consensus until
section 24's activation gates pass.

`FOG-CRYPTO-SUITES.md` admits the exact calculated construction to
complete-packet benchmarking but does not activate it. The unresolved
non-KEM primitives and implementation evidence keep the complete profile
non-active.

`FOG-CRYPTO-BENCHMARKS.md` records a first-host benchmark of this exact
geometry. Future implementations MUST preserve its strict integration
finding: the parameterized maintained API is wrapped by a FOG boundary that
rejects every path, packet, payload, SURB, encrypted reply, or reply-key length
that differs from this profile before cryptographic processing. The first-host
result does not change `geometry-only` status or assign a profile ID.

### 18.2 Candidate primitive inputs

The geometry calculation uses:

- KEMSphinx with one KEM ciphertext per hop;
- exactly four hops;
- hybrid `MLKEM768-X25519` built by the maintained HPQC security-preserving
  split-PRF combiner;
- component and ciphertext concatenation order of X25519 hashed-ElGamal KEM
  first, then ML-KEM-768, matching the evaluated HPQC registry despite the
  display name; the split-PRF is order-sensitive;
- ML-KEM-768 ciphertext length of 1,088 bytes;
- X25519 hashed-ElGamal KEM ciphertext length of 32 bytes;
- combined KEM ciphertext length of 1,120 bytes;
- 32-byte packet node IDs;
- 32-byte header MACs;
- 16-byte SURB IDs;
- 32-byte payload integrity tags;
- 48-byte SPRP keys and 16-byte SPRP IVs;
- two additional-data bytes fixed to `0x0000` for compatibility with the
  evaluated maintained KEMSphinx format;
- a two-byte forward plaintext header;
- a 4,096-byte user forward payload;
- one fixed SURB slot in every forward plaintext block.

The primitive suite currently evaluated with this geometry includes the
maintained Katzenpost KDF, header MAC, header stream, and AEZ-based payload
SPRP parameterization. Geometry compatibility does not constitute approval of
that primitive suite. In particular, the exact dependency revisions,
side-channel behavior, licensing, AEZ usage, deterministic vectors, and
independent review remain activation gates.

### 18.3 Exact calculation

```text
KEM_CIPHERTEXT_LENGTH = 1088 + 32
                      = 1120

NEXT_NODE_LENGTH      = 1 + 32 + 32
                      = 65

SURB_REPLY_LENGTH     = 1 + 16
                      = 17

PER_HOP_ROUTING_INFO  = 65 + 17 + 1120
                      = 1202

ROUTING_INFO_LENGTH   = 4 * 1202
                      = 4808

HEADER_LENGTH         = 2 + 1120 + 4808 + 32
                      = 5962

SPRP_KEY_MATERIAL     = 48 + 16
                      = 64

SURB_LENGTH           = 5962 + 32 + 64
                      = 6058

FORWARD_PAYLOAD       = 2 + 6058 + 4096
                      = 10156

PACKET_LENGTH         = 5962 + 32 + 10156
                      = 16150

PRIVATE_REPLY_KEYS    = (4 + 1) * 64
                      = 320
```

### 18.4 Candidate geometry table

| Field | Bytes |
|---|---:|
| Hop count | 4 hops |
| Additional data | 2 |
| KEM ciphertext | 1,120 |
| Per-hop routing information | 1,202 |
| Routing information | 4,808 |
| Header MAC | 32 |
| Header | 5,962 |
| Payload tag | 32 |
| Public SURB | 6,058 |
| Forward plaintext header | 2 |
| User forward payload | 4,096 |
| Forward payload | 10,156 |
| Complete KEMSphinx packet | 16,150 |
| Private reply key material | 320 |

Every arithmetic value is a protocol constant for this candidate. Runtime
configuration cannot change it.

## 19. Key Lifecycle

Every mix and terminal has an independently generated KEMSphinx key for each
authorized packet profile and epoch. Descriptors bind the public key, key ID,
purpose, owner node, role, layer, profile, and validity interval.

Private keys:

- are generated with an approved operating-system randomness source;
- are written only to role-local protected storage;
- are loaded only by the owning role process;
- are never copied to another layer, co-located role, authority, relay, or
  observer;
- are never used for Noise, entry capsules, storage, signatures, or messages;
- remain available only for the exact old-epoch drain interval;
- are erased after packet and replay retirement conditions both hold.

Key generation and persistence are crash-safe. A descriptor is not published
until the private key is durably available to its owner. A node never creates
a fresh private key under an already published key ID.

KEM decapsulation failures, malformed public keys, and component failures in
a hybrid KEM are fatal to that packet. A hybrid implementation must reject
incorrect component lengths before component decapsulation and must not reveal
which component failed.

## 20. Parser and Resource Limits

Before cryptographic work, an implementation enforces:

- exact logical message length;
- exact packet profile from connection context;
- one bounded packet buffer;
- no recursive, compressed, map-based, or self-describing packet data;
- no allocation based on routing commands;
- no profile or algorithm name supplied by the peer;
- per-connection, per-peer, per-key, and global cryptographic work budgets;
- bounded queues and deadlines.

After decryption, it enforces:

- exact command count, type, order, and zero padding;
- one legal next role and layer;
- delay bounds and epoch completion bounds;
- terminal capability length and one-time state;
- exact forward block flags and zero reserved bytes;
- exact payload and SURB lengths.

Memory containing shared secrets, per-hop keys, private reply keys, and
decrypted routing blocks is cleared promptly using the reviewed library's
supported mechanism. Memory clearing is defense in depth and does not replace
process isolation.

## 21. Logging and Observability

Core roles MUST NOT log:

- packet bytes or packet digests;
- raw replay tags or stored replay IDs;
- KEM ciphertexts or shared secrets;
- `surb_id`, SURBs, private reply tokens, or rendezvous capabilities;
- per-packet routes, next-hop IDs, delay values, or fine timing;
- terminal recipient capabilities or user payloads.

Permitted local diagnostics are coarse reason counters, queue occupancy
buckets, bounded latency histograms, replay database health state, and
profile-level totals under FOG-OBSERVER privacy rules. Operators cannot enable
packet tracing on a claim-bearing profile.

Test builds may use deterministic vectors and verbose traces only with public
test keys and synthetic packets. Such builds and keys are rejected by
production configuration.

## 22. SDK Boundary

The stable public SDK exposes typed operations, not raw cryptographic knobs.
It contains at least:

```text
ValidatePacketProfile(profile, consensus_context)
DerivePacketNodeID(profile, node_id)
BuildForwardRoute(consensus, terminal, rng)
BuildReplyRoute(consensus, rendezvous, surb_id, rng)
CreateSURB(profile, epoch, reply_route, rng)
CreateForwardPacket(profile, route, fixed_payload, optional_surb, rng)
CreateReplyPacket(profile, public_surb, fixed_payload)
UnwrapOneHop(profile, epoch_context, private_key, exact_packet)
DecryptSURBReply(private_token, surb_id, exact_returned_payload)
```

The SDK does not expose:

- arbitrary hop counts for core profiles;
- operator-selected algorithms or geometry;
- raw private-key serialization through ordinary application APIs;
- reuse or cloning of SURBs or reply tokens;
- packet parsing without an authenticated profile and epoch context;
- automatic foreign-profile detection;
- application-dependent packet sizes.

SURB and private-token types are move-only or guarded by an atomic persistent
state abstraction. Copyable byte slices are not the primary API.

The same reference codec and vector set are used by Composer, entry, mix,
terminal, relay import, simulator, and conformance tools. Each executable
still imports only the operations needed by its role.

## 23. Conformance Evidence

Each packet profile release includes machine-readable vectors generated with
fixed public test entropy. The vector manifest records dependency versions,
source revision, profile record hash, and generation command.

The required vector set covers:

1. packet node ID derivation;
2. hybrid KEM component key and ciphertext lengths;
3. complete candidate geometry arithmetic;
4. every routing command encoding and invalid tag;
5. zero padding and terminal unused KEM slot;
6. one complete four-hop forward packet at every unwrap;
7. one complete four-hop SURB reply at every unwrap and final decryption;
8. replay tags at each hop and durable duplicate rejection;
9. changed ciphertext, MAC, command, padding, payload tag, and SURB failures;
10. wrong profile, epoch, role, layer, route length, and upstream failures;
11. maximum and invalid delay values;
12. SURB state transitions, double use, expiry, and uncertain send;
13. replay database restart, rollback, corruption, and retirement;
14. FOG-WIRE fragmentation and exact logical body reassembly;
15. profile overlap with separate old and new epoch connections;
16. cross-implementation byte equality.

Property and fuzz tests additionally cover all fixed-offset parsers,
truncations at every byte boundary, trailing bytes, unknown commands, command
reordering, integer boundaries, malformed KEM component lengths, and queue
resource limits.

At least two independently integrated implementations must reproduce the
complete vectors before a claim-bearing profile becomes active. Calling the
same library through two thin wrappers is not independent evidence.

## 24. Activation Gates

`FOG-SPHINX-CANDIDATE-MLKEM768-X25519-1` remains non-active until all of the
following are complete:

- reproducible create, unwrap, SURB, and replay benchmarks on every supported
  hardware class using the exact 16,150-byte geometry; the first older x86-64
  create, unwrap, and SURB baseline is complete, while replay and the remaining
  hardware classes are open;
- memory, queue, storage, and maximum-throughput budgets for every role;
- dependency revision pinning, license review, and reproducible builds;
- review of the ML-KEM-768 implementation and X25519 adapter;
- confirmation that the exact security-preserving combiner meets the
  KEMSphinx hybrid-KEM requirement;
- review of KDF, MAC, stream, payload SPRP, fragile payload-tag construction,
  and side-channel behavior;
- complete deterministic vectors and negative corpus;
- fuzzing and restart-safe replay tests;
- simulator evidence for packet size, delay, cover traffic, n-1, and long-term
  disclosure behavior;
- compatibility verification with the selected maintained KEMSphinx target;
- independent protocol and implementation review;
- a separately documented decision promoting an exact immutable record to
  `alpha` and later `active`.

Failure of a gate produces a newly named geometry candidate or a newly
numbered complete packet profile, as applicable. It does not silently change
this candidate.

## 25. Profile Transitions

A packet-profile transition follows the FOG-PKI preannouncement and bounded
overlap rules.

During overlap:

- new work uses the profile mapped to the new epoch;
- old work drains only on old-epoch FOG-WIRE connections;
- each profile has separate KEM keys, replay-state keys, replay databases,
  queues, command-shape registry, and SDK object types;
- a packet is never converted in place from one profile to another;
- a failure in the new profile does not reactivate the old profile;
- minimum accepted profile state is monotonic.

After hard expiry, old connections close, old packets and SURBs are rejected,
private reply tokens expire, replay state completes its retention period, and
private KEM keys are erased.

Emergency retirement can stop creation and acceptance immediately, but it
cannot downgrade. In-flight delivery may be lost.

## 26. External Sphinx-Family Bridges

Foreign Sphinx, KEMSphinx, Katzenpost, Nym, YAMN, SMTP, NNTP, and other network
formats never enter a core packet parser. A bridge has:

- a separate `fog-bridge-*` executable and service identity;
- separate ports, FOG-WIRE link context, keys, writable state, queues, replay
  domains, metrics, and deployment policy;
- one explicitly named foreign profile and version;
- strict parsing and resource bounds for that profile only;
- no access to Composer plaintext or core private keys;
- a fresh core packet constructed after policy validation, never a header
  reinterpretation or in-place conversion;
- explicit disclosure as a correlation and availability point.

A bridge terminates one network anonymity context and originates another. It
cannot claim end-to-end mix-path unlinkability across the boundary, even when
the application payload remains end-to-end encrypted.

Automatic port sharing, packet sniffing, trial parsing, or silent downgrade
between core and foreign profiles is forbidden.

## 27. Failure Matrix

| Condition | Required behavior |
|---|---|
| Wrong packet length | reject before KEM work |
| Wrong epoch or profile context | reject, no alternate-key trial |
| KEM decapsulation failure | uniform drop |
| Header MAC failure | uniform drop, no durable replay insertion |
| Authenticated duplicate | uniform drop after exact replay lookup |
| Authenticated invalid command | durable replay insert, then uniform drop |
| Invalid next role or layer | durable replay insert, then uniform drop |
| Invalid delay or expired deadline | durable replay insert, then uniform drop |
| Replay commit failure | stop affected profile and epoch processing |
| Replay state missing or rolled back | fail closed until safe restore or new epoch |
| Delay queue overload | profile-defined uniform drop or role shutdown |
| Final payload tag failure | uniform drop, no terminal action |
| Invalid terminal capability | uniform drop after one-time-state rules |
| SURB reused or expired | discard without constructing another reply |
| Private token missing or consumed | discard returned payload |
| Old profile failure during transition | do not fall back or extend expiry |
| Foreign packet on core port | reject before foreign parsing |

Local diagnostics use coarse stable reason classes. Remote peers receive no
fine-grained error response.

## 28. Threat Traceability

| Threat-model concern | Packet-profile response |
|---|---|
| `TM-NET-01` traffic correlation | one fixed geometry, four hops, authenticated delay and cover profile |
| `TM-NET-02` low anonymity set | no packet-format claim; simulator and deployment gates remain required |
| `TM-NET-03` compromised mixes | stratified route, one transform, role validation, purpose-separated keys |
| `TM-NET-04` replay and compulsion | durable per-hop replay, single-use SURBs and rendezvous state |
| `TM-NET-05` tagging | authenticated header and payload processing, uniform terminal failure |
| `TM-NET-06` profile fingerprinting | consensus-selected immutable profiles, no autodetection or negotiation |
| `TM-PKI-02` stale consensus | epoch-bound packet context and monotonic transition state |
| `TM-ROLE-01` role collapse | separate KEM keys, replay state, ports, commands, and bridge processes |
| `TM-ROLE-02` relay knowledge | entry capsule hides L1; relay handles only opaque fixed packets |
| `TM-ROLE-03` terminal exposure | terminal gets fixed opaque application payload, not source address or full route |
| `TM-CRYPTO-01` primitive misuse | reviewed parameterized library, immutable suite record, activation gates |
| `TM-CRYPTO-02` key reuse | role, purpose, profile, and epoch separation |
| `TM-AVAIL-01` resource exhaustion | fixed buffers, crypto budgets, durable replay, bounded queues and failures |

## 29. Residual Risks

Even a conforming implementation remains exposed to:

- traffic analysis from timing, volume, routes, endpoints, and sparse use;
- n-1 and active-delay attacks by sufficiently placed malicious nodes;
- denial of service through connection, KEM, replay-store, queue, or terminal
  exhaustion within residual budgets;
- endpoint or Composer compromise;
- malicious or colluding entry, mix, terminal, relay, storage, and authority
  operators within the threat model's residual cases;
- reply tagging and compulsion not eliminated by SURBs;
- implementation, dependency, side-channel, randomness, and erasure defects;
- correlation introduced by external bridges;
- loss caused by fail-closed replay and single-use reply behavior;
- incorrect anonymity conclusions from a local PoC or low-traffic network.

These risks must appear in deployment documentation and public claims.

## 30. References

- George Danezis and Ian Goldberg, *Sphinx: A Compact and Provably Secure Mix
  Format*, 2009: <https://eprint.iacr.org/2008/475>
- Katzenpost, *The KEMSphinx Cryptographic Packet Format*:
  <https://katzenpost.network/docs/specs/kemsphinx/>
- Katzenpost, *The Sphinx Cryptographic Packet Format*:
  <https://katzenpost.network/docs/specs/sphinx/>
- Katzenpost, *The Katzenpost Mix Network Wire Protocol*:
  <https://katzenpost.network/docs/specs/mixnet/>
- Katzenpost, *Katzenpost Mix Network Replay Detection*:
  <https://katzenpost.network/docs/specs/packet_replay/>
- Federico Giacon, Felix Heuer, and Bertram Poettering, *KEM Combiners*,
  2018: <https://eprint.iacr.org/2018/024>
- NIST, *FIPS 203: Module-Lattice-Based Key-Encapsulation Mechanism Standard*,
  2024: <https://csrc.nist.gov/pubs/fips/203/final>
- RFC 7748, *Elliptic Curves for Security*:
  <https://www.rfc-editor.org/rfc/rfc7748>
- FOG Composer protocol: `FOG-COMPOSER.md`
- FOG cryptographic suite evaluation: `FOG-CRYPTO-SUITES.md`
- FOG cryptographic benchmark baseline: `FOG-CRYPTO-BENCHMARKS.md`
- FOG traffic and topology simulation: `FOG-SIMULATION.md`

## 31. Completion Checklist

Before implementation work for an active packet profile begins, reviewers
must be able to answer yes to all of these questions:

- Is one immutable profile selected by authenticated consensus and epoch
  context without packet guessing?
- Are both directions exactly four KEMSphinx hops with the entry outside the
  forward route?
- Are packet, header, routing, payload, SURB, and reply-token sizes proven by
  arithmetic and tests?
- Are command sequences exact, padded, role-valid, and parser-bounded?
- Is replay state durable before forwarding and fail-closed after rollback or
  loss?
- Are SURBs, private tokens, IDs, and rendezvous capabilities atomic and
  single-use?
- Is application fragmentation inside end-to-end protection and independent
  of packet size?
- Are KEM, replay, Noise, entry, storage, and message keys purpose-separated?
- Do FOG-WIRE command shapes and packet geometry agree exactly?
- Are complete vectors, fuzzing, benchmarks, simulation, and independent
  review available for the exact dependency set?
- Are external formats isolated in separate bridge processes and disclosed as
  correlation points?
- Do failures stop safely without topology, profile, or plaintext fallback?
