Skip to content

Client session resume (via LoC/SASL and draft/resume-0.5) - #95

Open
empus wants to merge 4 commits into
UndernetIRC:mainfrom
empus:feat/resume
Open

Client session resume (via LoC/SASL and draft/resume-0.5)#95
empus wants to merge 4 commits into
UndernetIRC:mainfrom
empus:feat/resume

Conversation

@empus

@empus empus commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds same-server, in-memory IRCv3 session resume for secure connections. An eligible client that loses its transport is held in a limbo state for a grace window instead of quitting, and can reattach to the same session — keeping its nick, account, user modes, oper privileges, and channels — with no visible quit/rejoin to the rest of the network. Primarily designed to survive edge reployments or other short-lived interruptions of WebSocket connections.

Off by default (RESUME feature flag); no behavior change unless explicitly enabled.

What it does

  • draft/resume-0.5 capability with a bearer token (128-bit id + 256-bit secret, URL-safe base64), rotated on every resume, constant-time secret comparison. Tokens are never logged or shown.
  • Detach triggers: BRB command for voluntary suspend; automatic detach on unexpected transport loss (EOF, reset, TLS error, abnormal WebSocket close); optional detach on ping timeout (RESUME_DETACH_PINGOUT) for silent losses a proxy never propagates.
  • Account-based auto-reattach (RESUME_AUTO_ACCOUNT): an authenticated client (SASL, login-on-connect, or a services login after connecting) returning with the same nick+account reattaches with no token and no client support — the nick collision is deferred through registration. Per-account opt-out via the 0x080 account flag.
  • State preserved across the connection swap: user modes, oper privileges/snomask, class sendq/flood limits, and pre-detach away.
  • On resume: the client's own view is rebuilt (welcome burst, modes, away, per-channel JOIN/topic/NAMES) and RPL_LOGGEDIN is re-sent for authenticated sessions so a token-path resumer re-learns its account.

Eligibility & limits

  • Requires TLS. RESUME_REQUIRE_WEBSOCKET (default on) further restricts to secure WebSockets.
  • Detached sessions capped by RESUME_MAX_DETACHED; grace window by RESUME_TIMEOUT (10–300s).
  • WHOIS shows a detached session to opers/self; detach/resume/expiry operator notices via RESUME_SERVER_NOTICES.

Testing

  • Python integration tests under tests/pr_resume/ covering detach, resume, account auto-reattach, auto-detach, and the secure-WebSocket requirement.
  • Docker test configs and iauth stubs included.

Docs

doc/readme.resume (feature guide), doc/readme.features, and doc/example.conf.

empus added 4 commits August 1, 2026 21:45
Add same-server, in-memory IRCv3 session resume for secure connections: an
eligible client that loses its transport is held for a grace window instead of
quitting, and may reattach keeping its nick, account, modes, and channels.

- draft/resume-0.5 capability with bearer token (128-bit id + 256-bit secret,
  URL-safe base64), rotated on every resume; constant-time secret compare.
- BRB for voluntary suspend; auto-detach on unexpected transport loss (EOF,
  reset, TLS error, abnormal WebSocket close); optional detach on ping timeout
  (RESUME_DETACH_PINGOUT) for silent losses a proxy never propagated.
- Account-based auto-reattach (RESUME_AUTO_ACCOUNT): an authenticated client
  (SASL, login-on-connect, or a services login after connecting) that returns
  with the same nick+account reattaches with no token and no client support;
  the nick collision is deferred through registration. Per-account opt-out via
  the 0x080 account flag.
- Preserves user modes/oper privileges/snomask/class limits across the
  connection swap; restores pre-detach away.
- Eligibility requires TLS; RESUME_REQUIRE_WEBSOCKET (default TRUE) further
  restricts to secure WebSockets. Detached sessions capped by
  RESUME_MAX_DETACHED; window by RESUME_TIMEOUT (10-300s).
- WHOIS shows a detached session to opers/self; detach/resume/expiry notices
  via RESUME_SERVER_NOTICES; tokens never logged or shown.
- Off by default (RESUME). Docs in doc/readme.resume and doc/readme.features;
  tests under tests/pr_resume/.
…efer iauth nick

- Re-send RPL_LOGGEDIN (900) during replay for an authenticated session that
  resumed via token. A token-path resumer never SASLs on the new connection,
  so it never received 900 and could not tell it was still logged in -- it
  would retry SASL on the next CAP NEW, which fails when services are down
  (the very case where a server-local token still lets it back in). Gated on
  the resumer not having authenticated on this connection, so account-path
  resumers are not sent a duplicate.

- Flush the replay burst with send_queued() before restoring the SendQ
  ceiling, so a large rebuild cannot leave the queue backed up above the
  class limit and trip "Max SendQ exceeded" on the client's next message.
  Document that the ceiling is enforced at queue time, not at dispatch.

- Withhold the deferred resume nick from iauth until it is actually
  committed, and forward it only once the client keeps it, so iauth/dronescan
  never evaluate a nick the client may not end up holding.
Carry state that must survive detach->resume across the connection swap and
clean up the transient's registration state, so a resumed session no longer
leaks or corrupts server-side bookkeeping:

- Drop the transient's in-flight SASL cookie and timer before the swap frees
  it, so the SASL session table / timer can't dangle at a freed client (H0).
- Carry the accumulated nick-change penalty (con_nextnick), closing a
  BRB-reconnect nick-flood bypass (M10).
- Move the session's conf attachments (incl. any Operator block) onto the live
  connection so oper-class link accounting stays balanced (L20).
- Carry the sockhost and byte counters so the local-count bucket balances and
  session traffic isn't lost to is_ni (L21).
- Tell iauth the detached fd is gone, so it doesn't leak a record on a
  reusable fd (L19).
…etes

A nick deferred for account-based reattach was withheld from iauth. iauth
(login-on-connect) needs the client's nick to finish its registration handshake
and send its verdict. The account is derived from the username, but it isn't
applied to the client until iauth replies -- and iauth won't reply without the
nick. So the account was never set, the reattach (which needs it) never fired,
and the reconnecting client hung. Forward the nick to iauth even while deferred;
it still stays out of the client nick-hash.
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