2.2 Event Format
An event is an indivisible record: a packed binary header followed immediately by a payload. Header and payload are always stored, delivered, and consumed as one contiguous byte sequence, and neither is meaningful alone.
2.2.1 Header layout #
The header fields are laid out sequentially with no padding and no alignment gaps. All multi-byte integers are little-endian.
| Offset | Size | Type | Field | Description |
|---|---|---|---|---|
| 0 | 4 | u32 | event_size | Total size of the event, header plus payload, in bytes. |
| 4 | 4 | u32 | header_size | Size of the header in bytes. |
| 8 | 8 | u64 | timestamp | Wall clock time at emission, in nanoseconds since the Unix epoch. |
| 16 | 8 | u64 | sequence | Per-CPU, per-boot monotonic sequence number. |
| 24 | 2 | u16 | cpu_id | The CPU on which the event was emitted, identifying the ring buffer that carries it. |
| 26 | 1 | u8 | origin_class | The emission path that produced the event. |
| 27 | 16 | GUID | effective_token_guid | GUID of the effective token of the emitting thread. Null GUID if unavailable. |
| 43 | 16 | GUID | true_token_guid | GUID of the emitting process's primary token. Null GUID if unavailable. |
| 59 | 16 | GUID | process_guid | GUID of the emitting process. Null GUID if unavailable. |
| 75 | 2 | u16 | type_len | Length of the event type string in bytes. |
| 77 | type_len | [u8] | type | Event type string, UTF-8, not null-terminated. |
GUIDs use the binary format defined in PCDS and are opaque 16 bytes to this contract. The null GUID is sixteen zero bytes and means the field is not applicable or was not available.
header_size is 77 + type_len in this version of the format. A
consumer MUST use header_size to locate the payload and MUST NOT
compute the payload offset from 77 + type_len or from any other
constant, so that a future header extension does not break it. All
fields before type are at fixed offsets and will remain so.
The payload occupies the bytes from header_size to event_size. The
next event in a ring buffer begins event_size bytes after the start
of the current one, with no alignment padding between events.
2.2.2 Payload #
The payload is exactly one MessagePack value, and its structure is defined by the emitter. KMES does not interpret it.
A consumer MUST NOT assume a payload is present: an event emitted by a
kernel subsystem MAY have event_size == header_size, meaning a
header and no payload at all. Events emitted through the system calls
always carry a payload, because an empty byte sequence is not a valid
MessagePack value and is rejected at the syscall boundary.
Note that MessagePack encodes its own length prefixes big-endian, whereas every integer in the event header is little-endian. Both appear in one event.
2.2.3 Event types #
The event type is an arbitrary UTF-8 string. KMES imposes no structure, namespace, or naming convention on it, and applies no case folding or normalisation. Consumers MUST compare event types as raw byte sequences.
2.2.4 Origin class #
| Value | Origin |
|---|---|
| 0 | Userspace, via system call |
| 1 | KMES |
| 2 | KACS |
| 3 | LCS |
Values 4–255 are unassigned. A consumer MUST tolerate an unrecognised origin class rather than rejecting the event, so that a kernel subsystem added later does not break it.
Events with origin class 0 were emitted through the system call interface, and their origin class is set by the kernel, not by the caller. A userspace emitter cannot claim to be a kernel subsystem.
2.2.5 Identity fields #
The three identity GUIDs are captured by the kernel at the moment the event is written to the ring buffer, not when the emitting call began.
effective_token_guidis the token governing the emitting thread's access rights. If the thread was impersonating, this is the impersonation token; otherwise it equalstrue_token_guid.true_token_guidis the emitting process's primary token, regardless of impersonation.process_guididentifies the emitting process. It is assigned when the process is created and does not change acrossexec.
Any of the three MAY be the null GUID, meaning the kernel had no identity to record — emission before the access control subsystem initialised, or from a context with no associated process such as a kernel worker thread. A consumer MUST treat a null identity as "unattributed" and MUST NOT treat it as a valid GUID value that could match a real token or process.
2.2.6 Ordering #
Events from different CPUs are ordered by timestamp. Events with
identical timestamps from different CPUs were genuinely concurrent and
have no defined relative order.
Within a single CPU, sequence is the ordering primitive. It is
monotonic across wall clock discontinuities, which timestamp is not:
a clock adjustment can move timestamps backwards, and a consumer that
requires monotonic ordering within a CPU MUST use sequence. Events
with identical timestamps on the same CPU are ordered by sequence.
Each CPU numbers independently and there is no global sequence. The
counter starts at zero when the kernel module loads and is incremented
before a value is taken, so the first event on a CPU carries sequence
number 1 and sequence 0 is never assigned. The pair
(cpu_id, sequence) uniquely identifies an event within one boot.
A gap in the sequence for a given CPU means events were lost — either overwritten before the consumer read them, or dropped by the kernel before they reached the buffer. Sequence numbers are continuous across a buffer replacement, so a generation change does not itself produce a gap.