March Docs

NetFrame

NetFrame: length-prefixed framing for the distributed net-kernel transport.

A frame is a 4-byte big-endian length header followed by exactly that many payload bytes. Bytes are represented as List(Int) (each 0..255), matching Msgpack.encode's output, so a framed MessagePack value is NetFrame.encode(Msgpack.encode(value)).

decode pulls one complete frame off the front of a (possibly partial) buffer, returning the payload and the unconsumed remainder; None means "need more bytes" (the header or body has not fully arrived yet).

Functions

fnbody_lengthbody_length(buf : List(Int)) : Int#

The body length the first four bytes of [buf] announce (big-endian), or 0 when [buf] has fewer than four bytes (the caller checks there are four).

NetFrame.body_length([0, 0, 1, 2, 99])   -- 258
fndecodedecode(buf : List(Int)) : Option((List(Int), List(Int)))#

Pull one complete frame off the front of [buf]: Some((payload, rest)) when a full header and body have arrived, rest the bytes after the frame; None when more bytes are needed (fewer than four, or a body still short). Linear in the buffer length.

NetFrame.decode([0, 0, 0, 2, 7, 8, 9])   -- Some(([7, 8], [9]))
NetFrame.decode([0, 0, 0, 2, 7])         -- None
fnencodeencode(payload : List(Int)) : List(Int)#

Prepend a 4-byte big-endian length header to [payload] (bytes 0..255). The length must be below 2^32; it is not checked.

NetFrame.encode([1, 2, 3])   -- [0, 0, 0, 3, 1, 2, 3]
fnmac_overheadmac_overhead() : Int do 40 end#

Bytes a sealed frame adds to its payload (sequence number + tag): 40.

fnmac_tagmac_tag(key : Bytes, seq : Bytes, payload : Bytes) : Bytes#

The 32-byte MAC tag over [seq] (the 8-byte encoded number) and [payload] under [key]: HMAC-SHA256(key, seq ++ payload).

fnopenopen(key : Bytes, body : Bytes) : Result((Int, Bytes), String)#

Open a sealed frame BODY (what follows the length prefix) under [key]: Ok((seq, payload)) when the tag verifies, Err otherwise. The caller decides whether [seq] is acceptable (window_accept). Err("frame too short for its MAC") for a body under mac_overhead() bytes, Err("frame MAC mismatch") for a modified frame or the wrong key.

fnresealreseal(key : Bytes, seq : Int, framed : Bytes) : Bytes#

Seal an already-framed (length-prefixed, unsealed) frame [framed]: its old 4-byte prefix is dropped and the body sealed as seal(key, seq, body). For NodeQueue's frames, framed at enqueue time.

fnsealseal(key : Bytes, seq : Int, payload : Bytes) : Bytes#

A sealed frame for [payload] as frame number [seq] under [key]: 4-byte length ++ 8-byte big-endian seq ++ payload ++ 32-byte tag, the length covering everything after it (Bytes.length(payload) + mac_overhead()).

fnseq_sizeseq_size() : Int do 8 end#

Bytes of a sealed frame's sequence number: 8.

fntag_sizetag_size() : Int do 32 end#

Bytes of a sealed frame's MAC tag (HMAC-SHA256): 32.

fnwindow_acceptwindow_accept(hi : Int, mask : Int, seq : Int) : Option((Int, Int))#

Accept [seq] against the receive window ([hi], [mask]) (start from (-1, 0)): Some(new window), or None for a replay (already seen) or a number 32 or more below [hi] (too old). A number above [hi] slides the window up to it.

NetFrame.window_accept(-1, 0, 0)   -- Some((0, 1))
NetFrame.window_accept(5, 1, 3)    -- Some((5, 5))
NetFrame.window_accept(5, 5, 3)    -- None (replay)
fnwindow_sizewindow_size() : Int do 32 end#

How many sequence numbers at and below the highest one seen the receive window remembers: 32.