Biome SEGB File Format: v1 and v2 Byte-Level Reference
Byte-level reference for Biome SEGB v1 and v2: headers, trailers, record states, CRC32, alignment, file-name times and protobuf field numbers per stream.
TL;DR. A Biome stream is a folder of SEGB files. v1 has a 56-byte header with SEGB at bytes 52–55 and the end-of-data offset in bytes 0–3; each record has a 32-byte header (length, state, two timestamps, CRC32, unknown) and is padded to 8 bytes. v2 starts with SEGB, an entry count and a creation time, and indexes its records through a trailer of 16-byte entries (end offset, state, time) at the end of the file; each record is a CRC32, an unknown int32 and the payload, aligned to 4 bytes. States are 1 written, 3 deleted, 4 empty. Payloads are protocol buffers without a published schema. None of this is documented by Apple.
This reference follows ccl-segb by CCL Forensics (MIT licence), which credits Cellebrite's research on the v2 layout, and it is what KnowledgeC Parser implements. For where these files live and why they matter, start with the knowledgeC.db forensics guide and knowledgeC vs Biome.
Where SEGB files sit
~/Library/Biome/streams/restricted/<Stream>/local/<file>
~/Library/Biome/streams/public/<Stream>/local/<file>
~/Library/Biome/streams/restricted/<Stream>/remote/<device UUID>/<file>
~/Library/Biome/streams/restricted/<Stream>/local/tombstone/<file>
/private/var/db/biome/streams/...
The stream name (App.InFocus, App.WebUsage, ...) is not stored in the file: it comes from the folder. Keep the folder layout when you collect, or you lose the stream name, the local / remote distinction and the device UUID. Files in tombstone/ are expired (Biome tombstone).
All integers below are little-endian. Times are Mac absolute time stored as 64-bit doubles: seconds since 2001-01-01 00:00:00 UTC. Add 978307200 for Unix time.
SEGB v1
File header (56 bytes)
| Offset | Size | Type | Field |
|---|---|---|---|
0x00 | 4 | uint32 | End-of-data offset (absolute, from the start of the file) |
0x04 | 48 | Not documented | |
0x34 | 4 | ASCII | Magic SEGB (bytes 52–55) |
Records start at byte 56 (0x38) and run until the end-of-data offset. If that offset points past the end of the file, the copy is truncated.
Record header (32 bytes)
| Offset in record | Size | Type | Field |
|---|---|---|---|
0x00 | 4 | int32 | Payload length |
0x04 | 4 | int32 | State |
0x08 | 8 | double | timestamp1 (Mac absolute) |
0x10 | 8 | double | timestamp2 (Mac absolute) |
0x18 | 4 | uint32 | CRC32 of the payload |
0x1C | 4 | int32 | Unknown |
0x20 | length | bytes | Payload |
After the payload, the next record starts at the next multiple of 8 from the start of the file. The names timestamp1 and timestamp2 are descriptive: public sources do not give them a fixed meaning for every stream, so report both raw values rather than labelling one "start" and one "end".
SEGB v2
File header (32 bytes)
| Offset | Size | Type | Field |
|---|---|---|---|
0x00 | 4 | ASCII | Magic SEGB |
0x04 | 4 | int32 | Entry count (number of trailer entries) |
0x08 | 8 | double | Creation time (Mac absolute) |
0x10 | 16 | Not documented |
Trailer (16 bytes per entry, at the end of the file)
The trailer occupies the last count × 16 bytes of the file.
| Offset in entry | Size | Type | Field |
|---|---|---|---|
0x00 | 4 | int32 | End offset of the record, relative to byte 32 |
0x04 | 4 | int32 | State |
0x08 | 8 | double | Time (Mac absolute) |
Records
Records start at byte 32. Each one is:
| Offset in record | Size | Type | Field |
|---|---|---|---|
0x00 | 4 | uint32 | CRC32 of the payload |
0x04 | 4 | int32 | Unknown |
0x08 | variable | bytes | Payload, up to the end offset |
A v2 record has no length field. To read it, sort the trailer entries by end offset, start at byte 32, and read up to 32 + end offset. The next record starts at the next 4-byte boundary. Because state and time live in the trailer, a v2 record header carries only the CRC and the unknown word.
Shared end offsets
Two trailer entries can carry the same end offset. The usual pattern is a record written (state 1) and later deleted (state 3): both entries point at the same bytes, each with its own time. A parser should report both, not treat the second one as a new record. KnowledgeC Parser shows the deleted entry with the same payload and flags it as deleted. It also ignores trailer entries with state 0, which reference no data.
Record states and CRC
| State | Meaning | What to do |
|---|---|---|
| 1 | Written | Normal record |
| 3 | Deleted | The payload can still be present: decode it and report it as deleted |
| 4 | Empty / unused | No usable record |
The CRC is standard CRC-32 as computed by zlib (zlib.crc32), over the payload only. A mismatch points to a partially written or damaged record, or a copy taken while the file was being written. Keep such records but mark them; do not silently drop them.
An annotated v2 example
The file below was constructed for this article with the layout above: one App.InFocus record saying Terminal came into focus. It is synthetic, and the undocumented header bytes are zero here.
00000000 53 45 47 42 01 00 00 00 00 00 00 a6 c0 2b c8 41 SEGB.........+.A
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000020 67 88 af ff 00 00 00 00 18 01 21 00 00 00 80 ff g.........!.....
00000030 2b c8 41 32 12 63 6f 6d 2e 61 70 70 6c 65 2e 54 +.A2.com.apple.T
00000040 65 72 6d 69 6e 61 6c 00 27 00 00 00 01 00 00 00 erminal.'.......
00000050 66 66 66 80 ff 2b c8 41 fff..+.A
| Bytes | Value | Meaning |
|---|---|---|
0x00–0x03 | SEGB | v2 magic |
0x04–0x07 | 01 00 00 00 | 1 trailer entry |
0x08–0x0F | double 811041100.0 | Created 2026-09-14 01:11:40 UTC |
0x10–0x1F | zeros | Undocumented |
0x20–0x23 | 0xffaf8867 | CRC32 of the payload |
0x24–0x27 | 0 | Unknown |
0x28–0x46 | 31 bytes | Protobuf payload (below) |
0x47 | 00 | Padding to a 4-byte boundary |
0x48–0x4B | 27 00 00 00 | End offset 39: 32 + 39 = 0x47, the end of the payload |
0x4C–0x4F | 01 00 00 00 | State 1, written |
0x50–0x57 | double 811073280.8 | 2026-09-14 10:08:00.8 UTC |
The payload decodes as:
| Bytes | Tag | Field | Value |
|---|---|---|---|
18 01 | field 3, varint | status | 1 = in focus |
21 + 8 bytes | field 4, 64-bit | time | 811073280.0 = 10:08:00 UTC |
32 12 + 18 bytes | field 6, length-delimited | bundle ID | com.apple.Terminal |
A stored file of this kind would be named after its creation time in microseconds, here 811041100000000.
File-name times
SEGB file names are integers. mac_apt reads them as Cocoa time in microseconds, which gives an approximate creation time for the file:
date -u -r $(( 811041100000000 / 1000000 + 978307200 ))
# Mon Sep 14 01:11:40 UTC 2026
Treat it as a file-level hint. Event times come from the records and, for some streams, from the payload.
Protobuf payloads
Wire types in brief
A protobuf message is a sequence of fields. Each starts with a varint key: field number << 3 | wire type (encoding guide).
| Wire type | Encoding | Typical content |
|---|---|---|
| 0 | Varint | Integers, booleans, enums |
| 1 | 8 bytes | double, fixed64 |
| 2 | Varint length + bytes | Strings, bytes, nested messages |
| 5 | 4 bytes | float, fixed32 |
Wire types 3 and 4 (groups) are deprecated and not expected here.
Why schema-less decoding is ambiguous
Apple publishes no .proto files for Biome. Without a schema:
- a length-delimited field can be a string, raw bytes or a nested message, and short strings such as bundle IDs sometimes parse as valid messages;
- an 8-byte field can be a double or an integer;
- a varint can be a signed, unsigned, zigzag-encoded or boolean value.
KnowledgeC Parser tries printable UTF-8 text first, then a nested message that consumes every byte, then raw bytes. It shows 8-byte values as doubles, with a date when the value is a plausible Mac absolute time. Some payloads embed binary property lists, which are shown as such. Field numbers are always kept, so you can check any interpretation against another tool.
Field numbers by stream
These numbers come from community reverse engineering in mac_apt's BIOME plugin and in iLEAPP. They are not Apple documentation; validate them against test data from the same OS build before relying on them.
| Stream | Field | Meaning | Source |
|---|---|---|---|
App.InFocus | 3 | Status: 1 in focus, 0 out of focus | mac_apt |
App.InFocus | 4 | Time (double) | iLEAPP |
App.InFocus | 6 | Bundle ID | mac_apt |
App.InFocus | 9, 10 | Version strings | mac_apt |
App.WebUsage | 2 | Time | iLEAPP |
App.WebUsage | 4 / 5 / 6 | URL / domain / bundle ID | mac_apt |
Safari.* | 1 | Domain | mac_apt |
ScreenTime.AppUsage | 1 / 3 | Status / bundle ID | mac_apt |
Notification.Usage | 4 / 8 / 9 | App / title / subtitle | mac_apt |
Device.Wireless.WiFi | 1 / 2 | SSID / status | mac_apt |
Device.Wireless.Bluetooth | 1 / 2 / 3 / 4 | Address / name / product ID / status | mac_apt |
SystemSettings.SearchTerms | 1 | Search term | mac_apt |
App.InFocus records are events, not intervals: an "in focus" record is followed later by an "out of focus" record for the same app. KnowledgeC Parser pairs them into intervals (same user, same device, same bundle ID, within 24 hours) and leaves unpaired records as single events.
What is unknown
- The 48 undocumented bytes of the v1 header, the 16 of the v2 header, and the "unknown" int32 in every record.
- A fixed meaning for v1
timestamp1andtimestamp2across all streams. - Fields not listed in the table above, and every field of streams without a public decoder.
App.MenuItem, documented by Unit 42 on macOS Tahoe 26, has no public field map: KnowledgeC Parser decodes it schema-less and shows raw field numbers. - The macOS release where v2 replaced v1.
When a report depends on one of these, say so and show the raw value.
Tools that read SEGB
| Tool | What it does |
|---|---|
| ccl-segb | Reference reader for single v1 and v2 files, raw records |
mac_apt BIOME plugin | Reads streams from an image or a folder and decodes the streams above |
| iLEAPP | iOS Biome streams |
| KnowledgeC Parser | v1 and v2 in the browser, with states, CRC checks, remote and tombstone origin, and a merged timeline with knowledgeC.db |
FAQ
Is the SEGB format documented by Apple?
No. The layouts of SEGB v1 and v2 come from community reverse engineering, implemented in ccl-segb (CCL Forensics), which credits research by Cellebrite. Payload field meanings come from mac_apt and iLEAPP. Anything those sources do not cover should be treated as unknown.
How do I tell SEGB v1 from v2?
Look for the SEGB magic. In v1 it sits at bytes 52 to 55, at the end of a 56-byte header. In v2 it is the first four bytes of the file, at the start of a 32-byte header.
Does a deleted SEGB record still contain data?
Often, yes. State 3 marks a record as deleted, but the payload can remain in the file. In v2 a deleted entry can share its end offset with a written one, so both point at the same bytes. Report such records as deleted, not as live activity.
Which macOS version switched from SEGB v1 to v2?
That boundary is not publicly documented for macOS. On iOS, v1 is reported for iOS 14 to 16 and v2 from iOS 17. Parsers detect the version from the header, so you do not need to know it in advance.