Core System Architecture Part 36 — Sphinx Packet, Cell, Fragmentation & Anonymous Message Framing Architecture

SIAR — Secure, Local-First, Multi-Transport Communication Platform

Series: Core System Architecture
Part: 36
Status: Architecture specification
Primary language: Rust
Builds on:

  • Part 34 — Mixnet, Loopix, Sphinx, Nym & High-Anonymity Transport Architecture
  • Part 35 — Anonymous Mailbox, Offline Receiving & Reply Capability Architecture

Primary purpose: define the complete anonymous packet and message-framing substrate for SIAR, including Sphinx-style packet layering, normalized cells, fragmentation/reassembly, padding, versioning, reply-block embedding, replay protection, integrity boundaries, provider adaptation, parser hardening, and performance/resource limits.


1. Purpose

SIAR's high-anonymity transport plane needs a transport framing system that does not expose application payload shape directly.

Without normalization, an observer may correlate traffic using:

message length
fragment count
packet timing
attachment size
message type
reply structure

even when payload contents are encrypted.

The governing principle is:

Anonymous transport packets must reveal as little application structure as practical, while remaining bounded, versioned, replay-safe, and provider-independent.


2. Architectural Position

Application Message
      │
      ▼
Application E2EE
      │
      ▼
Anonymous Message Framing
      │
      ├── message envelope
      ├── fragmentation
      ├── padding
      ├── cell normalization
      └── reassembly metadata
      │
      ▼
Provider Packet Layer
      │
      ├── Sphinx-style packet
      └── provider-specific encoding
      │
      ▼
Mixnet

3. Sphinx Is Below SIAR E2EE

Correct layering:

SIAR E2EE payload
    ↓
anonymous framing
    ↓
Sphinx/provider packetization

Never:

Sphinx replaces SIAR E2EE

4. Design Goals

The framing layer MUST support:

fixed/normalized packet sizes
message fragmentation
message reassembly
bounded memory
versioning
corruption detection
duplicate suppression
reply capability embedding
provider adaptation
small-control-message efficiency
attachment chunking
padding
forward compatibility
fuzz-safe parsing

5. Non-Goals

The framing layer does not:

define user identity
perform application E2EE
decide route policy
provide presence
store chat history
choose mix nodes directly
invent new cryptography

6. Packet vs Cell vs Message

Use precise terminology.

Application Message
    user/domain object

Anonymous Envelope
    provider-neutral encrypted message container

Cell
    normalized framing unit

Sphinx Packet
    routed anonymous packet carrying one or more cell payload portions

7. Recommended Layering

Message
  ↓
E2EE Envelope
  ↓
AnonymousMessageFrame
  ↓
Fragment(s)
  ↓
Cell(s)
  ↓
ProviderPacket
  ↓
Sphinx Packet

8. Anonymous Message Frame

#![allow(unused)]
fn main() {
pub struct AnonymousMessageFrame {
    pub version: AnonymousFrameVersion,
    pub class: AnonymousMessageClass,
    pub frame_id: AnonymousFrameId,
    pub expires_at: Timestamp,
    pub payload: Bytes,
}
}

9. Frame ID

#![allow(unused)]
fn main() {
pub struct AnonymousFrameId(pub [u8; 16]);
}

Must be:

cryptographically random
non-sequential
non-user-derived

10. Frame ID Scope

Frame ID is transport-scoped.

Do not reuse:

MessageId
ConversationId
AccountId
DeviceId

11. Message Classes

#![allow(unused)]
fn main() {
pub enum AnonymousMessageClass {
    Control,
    Text,
    Receipt,
    Bootstrap,
    MailboxControl,
    ReplyCapability,
    AttachmentChunk,
}
}

12. Message Class Leakage

If class is visible outside encrypted framing, it can leak metadata.

Preferred:

class encrypted inside provider-protected body

13. Fixed-Size Cell Principle

Cells should use a small number of normalized sizes.

Example classes:

#![allow(unused)]
fn main() {
pub enum CellSizeClass {
    Small,
    Standard,
    Large,
}
}

14. Initial Recommendation

Prefer:

one dominant standard cell size

for maximum anonymity.

Additional sizes can improve efficiency but weaken size indistinguishability.


15. Cell Size Tradeoff

Larger cells:

less fragmentation
better throughput
more padding waste for tiny messages

Smaller cells:

better efficiency for short control messages
more fragments for large payloads

16. Cell Payload Budget

Each cell must reserve bytes for:

version
fragment header
authentication/integrity
provider overhead
padding

17. Cell Layout

Conceptual:

+------------------------------+
| Cell Version                 |
| Frame/Fragment Metadata      |
| Encrypted Fragment Payload   |
| Padding                      |
| Integrity / Authentication   |
+------------------------------+

Exact cryptographic layout belongs to provider/established packet format.


18. Do Not Invent Sphinx Cryptography

Use audited/provider-compatible implementation.

SIAR owns only:

framing semantics
bounds
mapping
integration

19. Fragmentation

If payload exceeds one cell:

frame
→ fragment 0
→ fragment 1
→ fragment 2
...

20. Fragment Header

#![allow(unused)]
fn main() {
pub struct FragmentHeader {
    pub frame_id: AnonymousFrameId,
    pub index: u32,
    pub total: u32,
    pub payload_len: u32,
}
}

21. Fragment Count Bound

Hard limit.

Example concept:

max fragments per frame

must be configurable and enforced before allocation.


22. Maximum Frame Size

#![allow(unused)]
fn main() {
pub struct FramingLimits {
    pub max_frame_bytes: u64,
    pub max_fragments: u32,
    pub max_inflight_reassemblies: u32,
    pub max_reassembly_bytes: u64,
}
}

23. Attachment Strategy

Large files should not become one gigantic anonymous frame.

Use:

attachment object
→ chunk sequence
→ each chunk independently framed

24. Attachment Chunk Identity

Separate:

#![allow(unused)]
fn main() {
pub struct AnonymousAttachmentChunkId {
    pub transfer: TransferId,
    pub chunk_index: u32,
}
}

But provider-facing fields should not expose raw TransferId.

Use transport-scoped opaque IDs externally.


25. Reassembly

Recipient stores partial fragments in bounded staging.


26. Reassembly Key

FrameId
+
version

27. Reassembly State

#![allow(unused)]
fn main() {
pub struct ReassemblyState {
    pub frame_id: AnonymousFrameId,
    pub expected_fragments: u32,
    pub received: BitSet,
    pub received_bytes: u64,
    pub expires_at: Timestamp,
}
}

28. Reassembly Completion

When all fragments present:

validate bounds
→ reconstruct frame
→ validate integrity/authentication
→ pass to E2EE layer

29. Incomplete Reassembly

If expired:

discard

30. Reassembly Timeout

Must be bounded.


31. Fragment Reordering

Expected and supported.


32. Duplicate Fragments

Ignore after authenticated dedup.


33. Conflicting Duplicate

If same fragment index has different authenticated contents:

security anomaly

reject frame.


34. Corrupt Fragment

Drop.

Do not retain indefinitely.


35. Fragment Authentication Boundary

Each fragment/cell must be protected by provider-established integrity/authentication.


36. Application Integrity Boundary

Final payload still requires:

SIAR E2EE authentication

37. Two Integrity Layers

transport packet integrity
+
application E2EE integrity

Both are useful.


38. Padding

Padding hides exact payload length.


39. Padding Policy

#![allow(unused)]
fn main() {
pub enum PaddingPolicy {
    None,
    ToCellBoundary,
    FixedFrameClass,
    MaximumAnonymity,
}
}

40. Standard Anonymous Mode

Recommended:

ToCellBoundary

41. Maximum Anonymity

Recommended:

FixedFrameClass
+
normalized batching

where practical.


42. Padding Bytes

Generated cryptographically/randomly as required by framing/provider.


43. Do Not Encode Length in Padding Pattern

Hard rule.


44. Length Field

Only include minimal authenticated length metadata needed for reassembly.


45. Final Fragment Length

Can be encoded inside encrypted/authenticated frame metadata.


46. Size Bucketing

Potential:

512 B
1 KiB
2 KiB
4 KiB

but fewer buckets improve anonymity.


47. Packet Normalization Policy

#![allow(unused)]
fn main() {
pub struct PacketNormalizationPolicy {
    pub cell_size: CellSizeClass,
    pub padding: PaddingPolicy,
    pub batch_class: BatchClass,
}
}

48. Batch Class

#![allow(unused)]
fn main() {
pub enum BatchClass {
    Immediate,
    ShortDelay,
    PrivacyBatch,
}
}

49. Batch Timing

Framing layer can tag desired class.

Actual delay scheduling belongs to anonymity transport/provider.


50. No Content-Based Timing Leakage

Avoid:

attachments always delayed
text always immediate

if privacy mode aims to hide class.


51. Message Type Hiding

Maximum anonymity may map multiple application classes to same cell/framing profile.


52. Control Messages

Tiny control messages should still use normalized cells.


53. Receipts

Avoid unique tiny packet signatures.


54. Reply Capabilities

Reply capability material may be embedded inside encrypted anonymous frame.


55. Reply Descriptor Container

#![allow(unused)]
fn main() {
pub struct ReplyCapabilityEnvelope {
    pub version: u16,
    pub capability: SecretBytes,
    pub expires_at: Timestamp,
}
}

56. Reply Capability Secrecy

Never expose in:

logs
debug dumps
support bundles

57. SURB Embedding

Provider adapter maps:

ReplyCapabilityEnvelope
→ provider SURB/reply-block format

58. Provider-Neutral Core

Core should not know:

Nym SURB binary layout

59. Provider Packet Adapter

#![allow(unused)]
fn main() {
pub trait AnonymousPacketAdapter {
    type ProviderPacket;

    fn encode_cell(
        &self,
        cell: AnonymousCell,
        route: &AnonymousRouteContext,
    ) -> Result<Self::ProviderPacket, PacketError>;

    fn decode_cell(
        &self,
        packet: Self::ProviderPacket,
    ) -> Result<AnonymousCell, PacketError>;
}
}

60. Anonymous Cell

#![allow(unused)]
fn main() {
pub struct AnonymousCell {
    pub version: CellVersion,
    pub frame_id: AnonymousFrameId,
    pub fragment_index: u32,
    pub fragment_total: u32,
    pub payload: Bytes,
}
}

61. Cell Version

#![allow(unused)]
fn main() {
pub struct CellVersion(pub u16);
}

62. Version Strategy

Support explicit:

current
previous compatible
future unsupported

63. Version Negotiation

Do not negotiate per packet in a linkable way if avoidable.

Use capability negotiation via established secure channel.


64. Unknown Version

Reject safely.


65. Forward Compatibility

Optional extension area can be authenticated and length-prefixed.


66. Extension Budget

Bound extension bytes.


67. Parser Design

Parsers must be:

total-length checked
allocation-bounded
integer-overflow safe
duplicate-field safe
version aware

68. No Unbounded Vec Allocation from Network Length

Hard rule.


69. Integer Safety

Use checked conversions.


70. Malformed Fragment Count

Reject before allocation.


71. Oversized Payload Length

Reject.


72. Zero Fragment Total

Reject.


73. Fragment Index >= Total

Reject.


74. Frame Expiry

Reject expired frame early where safe.


75. Replay Resistance

Framing layer should support transport replay detection.


76. Replay Keys

Potential:

packet tag
frame ID
fragment ID
provider replay tag

77. Provider Replay Protection

Prefer provider/Sphinx implementation where available.


78. SIAR Reassembly Replay Cache

Still keep bounded local cache.


79. Replay Cache Entry

#![allow(unused)]
fn main() {
pub struct ReplayKey {
    pub frame_id: AnonymousFrameId,
    pub fragment_index: u32,
}
}

80. Replay Cache TTL

At least as long as valid transport lifetime.


81. Frame Lifetime

#![allow(unused)]
fn main() {
pub struct AnonymousFramePolicy {
    pub max_lifetime: Duration,
}
}

82. Expiry Hiding

If expiry metadata must be provider-visible, use coarse buckets.

Prefer encrypted expiry.


83. Timestamp Precision

Avoid exact user-action timing where possible.


84. Message Batching

Multiple small E2EE messages could optionally be packed into one anonymous frame.


85. Batch Envelope

#![allow(unused)]
fn main() {
pub struct AnonymousBatchFrame {
    pub version: u16,
    pub items: Vec<AnonymousBatchItem>,
}
}

86. Batch Limits

Bound:

item count
total bytes
lifetime spread

87. Batching Tradeoff

Benefits:

less overhead
better traffic normalization

Costs:

delivery coupling
latency
larger loss impact

88. Initial Recommendation

Do not batch unrelated conversations in v1.


89. Conversation Correlation

Batching unrelated recipients can complicate routing/privacy semantics.


90. Same-Recipient Batch

Safer initial option.


91. Control/Data Separation

Avoid exposing class via distinct packet sizes.

Use same normalized cell where possible.


92. Provider MTU

Different providers may have different packet capacity.


93. Effective Cell Capacity

provider packet size
-
routing header
-
crypto overhead
-
SIAR framing overhead
=
cell payload capacity

94. MTU Calculator

#![allow(unused)]
fn main() {
pub trait PacketCapacityCalculator {
    fn payload_capacity(
        &self,
        profile: &AnonymousPacketProfile,
    ) -> Result<usize, PacketError>;
}
}

95. No Magic Constants Scattered in Code

Centralize packet sizing.


96. Provider Packet Profile

#![allow(unused)]
fn main() {
pub struct AnonymousPacketProfile {
    pub provider: AnonymousProviderId,
    pub size_class: CellSizeClass,
    pub version: CellVersion,
}
}

97. Fragmenter

#![allow(unused)]
fn main() {
pub trait AnonymousFragmenter {
    fn fragment(
        &self,
        frame: &AnonymousMessageFrame,
        profile: &AnonymousPacketProfile,
    ) -> Result<Vec<AnonymousCell>, FramingError>;
}
}

98. Streaming Fragmentation

For large data, avoid collecting all fragments in memory.

Prefer iterator/stream:

#![allow(unused)]
fn main() {
pub trait StreamingFragmenter {
    type Stream: Stream<Item = Result<AnonymousCell, FramingError>>;

    fn fragment_stream(
        &self,
        source: PayloadSource,
        profile: AnonymousPacketProfile,
    ) -> Self::Stream;
}
}

99. Streaming Reassembly

Likewise support bounded incremental reassembly.


100. Attachment Chunking Before Framing

Recommended:

file
→ encrypted attachment chunks
→ anonymous frames
→ cells

101. Why Two Chunk Layers

Attachment chunking solves:

resume
integrity
storage

Cell fragmentation solves:

transport packet size

Keep them separate.


102. Do Not Couple File Chunk Size to Sphinx Cell Size

Hard rule.


103. Reassembly Storage

Small frames:

memory

Large frames:

bounded temp storage

104. Threshold

Benchmark-defined.


105. Temp Storage Encryption

Reassembly temp files must remain encrypted-at-rest.


106. Reassembly Garbage Collection

Periodic cleanup:

expired
invalid
orphaned

107. Fragment Storm Defense

Limit per source capability/mailbox.


108. Incomplete Frame Quota

#![allow(unused)]
fn main() {
pub struct ReassemblyQuota {
    pub max_frames: u32,
    pub max_bytes: u64,
    pub max_fragments_per_frame: u32,
}
}

109. Eviction Policy

Prefer:

oldest expired
then oldest inactive

Never evict active high-priority frame blindly if avoidable.


110. Priority

Possible:

#![allow(unused)]
fn main() {
pub enum AnonymousFramePriority {
    Control,
    Message,
    Attachment,
    Background,
}
}

111. Priority Visibility

Priority should not become externally distinguishable if anonymity profile forbids it.


112. Internal Scheduling Only

Use priority locally.


113. Error Taxonomy

#![allow(unused)]
fn main() {
pub enum FramingError {
    UnsupportedVersion,
    InvalidHeader,
    InvalidFragmentCount,
    FragmentTooLarge,
    FrameTooLarge,
    ReassemblyLimitExceeded,
    DuplicateConflict,
    IntegrityFailure,
    Expired,
    ProviderCapacityTooSmall,
    Internal,
}
}

114. Corruption Handling

Corrupt packet:

drop
record redacted metric
do not crash

115. Reassembly Failure UX

Normally hidden.

Application may see:

anonymous delivery failed

only after retries/expiry.


116. Provider Error Separation

Framing errors are distinct from:

route error
mailbox error
provider unavailable

117. Message Delivery State

Framing stage can expose:

Preparing
Packetizing
Queued

but ordinary UX should simplify.


118. Observability

Privacy-safe metrics:

frames created
cells created
average fragments bucket
reassembly success
reassembly timeout
invalid packet count
padding overhead bucket

119. Forbidden Metrics

Do not record:

exact per-contact frame sizes
exact per-message timing
frame ID mapped to AccountId

120. Padding Overhead Metric

Aggregate only.


121. Diagnostics

Developer view may show:

cell size class
provider profile
reassembly queue depth
invalid packet count

122. Do Not Show Raw Sphinx Header

No operational need for normal diagnostics.


123. Debug Dumps

Must redact secret/path material.


124. Testkit

Add deterministic:

framer
fragmenter
reassembler
fake provider capacity
corruption injector

125. Unit Tests

Test:

single-cell frame
multi-cell frame
exact boundary
one byte over boundary
max frame
empty payload

126. Fragment Order Tests

in order
reverse
random
duplicate
missing

127. Corruption Tests

bad version
bad count
bad index
bad length
tampered payload

128. Limit Tests

max fragments
max bytes
max inflight frames

129. Replay Tests

Same cell delivered repeatedly.


130. Duplicate Conflict Test

Same:

FrameId + index

different contents.

Must reject frame.


131. Expiry Tests

before expiry
at expiry
after expiry

132. Version Tests

current
previous compatible
future unknown

133. Provider Capacity Tests

Different provider MTUs.


134. Padding Tests

Ensure:

same size class

for payloads within bucket.


135. Length-Hiding Tests

Verify exact original payload length not exposed outside encrypted length metadata.


136. Fuzzing

Fuzz:

cell parser
fragment parser
batch parser
extension parser
reassembly state machine

137. Property Tests

Properties:

reassemble(fragment(frame)) == frame
duplicate identical fragment is idempotent
fragment count bound always enforced
malformed length never allocates beyond configured limit

138. Differential Tests

Where provider library exposes reference packet encoder/decoder:

compare behavior

without reimplementing cryptography.


139. Memory Tests

Malformed traffic cannot cause unbounded growth.


140. CPU Exhaustion Tests

Large invalid fragment storms stay bounded.


141. Concurrency Tests

Multiple frames reassembling concurrently.


142. Crash Tests

Crash during:

fragment write
reassembly
temp-file flush
completion

143. Restart Recovery

Incomplete reassembly may:

resume
or discard

according to policy.


144. Recommended Initial Policy

For small message frames:

discard incomplete on restart

Sender/mailbox can retry.

For large attachment chunk frames:

resume if safely persisted

145. Security Invariants

Mandatory:

1. Frame IDs are random and transport-scoped.
2. MessageId/ConversationId/AccountId are never exposed as frame IDs.
3. Fragment count and lengths are bounded before allocation.
4. Duplicate identical fragments are idempotent.
5. Conflicting duplicates invalidate the frame.
6. SIAR E2EE remains above anonymous framing.
7. Provider/Sphinx cryptography is not reimplemented ad hoc.
8. Reply capability material is never logged.
9. Large attachments are chunked separately from cell fragmentation.
10. Unknown versions fail safely.
11. Reassembly memory/storage is bounded.
12. Padding policy cannot silently weaken Maximum Anonymity mode.

146. Crate Layout

Recommended:

crates/
├── siar-anonymity-frame/
├── siar-anonymity-cell/
├── siar-anonymity-fragment/
├── siar-anonymity-reassembly/
├── siar-anonymity-padding/
├── siar-anonymity-packet-profile/
└── siar-anonymity-framing-testkit/

147. siar-anonymity-frame

Owns:

AnonymousMessageFrame
frame version
message class
batch envelope

148. siar-anonymity-cell

Owns:

AnonymousCell
CellVersion
size classes
serialization

149. siar-anonymity-fragment

Owns:

fragment headers
fragmenter
streaming fragmentation

150. siar-anonymity-reassembly

Owns:

inflight state
dedup
expiry
quota
completion

151. siar-anonymity-padding

Owns:

padding policy
size normalization

152. siar-anonymity-packet-profile

Owns:

provider capacity
overhead calculation
cell profile

153. Dependency Direction

message/E2EE
    ↓
frame
    ↓
fragment/cell
    ↓
provider adapter

Never reverse.


154. Serialization Format

Internal provider-neutral framing may use:

postcard

if bounded and explicitly versioned.


155. RON

Only for human-readable configuration/testing fixtures.


156. JSON

Not used in packet framing.


157. Zero-Copy

Use zero-copy/borrowed parsing only when safe and beneficial.


158. Parser Safety Over Micro-Optimization

Prefer:

clear bounds
checked offsets
small copies

over unsafe parsing tricks.


159. Bytes

Suitable for immutable payload slices.


160. Secret Types

Reply capability buffers use:

zeroizing secret wrapper

where practical.


161. Packet Builder API

#![allow(unused)]
fn main() {
pub trait AnonymousPacketFramer {
    fn frame(
        &self,
        input: FramingInput,
        profile: AnonymousPacketProfile,
    ) -> Result<FramedAnonymousMessage, FramingError>;
}
}

162. Framing Input

#![allow(unused)]
fn main() {
pub struct FramingInput {
    pub class: AnonymousMessageClass,
    pub payload: Bytes,
    pub expiry: Timestamp,
    pub reply: Option<ReplyCapabilityEnvelope>,
}
}

163. Framed Output

#![allow(unused)]
fn main() {
pub struct FramedAnonymousMessage {
    pub frame_id: AnonymousFrameId,
    pub cells: Vec<AnonymousCell>,
}
}

For large payloads use streaming API.


164. Reassembler API

#![allow(unused)]
fn main() {
pub trait AnonymousReassembler {
    fn ingest(
        &mut self,
        cell: AnonymousCell,
    ) -> Result<ReassemblyOutcome, FramingError>;
}
}

165. Reassembly Outcome

#![allow(unused)]
fn main() {
pub enum ReassemblyOutcome {
    Incomplete,
    Complete(AnonymousMessageFrame),
    Duplicate,
    Rejected,
}
}

166. Garbage Collection API

#![allow(unused)]
fn main() {
pub trait ReassemblyGc {
    fn collect_expired(
        &mut self,
        now: Timestamp,
    ) -> ReassemblyGcReport;
}
}

167. Performance Budget

Track:

framing throughput
cells/sec
reassembly latency
memory per inflight frame
padding overhead

168. Small Message Fast Path

A single standard cell should handle common text/control messages where possible.


169. Fast Path Must Not Change Privacy Semantics

No special visibly smaller packet.


170. Attachment Slow Path

Streaming fragmenter.


171. Backpressure

If provider queue is full:

pause fragment production

172. No Pre-Framing Entire Huge File

Hard rule.


173. Flow Control

source stream
→ fragmenter
→ bounded cell queue
→ provider

174. Bounded Channel

Required between fragmenter and provider.


175. Cancellation

If send cancelled:

stop generating future cells

Already-submitted cells may still arrive.


176. Cancellation Semantics

Application MessageId still determines final message visibility/state.


177. Partial Attachment

Recipient must not expose incomplete attachment as complete.


178. Verification

Attachment layer performs final content integrity verification after all chunks.


179. Batch Interaction

Do not mix fragments from unrelated frames into one SIAR batch unless provider semantics explicitly support and privacy review approves.


180. Cover Traffic Interaction

Cover cells must be indistinguishable at provider-visible framing layer where provider design supports.


181. Dummy Cell

#![allow(unused)]
fn main() {
pub enum CellPayloadKind {
    Real,
    Cover,
}
}

This distinction should remain local/internal and not provider-visible if possible.


182. Cover Cell Parsing

Recipient/provider runtime can discard dummy cells according to protocol.


183. Loop Traffic

May use same cell size/profile.


184. Traffic Analysis Resistance

Framing contributes by normalizing:

size
structure
fragmentation profile

but cannot alone hide:

timing
online/offline pattern
volume

Hence Part 34 cover/delay policy remains essential.


185. Provider Compatibility

Different provider implementations may not use identical packet internals.

SIAR core should guarantee:

same application framing semantics

while adapter maps to provider packet model.


186. Nym Adapter

Responsibilities:

map SIAR cell/profile
to Nym packet API
map reply capabilities
respect Nym packet capacity
surface provider errors

187. Native Mixnet Future

Could use:

Sphinx packets directly

but still implement same SIAR framing API.


188. Migration

Switching provider must not require changing application MessageId or E2EE payload semantics.


189. Wire Compatibility

Framing version should be independent from:

provider SDK version

where possible.


190. Upgrade Strategy

support N and N-1
publish capability
roll out
retire old

191. Downgrade Attack

Never accept older framing version solely because attacker requests it.

Version compatibility is policy-controlled.


192. Capability Advertisement

Authenticated.


193. Unknown Extension

Ignore only if:

marked optional
bounded
authenticated

194. Mandatory Extension

Unknown mandatory extension:

reject frame

195. Security Events

Repeated malformed authenticated frames may generate local security event.


196. Abuse Threshold

Avoid one event per packet.

Coalesce.


197. Metrics Cardinality

Low.

No frame ID labels in telemetry.


198. Support Bundle

Include only aggregate:

framing version
cell size class
invalid packet count
reassembly failures

199. UI Exposure

Normal user should not see:

Sphinx
fragment index
cell size

except advanced documentation/diagnostics.


200. Developer UX

Part 20 can show:

Anonymous packet profile: Standard
Framing version: v1
Reassembly queue: Healthy

201. Test Fixtures

Fixture examples:

single_text_frame
multi_fragment_text
reply_capability_frame
small_attachment
expired_frame
corrupt_fragment

202. Golden Binary Fixtures

Useful for stable framing compatibility.

Store synthetic data only.


203. Compatibility Tests

Decode old supported fixtures in every release.


204. Round-Trip Tests

encode
→ fragment
→ shuffle
→ reassemble
→ decode

205. End-to-End Integration Test

SIAR E2EE message
→ framing
→ fake Sphinx provider
→ mailbox
→ reassembly
→ E2EE decrypt

206. Adversarial Test

Inject:

truncation
bit flip
duplicate
reorder
oversize
invalid version
expired packet

207. Property: Bounded Allocation

For any input <= parser max:

allocation <= configured bound

208. Property: No ID Leakage

Serialized provider-facing frame must not contain raw:

AccountId
DeviceId
ConversationId
MessageId

unless explicitly encrypted within payload.


209. Property: Padding

Payloads in same configured bucket produce same outer cell length.


210. Property: Stable Parsing

Malformed input never panics.


211. Property: Deterministic Reassembly

Same authenticated fragment set yields same frame.


212. Initial Production Scope

Implement first:

one standard cell size
versioned anonymous frame
bounded fragment header
streaming fragmentation
bounded reassembly
padding to cell boundary
random frame IDs
provider capacity calculation
Nym adapter mapping
reply capability container
replay cache
fuzz/property tests

Then add:

multiple size classes
privacy batching
advanced fixed frame classes
conversation-specific profiles
native Sphinx packet provider

213. Definition of Done

Part 36 is complete when:

  • packet/message terminology is explicit
  • SIAR E2EE remains above anonymous framing
  • normalized fixed-size cells are defined
  • frame IDs are random and unrelated to application IDs
  • fragmentation and reassembly are bounded
  • attachment chunking is separate from transport fragmentation
  • duplicate, reorder, missing, corrupt, and conflicting fragments are handled
  • padding and size-bucketing policies are defined
  • reply capability/SURB material can be embedded securely
  • provider packet capacity is centrally calculated
  • provider-specific Sphinx/Nym packet details remain behind adapters
  • parsing is bounded and overflow-safe
  • replay cache and expiry rules are defined
  • streaming APIs avoid loading huge payloads in memory
  • diagnostics/metrics avoid peer correlation
  • fuzzing, property tests, binary compatibility fixtures, and adversarial tests are defined
  • no new cryptographic construction is introduced casually

214. Final Architecture

                 APPLICATION MESSAGE
                         │
                         ▼
                   SIAR APPLICATION E2EE
                         │
                         ▼
                 Anonymous Message Frame
                         │
                         ▼
                    Fragmentation
                         │
                         ▼
                  Normalized Cells
                         │
                         ▼
              Provider Packet Adapter
                         │
                         ▼
                 Sphinx / Nym Packet
                         │
                         ▼
                      Mixnet

Receive path:

Provider Packet
      ↓
Decode Cell
      ↓
Replay/Dedup Check
      ↓
Bounded Reassembly
      ↓
Anonymous Frame
      ↓
SIAR E2EE Validation/Decrypt
      ↓
Application Message

215. Final Principle

SIAR should hide message structure before traffic enters the anonymity network.

The correct model is:

E2EE
+
random transport frame identity
+
normalized cells
+
bounded fragmentation
+
padding
+
provider-adapted Sphinx packets

not:

encrypt the original message
→ send its natural size directly

This architecture gives SIAR a stable, provider-neutral anonymous packet substrate that can work with Nym today and a future native Sphinx/Loopix-inspired mixnet later, without coupling application semantics to one provider or exposing raw message structure to the network.