Fugu

Protocol::Imsg(3p) Perl Library Manual Protocol::Imsg(3p)

Protocol::Imsg - the OpenBSD imsg(3) frame, as bytes

    use Protocol::Imsg;
    my $codec = Protocol::Imsg->new;
    my $bytes = $codec->encode(type => 8, data => $payload);
    $codec->append($received_bytes);
    while (my $msg = $codec->next_message) {
        # $msg->{type}, $msg->{peerid}, $msg->{pid}, $msg->{data}
    }

This module frames and unframes messages of the OpenBSD imsg(3) facility. It takes bytes and returns bytes: it performs no system call, it opens no file, and it names no socket. Fugu::Imsg owns the socket and uses this module for the frame.

State three limits before anything else, because each one bounds where this module is usable.

Both ends of an imsg connection are the same machine, so imsg writes the header in the sender's own byte order. There is no byte-order negotiation and no network-order variant. Two hosts that disagree on byte order cannot exchange these frames.
Native imsg can pass a file descriptor as "SCM_RIGHTS" ancillary data, on a message whose "len" carries "IMSG_FD_MARK". This module masks that mark off and never sets it, so it reads such a message as a plain frame and it can never send one. A caller that needs descriptor passing needs "recvmsg" and "sendmsg" as well, which this codec has no way to reach.
"encode" reads $$ on every call.
Native imsg seeds the pid once, when the buffer is initialized. This module reads it per call instead. The two agree in one process and diverge in a forked child: the child's frames carry the child's pid where native imsg would still carry the parent's. Pass "pid" explicitly when that matters.

The wire format is in spec/protocol/MDNS-Imsg.md in this repository. That document is a curated reference, not an installed manual.

"HEADER_SIZE"
16, the size of "struct imsg_hdr".
"HEADER_TEMPLATE"
The "pack" template of the header: "L4", four native "uint32" fields in the order "type", "len", "peerid", "pid".
"MAX_IMSGSIZE"
16384, the bound on the whole message.
"MAX_PAYLOAD"
16368, which is "MAX_IMSGSIZE" less the header.
"FD_MARK"
0x80000000, the reserved high bit of "len".

new()

Return a codec with an empty decode buffer.

Return the framed bytes of one message.

"type"
The message type. Required.
"data"
The payload. The default is the empty string.
"peerid"
A correlation value that is opaque to this module. The default is 0, which is what a protocol that does not correlate sends.
"pid"
The sender pid. The default is $$. A "pid" of 0 also gives $$, because imsg substitutes the sender's pid for a 0 the caller passes.

The method returns "undef", with $! set to "EMSGSIZE", for a payload above "MAX_PAYLOAD". An encoder must refuse a larger payload rather than truncate it.

Add received bytes to the decode buffer and return the object. A partial header, a partial payload, and several whole messages at once are all normal.

next_message()

Pop one whole message off the buffer and return a hash reference with "type", "peerid", "pid", and "data". Return "undef" when more bytes are necessary.

An invalid "len" sets $! to "EBADMSG" and makes the failure permanent: native imsg drops such a connection, and so must every reader. "len" is invalid below "HEADER_SIZE" and above "MAX_IMSGSIZE".

is_failed()

Report the permanent framing failure. Nothing clears it but "reset".

reset()

Empty the buffer, clear the failure, and return the object. A transport calls it when it closes a connection, so that a frame which arrived before the close cannot come out after it.

Fugu::Imsg, spec/protocol/MDNS-Imsg.md

2026-10-16 OpenBSD