docs: fix false session persistence claim for registered accounts
All checks were successful
check / check (push) Successful in 1m4s

The README incorrectly claimed that registered sessions persist across
logouts. The actual code (handleQuit and cleanupUser in api.go) deletes
ALL sessions unconditionally when the last client disconnects — no check
for password_hash.

Updated Data Lifecycle, Registered Accounts, Identity & Sessions rationale,
flow diagram, and Design Principles to accurately state that both anonymous
and registered sessions are deleted on QUIT or last-client-logout.
Registration enables multi-client access (login from another device while
session is active), not session persistence across all-client removal.
This commit is contained in:
clawbot
2026-03-17 02:45:43 -07:00
parent 0900289af5
commit 2c6e4673c1

View File

@@ -115,7 +115,7 @@ Everything else is IRC. `PRIVMSG`, `JOIN`, `PART`, `NICK`, `TOPIC`, `MODE`,
Joining a nonexistent channel creates it. Channels disappear when empty. Nicks Joining a nonexistent channel creates it. Channels disappear when empty. Nicks
are unique per server. Identity starts with a key — a nick is a display name. are unique per server. Identity starts with a key — a nick is a display name.
Accounts are optional: you can create an anonymous session instantly, or Accounts are optional: you can create an anonymous session instantly, or
register with a password to persist your identity across sessions. register with a password for multi-client access to a single session.
### On the resemblance to JSON-RPC ### On the resemblance to JSON-RPC
@@ -152,7 +152,7 @@ need to change.
### Identity & Sessions — Dual Authentication Model ### Identity & Sessions — Dual Authentication Model
The server supports two authentication paths: **anonymous sessions** for The server supports two authentication paths: **anonymous sessions** for
instant access, and **optional account registration** for persistent identity. instant access, and **optional account registration** for multi-client access.
#### Anonymous Sessions (No Account Required) #### Anonymous Sessions (No Account Required)
@@ -168,7 +168,7 @@ The simplest entry point. No registration, no passwords.
#### Registered Accounts (Optional) #### Registered Accounts (Optional)
For users who want persistent identity across sessions: For users who want multi-client access (multiple devices sharing one session):
- **Registration**: client sends `POST /api/v1/register` with a nick and - **Registration**: client sends `POST /api/v1/register` with a nick and
password (minimum 8 characters) → server creates a session with the password (minimum 8 characters) → server creates a session with the
@@ -178,7 +178,9 @@ For users who want persistent identity across sessions:
new client token for the existing session. This enables multi-client new client token for the existing session. This enables multi-client
access: logging in from a new device adds a client to the existing session access: logging in from a new device adds a client to the existing session
rather than creating a new one, so channel memberships and message queues rather than creating a new one, so channel memberships and message queues
are shared. are shared. Note: login only works while the session still exists — if all
clients have logged out or the user has sent QUIT, the session is deleted
and the registration is lost.
- Registered accounts cannot be logged into via `POST /api/v1/session` - Registered accounts cannot be logged into via `POST /api/v1/session`
that endpoint is for anonymous sessions only. that endpoint is for anonymous sessions only.
- Anonymous sessions (created via `/session`) cannot be logged into via - Anonymous sessions (created via `/session`) cannot be logged into via
@@ -195,10 +197,12 @@ For users who want persistent identity across sessions:
**Rationale:** IRC has no accounts. You connect, pick a nick, and talk. **Rationale:** IRC has no accounts. You connect, pick a nick, and talk.
Anonymous sessions preserve that simplicity — instant access, zero friction. Anonymous sessions preserve that simplicity — instant access, zero friction.
But some users want to keep their nick across sessions without relying on a But some users want to access the same session from multiple devices without
bouncer or nick reservation system. Optional registration with password a bouncer. Optional registration with password enables multi-client login
solves this without adding friction for casual users: if you don't want an without adding friction for casual users: if you don't want an account,
account, don't create one. Identity verification at the message layer via don't create one. Note: in the current implementation, both anonymous and
registered sessions are deleted when the last client disconnects (QUIT or
logout); registration does not make a session survive all-client removal. Identity verification at the message layer via
cryptographic signatures (see [Security Model](#security-model)) remains cryptographic signatures (see [Security Model](#security-model)) remains
independent of account registration. independent of account registration.
@@ -430,13 +434,14 @@ The entire read/write loop for a client is two endpoints. Everything else
│ │ │ │
│ ... use the API normally (JOIN, PRIVMSG, poll, etc.) ... │ │ ... use the API normally (JOIN, PRIVMSG, poll, etc.) ... │
│ │ │ │
│ (Later, from a new device or after token expiry) │ (From another device, while session is still active)
│ │ │ │
│ 2. POST /api/v1/login │ │ 2. POST /api/v1/login │
│ {"nick":"alice", "password":"s3cret!!"} │ │ {"nick":"alice", "password":"s3cret!!"} │
│ → {"id":1, "nick":"alice", "token":"d4e5f6..."} │ │ → {"id":1, "nick":"alice", "token":"d4e5f6..."} │
│ (New client added to existing session — channels │ │ (New client added to existing session — channels │
│ and message queues are preserved) │ and message queues are preserved. If all clients
│ have logged out, session no longer exists.) │
│ │ │ │
└────────────────────────────────────────────────────────────┘ └────────────────────────────────────────────────────────────┘
``` ```
@@ -1977,14 +1982,19 @@ skew issues) and simpler than UUIDs (integer comparison vs. string comparison).
- **Client output queue entries**: Pruned automatically when older than - **Client output queue entries**: Pruned automatically when older than
`QUEUE_MAX_AGE` (default 30 days). `QUEUE_MAX_AGE` (default 30 days).
- **Channels**: Deleted when the last member leaves (ephemeral). - **Channels**: Deleted when the last member leaves (ephemeral).
- **Sessions**: Anonymous sessions are deleted on `QUIT` or when all clients - **Sessions**: Both anonymous and registered sessions are deleted on `QUIT`
have logged out. Registered sessions persist across logouts — the session or when the last client logs out (`POST /api/v1/logout` with no remaining
remains so the user can log in again later. Idle sessions (both anonymous clients triggers session cleanup). There is no distinction between session
and registered) are automatically expired after `SESSION_IDLE_TIMEOUT` types in the cleanup path — `handleQuit` and `cleanupUser` both call
`DeleteSession` unconditionally. Idle sessions are automatically expired
after `SESSION_IDLE_TIMEOUT`
(default 30 days) — the server runs a background cleanup loop that parts (default 30 days) — the server runs a background cleanup loop that parts
idle users from all channels, broadcasts QUIT, and releases their nicks. idle users from all channels, broadcasts QUIT, and releases their nicks.
- **Clients**: Individual client tokens are deleted on `POST /api/v1/logout`. - **Clients**: Individual client tokens are deleted on `POST /api/v1/logout`.
A session can have multiple clients; removing one doesn't affect others. A session can have multiple clients; removing one doesn't affect others.
However, when the last client is removed (via logout), the entire session
is deleted — the user is parted from all channels, QUIT is broadcast, and
the nick is released.
--- ---
@@ -2138,7 +2148,7 @@ export TOKEN=$(curl -s -X POST http://localhost:8080/api/v1/session \
-H 'Content-Type: application/json' \ -H 'Content-Type: application/json' \
-d '{"nick":"testuser"}' | jq -r .token) -d '{"nick":"testuser"}' | jq -r .token)
# 1b. Or register an account (persistent identity) # 1b. Or register an account (multi-client support)
export TOKEN=$(curl -s -X POST http://localhost:8080/api/v1/register \ export TOKEN=$(curl -s -X POST http://localhost:8080/api/v1/register \
-H 'Content-Type: application/json' \ -H 'Content-Type: application/json' \
-d '{"nick":"testuser","password":"mypassword"}' | jq -r .token) -d '{"nick":"testuser","password":"mypassword"}' | jq -r .token)
@@ -2577,9 +2587,9 @@ neoirc/
2. **Accounts optional** — anonymous sessions are instant: pick a nick and 2. **Accounts optional** — anonymous sessions are instant: pick a nick and
talk. No registration, no email verification. The cost of entry is a talk. No registration, no email verification. The cost of entry is a
hashcash proof, not bureaucracy. For users who want persistent identity, hashcash proof, not bureaucracy. For users who want multi-client access
optional account registration with password is available — but never (multiple devices sharing one session), optional account registration
required. Identity verification at the message layer uses cryptographic with password is available — but never required. Identity verification at the message layer uses cryptographic
signing, independent of account status. signing, independent of account status.
3. **IRC semantics over HTTP** — command names and numeric codes from 3. **IRC semantics over HTTP** — command names and numeric codes from