Skip to content

Bouncers: ZNC buffer replay, soju push, search and networks - #223

Draft
rdrake wants to merge 2 commits into
evilnet:developfrom
rdrake:bouncer-compat
Draft

rdrake wants to merge 2 commits into
evilnet:developfrom
rdrake:bouncer-compat

Conversation

@rdrake

@rdrake rdrake commented Nov 8, 2026

Copy link
Copy Markdown
Collaborator

What

Bouncer compatibility for ZNC and soju. Every change keys on what the server offers, so nefarious2 and plain ircds see no change.

  • ZNC buffer replay (BATCH znc.in/playback) is delivered as catch-up: appended, counted unread, no notification or highlight. ZNC has no msgids, so a buffered line is known by its time, sender, target and text, counted per conversation; a buffer ZNC keeps across attaches is not shown twice.
  • The bouncer owns the channel list. Behind ZNC (znc.in/* caps) or soju (BOUNCER_NETID, soju.im/bouncer-networks) the saved channels are never JOINed; the bouncer's own JOINs rebuild the list, so a channel parted from another client stays parted. Same rule as gamja.
  • soju web push: soju.im/webpush is requested alongside draft/webpush. Same command, VAPID token and payload.
  • soju search: a server that answers FAIL SEARCH INVALID_PARAMS in is asked once per joined channel, one after another (soju 0.7 has no labeled-response), and the parts are merged into one page. A server with MSGREFTYPES=timestamp is asked for a hit's context by its time.
  • soju networks (soju.im/bouncer-networks): a login without a network is the control connection. After a SASL login, each network it lists is added as its own saved network, bound with BOUNCER BIND before CAP END. A network the user removes is not added back. Ids are validated, at most 20 per list. The service worker binds too, so replying from a notification reaches the right network.

Testing

  • New mocha tests in test/irc/bouncer.ts, plus historysearch.ts, webpush.ts and caps.ts updates. Full suite, lint, typecheck, build and the smoke scenario pass.
  • Checked live in Chromium against soju 0.7 (networks added, bound, channels joined) and ZNC 1.9 (buffer shown once, in order).

Notes

  • Conflicts lightly with Add an optional server password (PASS) for networks #221 (server password): keep both options, and networks added from soju should also carry serverPassword. Whichever lands second takes that.
  • Not covered: TOFU certificate pinning for self-signed ZNC over TCP, ZNC's *playback module, CertFP.

ZNC's znc.in/playback batches are delivered as catch-up and deduped by
content per conversation. Behind a bouncer the saved channels are not
JOINed; the bouncer's own JOINs rebuild the list. soju.im/webpush is
requested alongside draft/webpush. soju search falls back to one
request per joined channel and to timestamp context refs. soju.im/
bouncer-networks adds each listed network as its own saved network,
bound with BOUNCER BIND before CAP END.
The cap of 20 counted one list at a time, and each -notify line is a
list of one, so a bouncer announcing new ids one by one got all of them
added. Count what the entry offered before (bouncerSeen) against it,
which also bounds bouncerSeen.

This branch was previously deployed

1 inactive deployment
github-pages — 6834437a Deployed Nov 8, 2026 by rdrake via deploy #767
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