Skip to content

client, signal: Optionally disable Signal and node processing#1139

Open
purpshell wants to merge 4 commits into
tulir:mainfrom
titan-api:rajeh/raw-node-hook
Open

client, signal: Optionally disable Signal and node processing#1139
purpshell wants to merge 4 commits into
tulir:mainfrom
titan-api:rajeh/raw-node-hook

Conversation

@purpshell
Copy link
Copy Markdown
Contributor

@purpshell purpshell commented May 11, 2026

Adds two opt-in extension points for proxy/intermediary use cases:

  1. Client.RawNodeHandler: called for every inbound node after Noise decrypt + Node decode, before standard dispatch (IQ response match, tag-based routing). Handlers can drop the node, replace it with a modified copy, or pass through. Lets an external system observe or take over inbound stanza handling without forking the library.

  2. Client.DisabledFeatures.Signal: short-circuits the library's Signal session machinery: decryptMessages becomes a stub, uploadPreKeys becomes a no-op. Use when an external system owns the Signal session for this device.

  3. events.UndecryptedMessage: emitted in place of decryption when DisabledFeatures.Signal is set. Carries the parsed MessageInfo and the raw <message> node (including all <enc> children) so the downstream Signal session owner can forward the envelope verbatim. This is distinct from UndecryptableMessage, which means decryption was attempted and failed; this one means decryption was intentionally skipped and ack ownership is downstream.

Checklist

Adds two opt-in extension points for proxy/intermediary use cases:

1. `Client.RawNodeHandler` — called for every inbound node after Noise
   decrypt + Node decode, before standard dispatch (IQ response match,
   tag-based routing). Handlers can drop the node, replace it with a
   modified copy, or pass through. Lets an external system observe or
   take over inbound stanza handling without forking the library.

2. `Client.DisabledFeatures.Signal` — short-circuits the library's
   Signal session machinery: decryptMessages becomes a true no-op (no
   decrypt and no ack — the downstream decrypting client is responsible
   for ack'ing), uploadPreKeys becomes a no-op. Use when an external
   system owns the Signal session for this device.

Both are labeled DANGEROUS in their godoc: incorrect use breaks
stateful invariants (session ratchets, IQ correlation, etc.). They
exist to let upstream services hold the Signal session externally and
forward raw envelopes verbatim, without the library second-guessing.

Designed as forward-compatible additions:
  - DisabledFeatures is a struct so future flags add fields rather
    than parameters.
  - RawNodeHandler returns (modified, drop) so the contract can grow
    without changing the signature.

Zero behavior change when the new fields are left at their zero values.
@purpshell purpshell force-pushed the rajeh/raw-node-hook branch from 81bf508 to 8ef4a70 Compare May 11, 2026 10:46
purpshell added 3 commits May 11, 2026 14:10
When DisabledFeatures.Signal is set, `<message>` envelopes were routed
through handlerQueue -> handleEncryptedMessage -> dispatchEvent, which
fires on the handler-queue goroutine. Callers that also forward non-
message stanzas via RawNodeHandler (which fires on the recv goroutine)
saw wire-order break between the two paths: a `<receipt>` could reach
the downstream forwarder before the `<message>` it acknowledges,
because the receipt was dispatched directly from recv while the
message was waiting in handlerQueue.

Short-circuit `<message>` + DisabledFeatures.Signal in handleFrame: parse
MessageInfo and dispatch UndecryptedMessage synchronously, right after
the RawNodeHandler block. Both forwarding paths now run on the recv
goroutine in wire order.

The else-if branch in handleEncryptedMessage stays in place so callers
invoking it directly (DangerousInternals.HandleEncryptedMessage,
`<appdata>` via the nodeHandlers map) still get the event.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant