Cloud Parts

Kaitai Struct

A YAML language for describing binary formats that already exist, compiled into ready-to-use parsers for a dozen programming languages.

What it is
File formats (format descriptions)
Type
Open source
Made by
Kaitai Project (community, funded in part by the NLnet Foundation)
License
GPLv3+ (compiler, Web IDE and console visualizer); runtime libraries MIT, except JavaScript (Apache 2.0); each spec in the format gallery carries its own licence
Facts checked
6 October 2026

What it is

You describe a binary format in a .ksy file, which is YAML. meta names the format and its byte order. seq lists the fields in the order they sit in the bytes, each with a type such as u4 (a 4-byte unsigned integer) or str, and a size that can be an expression over earlier fields. types defines nested structures, switch-on picks a type from a tag field, repeat reads arrays until the end of the stream, a count or a condition, and instances describe fields at computed offsets that are parsed lazily, only when something asks for them. process can unpack zlib or undo XOR before parsing. The compiler, kaitai-struct-compiler (ksc), is written in Scala and runs on Java. It turns the spec into source code for C++, C#, Go, Java, JavaScript, Lua, Nim, Perl, PHP, Python, Ruby or Rust, plus Graphviz diagrams. The generated class reads fields with calls such as _io.readU2be() into a small runtime library for that language, and gives you typed accessors: Gif.from_file("a.gif").logical_screen.image_width.

That is the opposite job to Protocol Buffers or Cap’n Proto. Those start from data in your program and define their own wire format; Kaitai Struct starts from bytes someone else designed (a GIF, a ZIP archive, an ELF binary, a game save, a device protocol) and describes them as they are. So it shows up in reverse engineering, forensics and digital preservation, and inside tools such as mitmproxy, Kismet and OWASP ZAP. The format gallery holds 189 ready specs, from PNG and SQLite to pcap and ext2. The Web IDE at ide.kaitai.io runs the compiler’s JavaScript build in your browser and shows a hex dump next to the parsed tree; your files stay in browser storage. Everything is free: there is no company or paid edition, and the compiler is GPLv3+ while the runtimes your product ships with are MIT or Apache 2.0.

Why teams pick it

  • Describe a format once and get parsers for 12 languages, with generated code that reads like hand-written code
  • 189 ready specs in the format gallery (archives, executables, file systems, images, network packets, databases), each with docs, a diagram and generated parsers
  • The browser-based Web IDE links each parsed field to its bytes in a hex view, and since 2024 shows the partial tree up to the point where parsing failed
  • Handles messy real-world formats: offsets computed from earlier fields, lazy instances, tagged unions with switch-on, bit-sized integers, imports between specs and zlib or XOR preprocessing
  • Runtime libraries are small and permissively licensed (MIT, Apache 2.0 for JavaScript), so a generated parser can ship inside closed-source software

Watch out for

  • Mostly read-only: writing (serialization) arrived only in 0.11, in September 2025, and only for Java and Python; every other language can parse but not write
  • Still at version 0.x after ten years, with years between releases (0.10 in 2022, 0.11 in 2025) and breaking changes in 0.11 for Java and JavaScript users
  • Language support is uneven: Go and Nim are marked entry-level, Rust first shipped in 0.11, and Swift is still a work in progress
  • It builds the whole object tree, then you use it; the FAQ points to event-based parsers for streams you need to act on chunk by chunk
  • The command-line compiler needs Java (8 or later, 21 recommended); the npm build has only a programmatic API

How it fits with other technologies

png_chunks.ksy: the PNG signature, its chunks and the IHDR header · yaml

meta:
  id: png_chunks
  file-extension: png
  endian: be                      # PNG stores integers big-endian
seq:
  - id: magic
    contents: [0x89, "PNG", 0x0d, 0x0a, 0x1a, 0x0a]   # parsing fails if these bytes differ
  - id: chunks
    type: chunk
    repeat: until
    repeat-until: _.type == "IEND" or _io.eof
types:
  chunk:
    seq:
      - id: len
        type: u4
      - id: type
        type: str
        size: 4
        encoding: ASCII
      - id: body
        size: len                   # the size comes from the field just read
        type:
          switch-on: type
          cases:
            '"IHDR"': ihdr          # other chunk types stay raw bytes
      - id: crc
        size: 4
  ihdr:
    seq:
      - id: width
        type: u4
      - id: height
        type: u4
      - id: bit_depth
        type: u1
      - id: color_type
        type: u1
        enum: color_type
      - id: compression_method
        type: u1
      - id: filter_method
        type: u1
      - id: interlace_method
        type: u1
enums:
  color_type:
    0: greyscale
    2: truecolor
    3: indexed
    4: greyscale_alpha
    6: truecolor_alpha

Compile the spec and parse a file#

The same .ksy compiles to any supported language. Here it becomes a Python module; the runtime library is a separate small package.

bash
brew install kaitai-struct-compiler        # or the .deb, .msi or universal .zip; needs Java
kaitai-struct-compiler -t python png_chunks.ksy   # writes png_chunks.py
kaitai-struct-compiler -t graphviz png_chunks.ksy # writes a .dot diagram of the format
pip install kaitaistruct                   # the Python runtime (MIT)

python3 - <<'EOF'
from png_chunks import PngChunks
png = PngChunks.from_file("image.png")
ihdr = png.chunks[0].body
print(ihdr.width, ihdr.height, ihdr.color_type.name)  # 3 2 truecolor_alpha
print([c.type for c in png.chunks])                   # ['IHDR', 'IDAT', 'IEND']
EOF

Kaitai Struct, Protocol Buffers or Cap’n Proto?#

All three compile a schema into code, but they solve opposite problems. Kaitai Struct’s FAQ puts it as “have binary protocol, need data in memory” versus “have data in memory, need binary protocol”.

Kaitai StructProtocol BuffersCap’n Proto
Starting pointBytes in a format someone else designedMessages you define in a .proto fileStructs you define in a .capnp file
Who decides the bytesThe format’s author; the spec describes them as they areProtobuf’s own encoding: field numbers and varintsCap’n Proto’s own layout: 8-byte words and pointers
WritingJava and Python only, since 0.11Every supported languageEvery supported language
Schema evolutionNone built in: you describe each version, e.g. with switch-onRules for adding and reserving field numbersRules for adding fields at the end of a struct
Typical useReverse engineering, forensics, reading legacy and media filesRPC payloads, events, telemetryZero-copy messages and RPC

See it in a real stack

No architecture uses Kaitai Struct yet. Drop it into the playground and wire it up yourself.Open playground →
Where it came from2016–2025

Anyone who reads binary files they didn’t design ends up writing the same code again and again: read four bytes, check the magic number, branch on a type field, jump to an offset. Advanced hex editors such as 010 Editor have template languages for describing such structures, but a template only drives that one editor. The code still has to be rewritten for every program and language that needs it.

Mikhail Yakshin made the first commit to the Kaitai Struct compiler in February 2016, a “semi-working first POC” written in Scala. Releases came quickly that year: C# in 0.4, then PHP, Perl and Graphviz diagrams in 0.5, along with a debug mode that made the Web IDE possible. Lua and Go followed in 0.8 in 2018, and the project began working with Construct, the Python library its FAQ calls its closest analogue.

After that, releases slowed to one every two or three years, with Yakshin and Petr Pučil the most active committers. NLnet Foundation money paid for the 2024 Web IDE improvements and much of 0.11 in September 2025, which finally added writing, for Java and Python, and a Rust target.

Why it’s built this way

Compile specs into source code instead of interpreting them
Unlike per-editor templates, a compiled .ksy becomes ordinary code in your own project, readable and about as fast as a parser written by hand.
Write the compiler in Scala
Scala compiles for the JVM and for JavaScript, so the same compiler runs as a desktop tool and inside the browser-based Web IDE.
Parse into an object tree, with lazy instances
Most programs want to parse a file and then use it through an API. Lazy instances let a spec point at data elsewhere in the file without reading it until it is needed.
Reading first, writing much later
Writing a format back out is a far bigger change than reading it, so the project shipped parsing for years and added serialization for two languages only in 0.11.

Timeline

  1. 2016

    First compiler commit in February; 0.4 adds C#; 0.5 adds PHP, Perl, Graphviz

  2. 2017

    0.7 lets .ksy files import types from each other

  3. 2018

    0.8 adds Lua and early Go; collaboration with Construct begins

  4. 2020

    0.9 adds a C++11 target and Nim

  5. 2024

    NLnet-funded Web IDE shows partial results when parsing fails

  6. 2025

    0.11 adds writing for Java and Python, and a Rust target

Sources21 · facts checked 6 October 2026

Improve this page on GitHub