.smoe Binary Format
The .smoe file is our sovereign artifact—a self-describing, rigorously page-aligned binary vault. It is the sole output of the Sculptor (shatter_moe.py) and the sole input of the Streamer (streamer.cpp). This document is its uncompromising blueprint.
Immutable Encoding
All integer fields are little-endian. All byte offsets are measured from the start of the file. All expert blobs are perfectly aligned to the Apple Silicon 16 KB hardware page boundary (16,384 bytes).
File Architecture
Offset 0x0000
┌──────────────────────────────────────────────────┐
│ FILE HEADER (64 bytes) │
├──────────────────────────────────────────────────┤
│ EXPERT TABLE (N × 48 bytes) │ ← immediately after header
├──────────────────────────────────────────────────┤
│ TENSOR DESCRIPTORS (N × 3 × 44 bytes)│ ← immediately after table
├──────────────────────────────────────────────────┤
│ ARCH BLOCK "SARC" (128 bytes) │ ← reserved_ext (64 B-aligned)
├──────────────────────────────────────────────────┤
│ [zero padding to 16 KB boundary] │
├──────────────────────────────────────────────────┤
│ EXPERT BLOB 0 (padded to 16 KB) │ ← data_offset (16 KB-aligned)
├──────────────────────────────────────────────────┤
│ EXPERT BLOB 1 (padded to 16 KB) │
├──────────────────────────────────────────────────┤
│ ... │
└──────────────────────────────────────────────────┘File Header — 64 bytes
Anchored at absolute offset 0x0000.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
| 0 | 8 | uint8[8] | magic | 53 4D 4F 45 DE EA 00 01 — "SMOE" + model marker |
| 8 | 4 | uint32 | version | Format version — currently 2; readers accept 1–2 |
| 12 | 4 | uint32 | num_moe_layers | Number of transformer layers harboring MoE experts |
| 16 | 4 | uint32 | max_experts_per_layer | Maximum expert count across all MoE layers |
| 20 | 4 | uint32 | total_experts | Total entries in the expert table |
| 24 | 8 | uint64 | table_offset | Byte offset of the first ExpertEntry (always 64) |
| 32 | 8 | uint64 | data_offset | Byte offset of the first expert blob (16 KB-aligned) |
| 40 | 4 | uint32 | group_size | SMOE quantisation group size (always 64) |
| 44 | 4 | uint32 | bits | Quantisation bit depth (2 for SMOE-Q2, 4 for SMOE-Q4) |
| 48 | 4 | uint32 | d_model | Hidden dimension (dynamic, written by Sculptor) |
| 52 | 4 | uint32 | vocab_size | Vocabulary size (dynamic) |
| 56 | 4 | uint32 | ffn_dim | Dense/shared FFN intermediate dimension (dynamic) |
| 60 | 4 | uint32 | reserved_ext | v2: byte offset of the arch block. v1: zero |
The d_model, vocab_size, and ffn_dim empower the C++ runtime to dynamically construct itself from the vault header, fully independent of the Scout .safetensors file.
Version acceptance
Readers accept versions 1 through 2. A v1 vault has no arch block — the engine falls back to a legacy inference path with Qwen3-family defaults. A v2 vault must carry a valid arch block at reserved_ext; the engine validates it at load (magic, size, SiLU activation, moe_top_k ≤ 16, head_dim ≤ 256, head divisibility) and refuses to start on failure. Pre-v2 vaults upgrade in place with scripts/upgrade_vault_v2.py — the block is written into the existing padding gap, so no expert blob moves.
Arch Block "SARC" — 128 bytes
The vault's birth certificate: every model-specific mathematical constant the engine needs, serialised from the checkpoint's HF config.json at shatter time. Located via header.reserved_ext, 64-byte aligned, inside the padding gap before data_offset. Mirrored as SmoeArchBlock in src/common.hpp and ARCH_FMT in shatter_moe.py.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
| 0 | 4 | uint8[4] | magic | 53 41 52 43 — "SARC" |
| 4 | 4 | uint32 | block_version | Currently 1 |
| 8 | 4 | uint32 | block_size | Always 128 |
| 12 | 4 | uint32 | flags | bit 0 norm_topk_prob · bit 1 has_qk_norm · bit 2 has_dense_layer_0 |
| 16 | 4 | float32 | rope_theta | RoPE base frequency |
| 20 | 4 | uint32 | num_heads | Attention query heads |
| 24 | 4 | uint32 | num_kv_heads | GQA key/value heads |
| 28 | 4 | uint32 | head_dim | Attention head dimension |
| 32 | 4 | uint32 | moe_top_k | Routed experts per token |
| 36 | 4 | uint32 | moe_ffn_dim | Expert intermediate dimension |
| 40 | 4 | uint32 | dense_ffn_dim | Dense-layer intermediate dimension |
| 44 | 4 | uint32 | shared_expert_ffn_dim | 0 = no shared expert |
| 48 | 4 | uint32 | activation | 0 = SiLU/SwiGLU (the only value the kernels implement) |
| 52 | 4 | uint32 | num_hidden_layers | Total transformer layers |
| 56 | 4 | float32 | rms_norm_eps | RMS-norm epsilon (kernels compile 1e-6; a differing value warns) |
| 60 | 24 | char[24] | model_type | HF model_type, NUL-padded (e.g. qwen3_moe) |
| 84 | 44 | uint8[44] | reserved | Must be zero |
The engine trusts the block for math constants (rope_theta, moe_top_k, head geometry, renormalisation) but keeps the Scout's tensor shapes as the authority for structural facts — has_qk_norm, has_dense_layer_0, and the shared-expert dimension are cross-checked against the tensors actually present, with a warning on any disagreement. Shapes don't lie.
Expert Table Entry — 48 bytes each
N entries flow immediately after the file header, where N = total_experts.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
| 0 | 4 | uint32 | layer_id | Transformer layer index |
| 4 | 4 | uint32 | expert_id | Expert index within the layer |
| 8 | 8 | uint64 | byte_offset | Absolute file offset of this expert's blob |
| 16 | 8 | uint64 | raw_size | Actual data bytes before padding |
| 24 | 8 | uint64 | padded_size | Data bytes rounded perfectly to 16 KB |
| 32 | 4 | uint32 | group_size | Group size used (= header.group_size) |
| 36 | 8 | uint64 | num_groups | Total quantisation groups across all three sub-tensors |
| 44 | 4 | uint8[4] | reserved | Must be zero |
Flash Lookup
To pinpoint an expert by (layer_id, expert_id) at runtime, we iterate the expert table once at startup to forge a map<(layer, expert), byte_offset>. The table is sorted by layer ascending, expert ascending.
Tensor Descriptor — 44 bytes each
Three descriptors follow each expert entry in table order (gate → up → down). Total: N × 3 descriptors packed contiguously after the expert table.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
| 0 | 1 | uint8 | tensor_type | 0=gate_proj, 1=up_proj, 2=down_proj |
| 1 | 1 | uint8 | ndim | Number of dimensions (always 2) |
| 2 | 2 | uint8[2] | reserved_0 | Must be zero |
| 4 | 4 | uint32 | rows | Weight matrix rows (original shape[0]) |
| 8 | 4 | uint32 | cols | Weight matrix cols (original shape[1]) |
| 12 | 8 | uint64 | packed_offset | Byte offset of packed weights within the expert blob |
| 20 | 8 | uint64 | packed_size | Byte size of packed weights |
| 28 | 8 | uint64 | scales_offset | Byte offset of float16 scale array within the expert blob |
| 36 | 8 | uint64 | scales_size | Byte size of float16 scale array |
Expert Blob Architecture
Each expert blob is an opaque, densely packed byte buffer starting at ExpertEntry.byte_offset. Three sub-tensors are woven together continuously:
[gate_proj packed bytes ] ← ceil(rows×cols × bits / 8) bytes
[gate_proj scale bytes ] ← float16, ceil(rows×cols / group_size) × 2 bytes
[up_proj packed bytes ]
[up_proj scale bytes ]
[down_proj packed bytes ]
[down_proj scale bytes ]
[zero padding ] ← pad to padded_size (16 KB boundary)We always use TensorDescriptor.packed_offset and scales_offset—never assuming a fixed stride.
C++ Struct Mirror
Every struct above is rigidly defined in src/common.hpp and protected by static_assert:
static_assert(sizeof(SmoeHeader) == 64);
static_assert(sizeof(ExpertEntry) == 48);
static_assert(sizeof(SmoeArchBlock) == 128);The C++ struct and this specification are the undeniable ground truth. If they diverge, the struct wins.