Expose canonical message recipient (audience) from the parser (WASM + NIF) to kill the browser's hand-rolled derivation #11
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
The parser does not expose a message's canonical recipient to consumers. Every
consumer therefore re-derives it, and the one consumer that cannot reuse the
parser — the browser chat client — hand-rolls a second S-expression parser to do
so. Because that recipient is signed into the SPEC-012 signed-member envelope as
audience, any drift between the hand-rolled derivation and the canonical parsersilently breaks every frame with
bad-signature. This has already produced onecritical defect.
This issue asks
cbcl-rsto expose the canonical recipient from the parser surface(WASM, and ideally the NIF), so all consumers source it from one compiled
implementation instead of re-implementing it.
Background: where
audiencecomes fromThe signed-member envelope (SPEC-012 v0.5.2, REQ-001) binds the recipient into the
Ed25519 signature:
For a frame to verify, the
audiencebytes the client signs over must bebyte-for-byte identical to what the hub independently reconstructs.
audienceisthe canonical recipient of the message — the innermost recipient after descending
(lang <dialect> <inner>)wrappers, with the@sigil retained.The fragility
The recipient rule currently exists in two implementations:
Hub side (LFE). Derives the recipient from the canonical parser's structured
output (the NIF-parsed message map /
cbcl-core-msg:room/innermost).Authoritative — it consumes the real parser.
Browser client (
app.jsin thecbcl-busumbrella,apps/cbcl_chat).Imports
parse_messagefromcbcl_wasm— the same parser, compiled to WASM —but
parse_messagereturns only the canonical string(
cbcl_wasm.d.ts:parse_message(input: string): string). To get therecipient it then re-parses that string with a hand-rolled
parseSexpr+recipientOf(app.js~lines 187, 267), approximating the hub'srule in JavaScript.
That hand-rolled shadow is the fragility:
@-sigil byte-parity bug, wherethe client stripped the sigil the hub retains and every frame failed
bad-signature.meta/teachhandling, fail-closed on ambiguous/multi-valued), so any non-plain-
tellframe isa latent silent-total-failure.
bad-signature.Root cause
The browser already ships the canonical parser (
cbcl_wasm_bg.wasm— the same Rustcode the hub runs as a NIF), but the WASM surface exposes only
parse_message → String, not the recipient. The browser holds the authoritative parser and is forcedto ignore it for this one field, re-implementing recipient extraction by hand.
Proposed change (in
cbcl-rs)Expose the canonical recipient from the parser surface, backed by the same core
function the NIF/
cbcl-core-msguses (no new implementation of the rule):crates/cbcl-wasm(src/lib.rs) a function such as: (Alternatively / additionally, giveparse_messagea structured JSON variant thatincludes the recipient, so the client gets canonical form + recipient in one call.)
cbcl-erlNIF so the hubreads the recipient from the parser rather than walking the parsed map.
recipient/innermostfunction so there is asingle source of truth across NIF and WASM.
Acceptance criteria
hub binds as
audience, for the same canonical message.app.jscan deleterecipientOf/parseSexpr(for audience) and callthe WASM accessor; the hand-rolled shadow is removed.
tell/ask,replyandother recipient-less terminals,
metaandteach,(lang …)-wrapped messages,and ambiguous/multi-recipient (fail-closed) cases.
Downstream consumers to update (tracked separately, in
cbcl-bus)apps/cbcl_chat/priv/web/app.js— replacerecipientOf/parseSexpraudiencederivation with the WASM accessor; re-vendor the rebuilt
cbcl_wasm.js/cbcl_wasm_bg.wasmintoapps/cbcl_chat/priv/web/cbcl/.cbcl-core-msg:room/cbcl-frame-recipient) stays the reference, or is also repointed at the sharedcore.
Refs
per-performative recipient; envelope
audience== payload recipient).