Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

2. The Pulse

Reference · normative · the 128-byte atom every conformant transport carries or references

The Pulse is the core primitive. Every message exchanged through a conformant transport is a Pulse or references one. The reference type is pulse_canon::Pulse; the vector pulse-layout-known-good under pulse/pulse-spec/test-vectors is its executable form.

2.1 Wire layout

A Pulse MUST be exactly 128 bytes. The layout is C-compatible field order with no padding beyond the declared fields, and the type is aligned to a 64-byte boundary in memory (#[repr(C, align(64))] in the reference). All multi-byte integers are little-endian on the wire. The reference keeps a Pulse in memory as exactly these bytes, so it builds only for little-endian targets.

OffsetSizeFieldTypeSemantics
08seq_locku64, atomicThe SeqLock word of the slot the Pulse sits in: odd while a writer is in progress, even when stable. Owned by the substrate (chapter 4); a Pulse on the wire carries whatever value the sender’s slot had, and receivers MUST ignore it.
816origin_id[u8; 16]The producing node’s identifier: a truncated hash of the node’s name or key. A hub writes its own instance identifier, sixteen random bytes drawn when it was created or opened, into an origin_id left unset on the Pulses it publishes, and uses it to skip the envelope signals it wrote itself.
248monotonic_nanosu64A monotonic clock reading at creation, set by the producer, in nanoseconds. A hub stamps the signals it writes and the envelopes it routes with the host’s monotonic clock, the clock this field is defined on: CLOCK_MONOTONIC on Linux and CLOCK_UPTIME_RAW on macOS, both in nanosecond steps and neither counting time asleep. An application may drive the hub’s clock instead with set_monotonic_nanos.
328chain_stepu64Remaining steps in the optional hash chain; a countdown to zero is a time to live. Zero when the replay defence is not used.
402instructionu16The opcode, from the table below.
422_pad0u16Alignment padding. MUST be zero.
444scopeu32The execution scope bound; interpreted as a context id and a subscope by the context registry.
4816envelope_ref[u8; 16]The content id of an associated PulseEnvelope, or all zeros when none is attached.
6432preimage[u8; 32]The hash-chain preimage for the optional replay defence. MAY be zero.
9632_pad2[u8; 32]Fills the second cache line. Carries the heartbeat overlay (2.5) when the instruction is Heartbeat, and the data overlay (2.6) when it is Data.

The first 64 bytes are one cache line and the second 64 bytes another; a Pulse occupies exactly two.

Compile-time invariant. A conformant implementation MUST assert at build time that its Pulse type is exactly 128 bytes and 64-byte aligned. The reference does so with a constant assertion in pulse/pulse-canon/src/pulse.rs, and PULSE_WIRE_SIZE is published as a public constant.

2.2 Instructions

OpcodeNameMeaning
0x0000NullA blank slot: zeroed memory or an empty buffer.
0x0001HeartbeatA liveness signal. Bytes 96 to 128 carry the heartbeat overlay (2.5).
0x0010DataA data signal. envelope_ref names the PulseEnvelope that carries the payload, and bytes 96 to 128 carry the data overlay (2.6).

Receivers MUST treat an unknown opcode as Null and MUST NOT fail-stop on one; the reference’s Instruction::from_u16 maps every other value to Null. Future draft revisions MAY assign further opcodes.

2.3 Alignment

The Pulse type MUST be aligned to a 64-byte boundary, one cache line on contemporary x86-64 and AArch64 processors. The alignment prevents false sharing when adjacent slots are touched by different cores, makes a read a single cache-line fetch per line, and permits zero-copy reinterpretation of an aligned byte slice as a Pulse. Implementations on platforms with a different cache-line size MUST still align to 64 bytes for wire compatibility.

2.4 SeqLock access and multi-slot atomicity

When Pulses are exchanged through a shared-memory ring, the seq_lock word gives readers torn-read detection without a mutex; the protocol is chapter 4. The layout provides atomicity for one Pulse at a time. A producer that needs several Pulses to be observed together MAY wrap them as inner envelopes of one nested PulseEnvelope (3.4), identified by a single content id, and publish one Pulse that references it.

2.5 The heartbeat overlay

When instruction is Heartbeat, bytes 96 to 128 MUST be read as a HeartbeatPayload:

Offset within _pad2SizeFieldTypeMeaning
08schema_versionu64An opaque schema or configuration version, compared bytewise.
88uptime_msu64Milliseconds since the node started.
164error_countu32Errors since the node started.
202stage_countu16Registered processing stages.
222path_countu16Registered processing paths.
242consecutive_errorsu16Errors in the most recent window.
261wake_stateu8Position in the wake cycle, 0 to 15.
271health_stateu80 healthy, 1 warning, 2 restarting, 3 isolated.
284_reserved[u8; 4]MUST be zero.

The reference reads and writes the overlay through heartbeat_payload and set_heartbeat_payload. When the instruction is neither Heartbeat nor Data, bytes 96 to 128 MAY hold any pattern and receivers MUST ignore them.

2.6 The data overlay

When instruction is Data, bytes 96 to 128 are a DataPayload, which names the stored copy of the envelope that envelope_ref identifies. Envelopes with the same content share an id, so the id alone cannot say which copy an envelope store (chapter 5) holds; the overlay names one by its store cursor.

Offset within _pad2SizeFieldTypeMeaning
08store_cursoru64The cursor at which the envelope store holds the copy, when has_store_cursor is 1.
81has_store_cursoru81 when store_cursor names a copy; any other value names none.
923reserved[u8; 23]MUST be zero.

All zeros, the _pad2 of a zeroed Pulse, name no copy. A receiver that reads envelopes from a store MAY fetch the copy at the named cursor, and MUST treat what it finds there as the signalled envelope only when it carries the id envelope_ref names, since the store may have reused the slot. A receiver that does not read a store MAY ignore the overlay. The reference writes it through set_data_payload and set_store_cursor, reads it through data_payload and store_cursor, and its hub fetches the named copy with EnvelopeStore::fetch_at, which checks the id.

2.7 What would refute this chapter

The vector pulse-layout-known-good.hex is the bytes of a Pulse with known field values. An implementation whose Pulse does not reproduce them field for field, or whose type is not 128 bytes and 64-byte aligned, does not conform; a reference build in which the layout assertion fails contradicts this chapter and is a defect.