MPP Protocol Reference
- Version: MPP v2
- Audience: Developers implementing a mobrule Pack Adapter in any language.
Breaking change from v1. MPP v2 introduces a mandatory
pull{queue}inbound frame as the credit grant for dispatch, adds aqueuerouting tag to the outboundinvocationframe, narrows the disconnect-requeue scope to in-flight rows only, and rejects legacympp:1envelopes withunsupported_mpp_version. There is no fallback path — adapters that do not sendpullcannot pump invocations; the bridge does not eagerly dispatch on row insert. MPP v1 is deprecated and unsupported.
1. Overview
Section titled “1. Overview”The Mobrule Pack Protocol (MPP) is the wire format used between the mobrule bridge and a Pack’s Adapter process. The bridge is the platform-owned router that relays Invocations (Pack events that need to run inside the game). The Adapter is community-supplied: it speaks MPP and translates each Invocation into actual in-game effects.
The protocol is line-oriented JSON over TCP. The Adapter connects;
the bridge listens. After a successful authentication handshake the
Adapter must issue a pull{queue} for each queue it is ready to
service; the bridge then dispatches at most one matching Invocation
per outstanding pull, and the Adapter reports the outcome back. This
document is the authoritative reference for the v2 wire format —
Adapters written from this document alone are guaranteed to
interoperate with any conforming bridge.
2. Transport
Section titled “2. Transport”- Protocol: TCP. The bridge binds a port (default
7777, configurable via--port); the Adapter is the initiator. - Framing: NDJSON — exactly one JSON object per line. Each frame
is terminated by a single LF byte (
\n,0x0A). CRLF is not emitted by either side; receivers SHOULD tolerate a trailing\rbefore the LF as a safety net but MUST NOT depend on it. - Encoding: UTF-8.
- No length prefix. Parsers buffer until LF and parse the preceding bytes as a single JSON value.
- Max frame size: 64 KiB (65,536 bytes including the trailing
LF). If either side observes a line exceeding this cap, it MUST
close the connection. The bridge SHOULD send a
protocol_errorwithcode = "FRAME_TOO_LARGE"before closing if at all possible. - Forward compatibility: parsers MUST tolerate unknown keys. Future minor versions of v2 may add optional fields; receivers ignore unrecognised keys silently.
3. Versioning
Section titled “3. Versioning”Every frame, in either direction, MUST include:
{"mpp": 2, "type": "<frame-type>"}"mpp"(integer) is the protocol major version. v2 implementations emit and accept only2."type"(string) is the frame discriminator and selects how the remaining fields are interpreted.
JSON objects have no guaranteed key ordering. Senders SHOULD emit
"mpp" and "type" first by convention, but parsers MUST tolerate
any key order.
3.1 Negotiation
Section titled “3.1 Negotiation”The Adapter’s hello MUST carry "mpp": 2. If the value is anything
other than 2, the bridge replies with
hello_ack ok=false reason="unsupported_mpp_version" and closes the
connection. In particular, legacy mpp:1 envelopes are rejected
unconditionally — there is no compatibility shim.
Worked example — successful negotiation:
{"mpp": 2, "type": "hello", "pack_id": "com.example.mypack", "manifest_hash": "sha256:abc123...", "bridge_token": "<raw-token>"}{"mpp": 2, "type": "hello_ack", "ok": true, "broadcaster_login": "streamer_login", "session_id": 42}Worked example — rejected mpp:1:
{"mpp": 1, "type": "hello", "pack_id": "com.example.mypack", "manifest_hash": "sha256:abc123...", "bridge_token": "<raw-token>"}{"mpp": 2, "type": "hello_ack", "ok": false, "reason": "unsupported_mpp_version"}Future versions may introduce a counter-proposal field in
hello_ack to advertise a supported downgrade; v2 does not.
4. Frame Inventory
Section titled “4. Frame Inventory”A→B denotes Adapter-to-Bridge; B→A denotes Bridge-to-Adapter.
| Frame | Direction | Lifecycle phase | Purpose |
|---|---|---|---|
hello | A→B | auth | Adapter self-registers |
hello_ack | B→A | auth | Bridge confirms or rejects |
pull | A→B | dispatch | Adapter grants one credit on a queue |
invocation | B→A | dispatch | Bridge sends an Event to the Adapter |
ack | A→B | dispatch | Adapter confirms receipt of invocation |
applied | A→B | dispatch | Adapter reports effect applied in-game |
done | A→B | dispatch | Adapter signals completion (terminal) |
failed | A→B | dispatch | Adapter reports failure (terminal) |
heartbeat | A↔B | keepalive | Liveness ping; no response required |
log | A→B | telemetry | Adapter emits a log line |
state_write | A→B | telemetry | Adapter writes an adapter-owned Pack-state key |
protocol_error | B→A | error | Bridge signals a fatal protocol violation |
The twelve frame types listed above constitute the complete MPP v2 frame set.
5. Frame Definitions
Section titled “5. Frame Definitions”In every example below the "mpp": 2 and "type" fields are
mandatory; field tables document the additional payload.
5.1 hello (A→B)
Section titled “5.1 hello (A→B)”{"mpp": 2, "type": "hello", "pack_id": "com.example.mypack", "manifest_hash": "sha256:abc123...", "bridge_token": "<raw-token>"}| Field | Type | Required | Notes |
|---|---|---|---|
pack_id | string | yes | Reverse-DNS Pack identifier. MUST equal the bridge’s loaded Pack. |
manifest_hash | string | yes | Pack Manifest hash in the form "sha256:" + 64 lowercase hex. |
bridge_token | string | yes | Raw bearer token issued out-of-band by the platform during pairing. |
The Adapter MUST send hello as the first frame on a fresh
connection. The bridge MUST receive hello within 10 seconds of
accept; otherwise it closes the connection silently.
5.2 hello_ack — success (B→A)
Section titled “5.2 hello_ack — success (B→A)”{"mpp": 2, "type": "hello_ack", "ok": true, "broadcaster_login": "streamer_login", "session_id": 42}| Field | Type | Required | Notes |
|---|---|---|---|
ok | boolean | yes | true on success. |
broadcaster_login | string | yes (ok) | The Twitch broadcaster login the bridge is authenticated as. |
session_id | u64 | yes (ok) | Platform session identifier — opaque to the Adapter, useful in logs. |
persisted | array | no | Replay of durably stored persist Pack-state keys. Omitted when empty. See below. |
On success the bridge server-pins pack_id and
broadcaster_login from its loaded Manifest and session record. The
Adapter is now considered the active Adapter for that session and
MUST issue a pull for each queue it intends to service before any
invocation will be sent.
persisted[] replay. For every Pack-state key declared
persist = true in pack.toml that has a durably stored value, the bridge
includes one { key, value, scope } row:
{"mpp": 2, "type": "hello_ack", "ok": true, "broadcaster_login": "streamer_login", "session_id": 42, "persisted": [{"key": "resets", "value": 3, "scope": "trainer:42"}]}| Field | Type | Required | Notes |
|---|---|---|---|
key | string | yes | The declared persist Pack-state key. |
value | any | yes | The last durably stored value (schema-valid under the key’s current schema). |
scope | string | no | The opaque run-scope tag the Adapter attached on the durable write. Omitted when none. |
The bridge replays unconditionally. The bridge never interprets,
compares, or parses scope — it stores it, echoes it here verbatim, and the
Adapter alone matches each scope against its current run to decide
resume-vs-reset. The Adapter MUST apply persisted[] before its first
state_write, or it would clobber the seeded value with zeros under
last-write-wins. A pack with no persist keys sees no persisted field at
all.
5.3 hello_ack — failure (B→A)
Section titled “5.3 hello_ack — failure (B→A)”{"mpp": 2, "type": "hello_ack", "ok": false, "reason": "manifest_hash_mismatch"}| Field | Type | Required | Notes |
|---|---|---|---|
ok | boolean | yes | false on failure. |
reason | string | yes (!ok) | One of the reason codes below. |
Reason codes:
invalid_token—bridge_tokendoes not match.manifest_hash_mismatch— Adapter’s manifest_hash differs from bridge’s.pack_id_mismatch— Adapter claims a differentpack_id.unsupported_mpp_version— Adapter’smppis not2(covers the legacympp:1case explicitly).unpaired— bridge has not completed its pairing handshake with the cloud.already_connected— another Adapter is currently active.
After sending a failure hello_ack the bridge closes the TCP
connection.
5.4 invocation (B→A)
Section titled “5.4 invocation (B→A)”{"mpp": 2, "type": "invocation", "id": 123, "queue": "default", "event": "give_item", "params": {"item_id": 4, "count": 1}}| Field | Type | Required | Notes |
|---|---|---|---|
id | u64 | yes | Unique Invocation identifier. |
queue | string | yes | Routing tag identifying which Pack-declared queue this Invocation belongs to. Matches the [events.<name>].queue value in the Pack Manifest. The Adapter MUST route this Invocation to its internal per-queue handler. |
event | string | yes | Event name as declared in the Pack Manifest. |
params | object | yes | Event parameters; already JSON-Schema-validated by the bridge before dispatch. |
When the bridge sends an invocation it has already transitioned
the Invocation from pending to dispatched, and a matching
pull{queue} credit has been consumed (see §5.5 and §6). The
Adapter SHOULD respond promptly with at least one of ack,
applied, done, or failed, and MUST wait until the queue’s
configured ready_after state is reached before sending the next
pull for the same queue.
5.5 pull (A→B)
Section titled “5.5 pull (A→B)”{"mpp": 2, "type": "pull", "queue": "default"}| Field | Type | Required | Notes |
|---|---|---|---|
queue | string | yes | Name of the queue the Adapter is ready to service. MUST be a queue declared in the active Pack Manifest. |
Credit-of-1 semantics. Each pull{queue} grants the
bridge permission to dispatch at most one matching Invocation on
that queue. The bridge does not pre-credit and does not multi-pull.
After the Adapter sends pull{queue} and the bridge dispatches a
matching pending Invocation, the Adapter MUST wait until that
Invocation reaches the queue’s ready_after state (applied by
default, or done if [queues.<name>].ready_after = "done") before
sending the next pull for that same queue. Sending an additional
pull for a queue that already has an outstanding (parked) credit
or an in-flight Invocation is a protocol violation; the bridge MAY
emit protocol_error code=INVALID_FRAME and close.
Per-queue independence. Pulls on different queues are independent: the Adapter MAY have one outstanding pull per declared queue simultaneously. This is how an Adapter pumps multiple queues concurrently while preserving strict serial ordering inside each queue.
Unknown queue. If queue is not declared in the active Pack
Manifest, the bridge replies with
protocol_error code="UNKNOWN_QUEUE" and closes the connection
(see §5.12).
No fallback. If the Adapter never sends pull{queue}, the
bridge will never dispatch on that queue. Pending Invocations
accumulate indefinitely. This is by design — there is no
eager-dispatch escape hatch.
5.6 ack (A→B)
Section titled “5.6 ack (A→B)”{"mpp": 2, "type": "ack", "id": 123}The Adapter confirms it received the invocation frame. The bridge
logs receipt; the Invocation’s state does not change (it remains
dispatched). ack is informational and does NOT release the
queue’s in-flight slot (see §6).
5.7 applied (A→B)
Section titled “5.7 applied (A→B)”{"mpp": 2, "type": "applied", "id": 123, "result": {"resolved_item_id": 4, "resolved_count": 1}}| Field | Type | Required | Notes |
|---|---|---|---|
id | u64 | yes | The Invocation id this frame relates to. |
result | object or null | yes | Effect-specific result payload. May be null if there is nothing to report. |
The bridge transitions the Invocation from dispatched to applied.
If the queue’s ready_after = "applied" (the default), this also
clears the queue’s in-flight slot, freeing it to dispatch the next
parked or future pull for the queue (see §6).
5.8 done (A→B)
Section titled “5.8 done (A→B)”{"mpp": 2, "type": "done", "id": 123}The Adapter signals completion. The bridge transitions the Invocation
from applied to done. done is terminal; no further frames for
this id are expected or honoured. If the queue’s
ready_after = "done", done clears the queue’s in-flight slot.
5.9 failed (A→B)
Section titled “5.9 failed (A→B)”{"mpp": 2, "type": "failed", "id": 123, "reason": "item not found in inventory"}{"mpp": 2, "type": "failed", "id": 124, "reason": "target already fainted", "refund": true}| Field | Type | Required | Notes |
|---|---|---|---|
id | u64 | yes | The Invocation id this frame relates to. |
reason | string | yes | Human-readable failure reason; recorded verbatim. |
refund | bool | no | If true, signals the originating Cue should be refunded to the viewer. Default false. |
When refund is omitted or false, the bridge transitions the Invocation to
terminal state failed. When refund=true, the bridge transitions it to
terminal state refunded and records a refund request; the platform
subsequently calls the upstream provider’s refund API (e.g. Twitch Helix
UpdateRedemptionStatus set to CANCELED for channel-point redemptions).
failed (regardless of refund) is terminal and always clears the
queue’s in-flight slot.
Refundability depends on the upstream provider:
- Channel-point redemptions: refundable via Helix; viewer’s points are returned.
- Bits cheers: not programmatically refundable by Twitch; the refund request is recorded as skipped and the streamer is notified out-of-band.
- Other Cue sources: handled case-by-case by the platform’s refund worker.
Adapters SHOULD use refund=true only when the Invocation could not be
meaningfully applied (target already in requested state, prerequisite resource
missing, gating impossible). Adapters MUST NOT use refund=true to express
“applied, but partially” — that is applied followed by done or failed
without refund.
5.10 heartbeat (A↔B)
Section titled “5.10 heartbeat (A↔B)”{"mpp": 2, "type": "heartbeat"}Either side MAY emit heartbeat at any time after the handshake.
No response is required. A connection that goes more than 30s
without traffic in either direction MAY be considered stale; the
bridge SHOULD log a warning but is not required to close the
connection on that basis alone.
5.11 log (A→B)
Section titled “5.11 log (A→B)”{"mpp": 2, "type": "log", "level": "info", "message": "loaded ROM in 12 ms"}| Field | Type | Required | Notes |
|---|---|---|---|
level | string | yes | One of trace, debug, info, warn, error. |
message | string | yes | Free-form log line. |
The bridge captures log frames into an in-memory ring buffer
(capacity 1000 entries, FIFO eviction).
5.11a state_write (A→B)
Section titled “5.11a state_write (A→B)”{"mpp": 2, "type": "state_write", "key": "resets", "value": 3, "scope": "trainer:42"}| Field | Type | Required | Notes |
|---|---|---|---|
key | string | yes | A [state.<key>] declared in the active Pack Manifest. |
value | object/any | yes | Arbitrary JSON; validated against the key’s declared schema (if any). |
scope | string | no | Opaque run-scope tag. Stored + replayed for a persist = true key; ignored for non-persist keys. The bridge never interprets it. |
The Adapter is the writer for a Pack-state key only when that
key’s [state.<key>] writer = "adapter". On receipt the bridge
enforces, in order:
keyis declared in the active Manifest’s[state.*]table — elseprotocol_error code=INVALID_FRAMEand close.- The key’s declared
writerisadapter— elseprotocol_error code=INVALID_FRAMEand close (the frame is structurally valid but the Adapter is not the authorised writer). valuevalidates against the key’s compiled JSON Schema (when the key declares a schema; keys with no schema accept any JSON) — elseprotocol_error code=SCHEMA_VIOLATIONand close.
On success the bridge inserts the value into the Pack-state store
keyed by (pack_id, key). There is no outbound acknowledgement
frame: an accepted write is silent, and the snapshot/diff fan-out to
overlay subscribers is the store’s concern, not this frame’s.
When the key is declared persist = true, the accepted value +
scope are additionally durably stored (eventually durable — an async
write, not commit-before-ack) keyed by (pack_id, key), last-write-wins, and
replayed to the Adapter in the next hello_ack persisted[] leg (§5.2). The
durable store survives both Adapter and bridge restarts.
5.12 protocol_error (B→A)
Section titled “5.12 protocol_error (B→A)”{"mpp": 2, "type": "protocol_error", "code": "INVALID_FRAME", "message": "unknown type: 'foo'"}| Field | Type | Required | Notes |
|---|---|---|---|
code | string | yes | Machine-readable error code (see list below). |
message | string | yes | Human-readable detail. |
Codes:
INVALID_FRAME— JSON parse failed, missingmpp/type, or unknowntype. Also covers astate_writeto an undeclared key or to a key the Adapter does not own.UNKNOWN_INVOCATION_ID— Adapter referenced anidthe bridge did not dispatch.UNKNOWN_QUEUE— Adapter sentpull{queue}for a queue not declared in the active Pack Manifest.SCHEMA_VIOLATION— params for an Invocation, or astate_writevalue, failed schema validation.UNEXPECTED_STATE_TRANSITION— Adapter sent a frame inconsistent with the Invocation’s state.FRAME_TOO_LARGE— line exceeded 64 KiB.
protocol_error from the bridge is always fatal: the bridge sends
the frame and immediately closes the TCP connection. An Adapter
that receives protocol_error MUST treat it as terminal and exit
or reconnect rather than continue.
An Adapter-originated protocol_error (sent A→B in unusual
recovery scenarios) is similarly treated as fatal by the bridge:
it is logged into the ring buffer and the connection is closed.
6. Dispatch Semantics
Section titled “6. Dispatch Semantics”MPP v2 is pull-driven. Dispatch occurs on a given queue if and only if both conditions hold:
- The Adapter has sent a
pull{queue}whose credit is still outstanding (parked) for that queue. - A
pendingInvocation exists whose queue equals that queue and whosepack_id+manifest_hashmatch the bridge’s server-pinned identity.
The bridge does not dispatch eagerly when an Invocation is
created. A new pending Invocation whose queue has no parked pull
simply waits; it will be picked up the next time the Adapter sends
pull{queue}. There is no fallback path: Adapters that do not pull
do not pump.
6.1 Per-queue state
Section titled “6.1 Per-queue state”For each queue the bridge maintains an in-memory record of:
in_flight— set when an Invocation has been dispatched on this queue and not yet cleared; empty otherwise.pending_pull— set when apull{queue}has arrived for this queue and not yet been consumed by a dispatch; cleared on dispatch.ready_after— resolved from the active Manifest’s[queues.<name>].ready_afterat the time of the first pull.
A queue is dispatch-eligible iff in_flight is empty and
pending_pull is set. When both conditions hold and a matching
pending Invocation exists, the bridge dispatches the oldest such
Invocation, marks it in-flight, and clears pending_pull.
6.2 ready_after
Section titled “6.2 ready_after”[queues.<name>].ready_after controls when the bridge clears
in_flight for that queue:
"applied"(default) —in_flightclears as soon as the bridge receivesappliedfor the in-flight Invocation. The queue immediately becomes dispatch-eligible if a newpullis parked, even though the Invocation may still progress todoneorfailed. Use this when the Adapter’s “applied” event represents the point at which the next Invocation can safely run."done"—in_flightclears only when the bridge receivesdone(orfailed) for the in-flight Invocation. The queue is held strictly until the terminal transition. Use this for queues whose effects must fully complete in-game before the next Invocation begins.
failed (terminal) always clears in_flight, regardless of
ready_after.
6.3 Per-queue serial dispatch
Section titled “6.3 Per-queue serial dispatch”Within a single queue, at most one Invocation is in flight at any time. The credit-of-1 + in-flight gate guarantees this without any extra coordination: while an Invocation is in flight, no subsequent pull on the same queue can be consumed.
Across queues, dispatches are independent. An Adapter with N declared queues can have up to N Invocations in flight simultaneously (one per queue).
6.4 Unknown queue handling
Section titled “6.4 Unknown queue handling”Queue names are not validated when a Cue produces an Invocation; validation happens at bridge dispatch time:
- If a
pendingInvocation names a queue that the bridge’s loaded Manifest does not declare, the bridge marks itfailedwithreason = "unknown_queue: <name>"and does not dispatch it. - If the Adapter sends
pull{queue}for a queue the Manifest does not declare, the bridge replies withprotocol_error code="UNKNOWN_QUEUE"and closes the connection.
7. Identity Pinning
Section titled “7. Identity Pinning”After a successful hello_ack, the bridge records pack_id and
broadcaster_login as the server-pinned identity for the
connection. These values are taken from the bridge’s loaded
Manifest and its platform session — not from anything the Adapter
sends in subsequent frames.
The Adapter MUST NOT include pack_id or broadcaster_login in
non-hello frames. If the bridge sees those keys in any non-hello
frame, they are silently ignored. This rule prevents a compromised
or buggy Adapter from claiming another Pack’s identity once
connected.
Similarly, queue is a routing tag on invocation and pull
only — the bridge does not honour queue on terminal frames
(ack, applied, done, failed). The bridge resolves the
queue for those frames from its own in-flight map keyed by the
Invocation id.
8. Disconnect Behaviour
Section titled “8. Disconnect Behaviour”When the Adapter’s TCP connection closes (clean FIN, RST, read
error, or protocol_error), the bridge:
- Stops dispatching further Invocations to this Adapter.
- For every queue, requeues only the Invocation currently
in flight (i.e. dispatched-but-not-yet-cleared) back to
pending. Invocations that have already reachedappliedon aready_after = "done"queue are left inapplied— they are NOT requeued. This narrowed scope eliminates the double-application risk that the v1 “requeue everything dispatched” rule had for long-runningready_after = "done"workflows. - Pending Invocations whose queue had a parked pull at disconnect
time stay
pending; the next Adapter connection’s firstpull{queue}will pick them up. - The bridge clears its in-memory per-queue state and resumes listening for a new Adapter connection. State is built fresh on the next hello.
Invocations belonging to other bridge sessions are untouched. There is no timeout-based retry. The bridge does not re-send or duplicate Invocations after dispatch. Resilience is provided exclusively by the narrowed re-queue-on-disconnect mechanism above.
9. Connection Lifecycle
Section titled “9. Connection Lifecycle”Adapter Bridge | ---- hello (mpp:2) -----> | | <---- hello_ack (ok) ------- | bridge server-pins pack_id + broadcaster_login | | | ----- pull (queue=default) -> | bridge parks credit on "default" | <--- invocation (id=42, | bridge: invocation 42 pending → dispatched | queue=default) -------- | in_flight[default] = 42; pending_pull[default] = false | ----- ack (id=42) ----------> | | ---- applied (id=42) -------> | bridge: 42 dispatched → applied | | ready_after=applied → in_flight[default] = None | ------ done (id=42) --------> | bridge: 42 applied → done (terminal) | ----- pull (queue=default) -> | bridge parks next credit | | | <--- heartbeat -------------- | | ---- log (info, "...") -----> | bridge appends to ring buffer | | | [TCP close] | bridge requeues ONLY in-flight rows | | per queue → pending9.1 Invocation lifecycle (for cross-reference)
Section titled “9.1 Invocation lifecycle (for cross-reference)”pending ──► dispatched ──► applied ──► done (terminal)pending ──► dispatched ──► failed (terminal; refund=false)pending ──► dispatched ──► refunded (terminal; refund=true)pending ──► failed (validation-fail before dispatch, e.g. unknown_queue)The bridge owns every transition. The Adapter never talks to the
platform’s database directly. The refunded state is reached only
via a failed frame carrying refund: true.
10. Error Handling
Section titled “10. Error Handling”| Scenario | Action |
|---|---|
| Inbound JSON parse error | Bridge sends protocol_error code=INVALID_FRAME, closes connection. |
Frame missing "mpp" or value ≠ 2 | Hello path: hello_ack ok=false reason=unsupported_mpp_version. Post-hello: protocol_error code=INVALID_FRAME. Both close the connection. |
pull{queue} referencing a queue not in the active Manifest | Bridge sends protocol_error code=UNKNOWN_QUEUE, closes connection. |
ack/applied/done/failed referencing an id this connection did not dispatch | Bridge sends protocol_error code=UNKNOWN_INVOCATION_ID, closes connection. |
| Bridge-side schema validation fails before dispatch | Bridge marks the Invocation failed with reason = "schema_validation: <details>". Adapter is never sent the frame. |
| Pending Invocation naming a queue not in the active Manifest | Bridge marks it failed with reason = "unknown_queue: <name>"; no invocation is emitted. |
Adapter sends protocol_error | Bridge logs the frame into the ring buffer and closes the connection. |
| Frame exceeds 64 KiB | Receiver closes the connection; bridge SHOULD send protocol_error code=FRAME_TOO_LARGE first. |
state_write to a key not declared in the active Manifest, or to a key the Adapter does not own (writer ≠ adapter) | Bridge sends protocol_error code=INVALID_FRAME, closes connection; no store write. |
state_write value fails the key’s compiled schema | Bridge sends protocol_error code=SCHEMA_VIOLATION, closes connection; no store write. |
11. Security & Threat Model (informative)
Section titled “11. Security & Threat Model (informative)”MPP v2 is designed for localhost deployment: the bridge binds
to a local TCP port and the Adapter runs on the same machine. The
bridge_token is a shared secret of approximately 32+ bytes. The
following are explicit choices, not oversights:
- No TLS: the assumption is loopback transport. Operators who expose the bridge port off-host are responsible for tunnelling it themselves.
- Token comparison is a plain hash equality (
sha256(received) == stored_hash). Constant-time comparison is not specified because the threat model precludes a meaningful timing-attack adversary. - The bridge ignores
pack_id/broadcaster_loginoutsidehelloprecisely to limit damage from a hostile Adapter. - Queue names are capped at 64 bytes at ingress and validated against the loaded Manifest at bridge dispatch; a hostile or buggy caller cannot inflate storage by injecting arbitrary queue names.
12. Version History
Section titled “12. Version History”| Version | Status | Notes |
|---|---|---|
| v2 | Current | Adds pull{queue} credit grant + invocation.queue routing tag; narrows disconnect requeue to in-flight per queue; rejects mpp:1. Breaking; no v1 fallback. |
| v1 | Deprecated | Initial release. Eager dispatch; single-slot serial dispatch; no queue concept. Unsupported as of v2 — mpp:1 envelopes are rejected with unsupported_mpp_version. |
See also
Section titled “See also”- Adapter tutorial: Python — walks through using these frames.
- pack.toml reference — the Manifest the bridge pins per session.