Skip to content

Server password (PASS) for bouncers without client SASL - #10

Draft
rdrake wants to merge 2 commits into
developfrom
claude/project-thread-vlf7gw
Draft

rdrake wants to merge 2 commits into
developfrom
claude/project-thread-vlf7gw

Conversation

@rdrake

@rdrake rdrake commented Nov 8, 2026 •

Copy link
Copy Markdown
Owner

Why

Seance never sent PASS, so it could not log in to ZNC 1.9 and older at all. Those versions offer no SASL to clients: a client that sends no PASS gets 464 Password required, and ZNC then just waits. ZNC 1.10 does take SASL, but only with the saslplainauth module loaded, and only new installs load it by default. Getting every ZNC user to reconfigure isn't realistic, so this adds a server password field instead. The field isn't ZNC-specific: soju, older bouncers, password-protected ircds and a few networks that pass a server password to NickServ use it too.

What

  • On the wire: ConnectOptions.serverPassword is sent as PASS :<password> after CAP LS 302 and before NICK/USER, in the same flush. Nothing is sent when it is empty, so other networks see no change. It is independent of SASL; both are sent when both are set.
  • Storage: it follows the SASL password's rules.
    • It is written to disk only when rememberPassword is on. Otherwise hasServerPassword is kept, so needsPassword() sends a link or an autoconnect back to the connect form to ask for it.
    • It is stripped from a settings backup unless passwords are kept.
    • It can never come from a URL.
  • Not included: the push worker's own connection does not send it. None of the servers that need a server password offers a push Seance uses. The docs say where to add it if one does.
  • Network settings: switching it off clears it. Leaving the switch on with the field blank keeps the stored password, or the one a live client holds in memory.
  • 464 before 001: the connection stops instead of retrying. The lobby says whether a password was missing or refused, and the entry stops connecting by itself. After 001, a 464 (for example from OPER) stays an ordinary error.
  • Forms: both the connect form and network settings get a "This server needs a password" switch with the field under it. The connect form hides it when the deploy locks the host. "Remember password" now shows for either password.
  • Seven new strings, added through tools/i18n/add.ts.
  • docs/resources/client-features.md § Server password.

Testing

  • pnpm typecheck, oxlint, prettier and i18n:check are clean. The full mocha run passes (5142).
  • New tests:
    • test/irc/server-password.ts: line order, no PASS when empty, 464 missing or refused, 464 after 001, and keeping the in-memory password across an edit.
    • Round trip, stripping, needsPassword and the edit-form rules in test/irc/saved-networks.ts.
    • Backup stripping in test/helpers/settingsBackup.ts.
  • Live run in Chromium against ZNC 1.9.0, in front of nefarious2, through a small WebSocket-to-TCP line bridge standing in for the app's TCP socket:
    • With no password, ZNC answered 464. Seance sent QUIT :Server password required, showed the hint in the lobby and did not redial.
    • Setting the password in network settings and running /connect sent PASS :user/net:pw, got 001 and joined.
    • The connect form's new-server path sent PASS and joined. With "Remember password" off, the stored entry kept hasServerPassword: true and no password.
  • I haven't checked right-to-left or the qqx locale. No CSS was added; the rows reuse the existing form classes.

ZNC 1.9 and older offer no SASL to clients and answer a client that sends
no PASS with 464, then wait; ZNC 1.10 takes SASL only with saslplainauth
loaded, which only new installs get by default. Seance never sent PASS, so
it could not log in to them at all.

- ConnectOptions.serverPassword, sent as `PASS :<password>` after CAP LS
  and before NICK/USER; nothing is sent when it is empty. Independent of
  SASL: both go out when both are set.
- Stored under the SASL password's rules: on disk only with
  rememberPassword, with hasServerPassword kept so needsPassword() sends a
  link or an autoconnect back to the connect form; stripped from a settings
  backup unless passwords are kept; never from a URL; carried to the push
  worker's stash under the same rule, and the worker sends it too.
- Edit form: the switch off clears it; on with the field blank keeps the
  stored one, or the one a live client holds in memory.
- 464 before 001 stops the connection instead of retrying: the lobby says
  whether a password was missing or refused, and the entry stops
  connecting by itself. After 001 a 464 (OPER) stays an ordinary error.
- Forms: a "This server needs a password" switch and field in the connect
  form (hidden when the deploy locks the host) and in network settings;
  "Remember password" shows for either password.
@rdrake rdrake self-assigned this Nov 8, 2026
The worker logs in only for push (draft/webpush), and none of the servers
that want a server password offers a push Seance uses: ZNC has none, and
soju's is soju.im/webpush. Docs say where to add it when one does.

This branch was previously deployed

1 inactive deployment
github-pages — 2b6032d7 Deployed Nov 8, 2026 by rdrake via deploy #177
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.

2 participants