FWC-1§3Normative

Container format

Twelve bytes of header, then the body. Everything a decoder needs to identify, size-check and verify the payload before touching it.

magic0–1
version2
flags3
length4–7
crc328–11
body12…
00
magic · 2 B
ASCII "FW" — 0x46 0x57
02
version · 1 B
Format version, currently 1
03
flags · 1 B
Bit field, see below
04
length · 4 B
Body length in bytes, uint32 BE
08
crc32 · 4 B
CRC-32 of body, uint32 BE
12
body · n B
length bytes of body
FWC-1 container layout · 12 fixed bytes

Header fields

magic (offset 0, 2 bytes)

MUST be the two ASCII bytes F (0x46) and W (0x57). Decoders MUST treat any other value as "not a container". The magic is deliberately short: the header competes for space with the payload in a 16×16 icon (756 usable bytes), and a two-byte magic combined with the version check gives a false-positive rate on random pixel data of roughly one in sixteen million.

version (offset 2, 1 byte)

MUST be 0x01 for this specification. A decoder that encounters an unknown version MUST NOT attempt to interpret the remaining fields and SHOULD report unsupported. See §12 for how future versions are introduced.

flags (offset 3, 1 byte)

bit 7
bit 6
bit 5
bit 4
bit 3
bit 2
bit 1CMP
bit 0ENC
bit 0 · 0x01
encrypted (§6)
bit 1 · 0x02
compressed (§5)
bits 2–7
reserved, MUST be written 0, MUST be ignored by decoders

Bits 2–7 are reserved and MUST be written as zero. Decoders MUST ignore reserved bits they do not understand rather than rejecting the container; this is what allows backward-compatible additions within version 1.

length (offset 4, 4 bytes)

Unsigned 32-bit big-endian count of body bytes. A decoder MUST check length ≤ capacity(size, mode) before reading the body; a length that exceeds what the image can physically hold is a definitive sign the header is not genuine (random pixels that happened to spell "FW\x01") and MUST be reported as not-a-container, not as truncation.

crc32 (offset 8, 4 bytes)

CRC-32 as defined in ISO 3309 / ITU-T V.42 and used by PNG and zlib: reflected polynomial 0xEDB88320, initial value 0xFFFFFFFF, final XOR 0xFFFFFFFF. It is computed over the body only — never the header — so that the header can be validated independently and so that the same body has the same CRC regardless of flag changes in future versions.

const TABLE = new Uint32Array(256)
for (let n = 0; n < 256; n++) {
  let c = n
  for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1
  TABLE[n] = c >>> 0
}

export function crc32(bytes: Uint8Array): number {
  let crc = 0xffffffff
  for (const b of bytes) crc = TABLE[(crc ^ b) & 0xff] ^ (crc >>> 8)
  return (crc ^ 0xffffffff) >>> 0
}
Reference CRC-32 (table-driven)

Body

The body is the payload transformed by the pipeline payload → [deflate-raw] → [encrypt] → body, applying each step only when its flag is set. Order matters: compression MUST precede encryption, because ciphertext is incompressible and a compressor run after encryption would waste bytes and leak nothing useful.

Worked example

The 11-byte payload <h1>Hi</h1> does not shrink under deflate, so the encoder stores it raw with flags = 0. The complete container is 23 bytes:

46 57                  magic   "FW"
01                     version 1
00                     flags   (none)
00 00 00 0b            length  11
a7 ad b6 8f            crc32   0xA7ADB68F
3c 68 31 3e 48 69 3c 2f 68 31 3e    body "<h1>Hi</h1>"

Header inspector

Type anything and watch the 12-byte header, the first 32 body bytes, and the resulting 16×16 noise icon update live.

header
magicversionflagslengthcrc32
body