Skip to content

feat(server): client list shows when each paired client was last seen - #26

Merged
r4iju merged 3 commits into
mainfrom
feat/client-last-seen
Sep 12, 2026
Merged

r4iju merged 3 commits into
mainfrom
feat/client-last-seen

Conversation

@r4iju

@r4iju r4iju commented Sep 12, 2026

Copy link
Copy Markdown
Owner

Closes #24.

What was wrong

Settings → Connections only knew when a client last opened a socket (lastConnectedAt), and only showed it in a hover tooltip. A client that stayed connected for three weeks and then dropped read as "last connected three weeks ago", and HTTP-only traffic never moved it. Deciding what to revoke needs "last seen".

How it's fixed

  • auth_sessions.last_seen_at (migration 051, nullable; existing rows read as null). The update is monotonic and skips revoked rows.
  • SessionStore writes it on socket open and on the last socket close, and a verified HTTP request advances it at most once per hour per session (in-memory coalescing map, pruned when a session is removed). Each write publishes clientUpserted on the access stream so an open Connections page updates.
  • AuthClientSession.lastSeenAt on the contract; the web client row shows "Connected" / "Last seen 2h ago" / "Not seen yet" inline; t3 auth listing prints last seen: in text and JSON.

Display signal only — #19's inactivity deadline keys on explicit activity, not traffic, and stays a separate field.

Verification

  • Controlled-clock SessionStore tests written first: open/close writes, coalesced HTTP touches within and across the hour, stream emit only when the value changes, revoked session never advances. Web clientSessionPresenceLabel tests; CLI listing test.
  • vp test run on server auth/persistence/HTTP suites (317 passing; one pre-existing ServerSecretStore race flake fails intermittently on clean main too), web presence tests; typecheck clean on server, web, mobile, client-runtime.
  • No screenshot of the row yet — the change is one inline text segment; say the word and I'll attach one from a local run.

🤖 Generated with Claude Code

The Connections page only knew when a client last opened a socket, and
only in a tooltip, so a client that stayed connected for weeks and then
dropped read as weeks stale while HTTP-only clients never moved at all.

Auth sessions now persist last_seen_at: socket open and close always
write it, and a verified HTTP request advances it at most once an hour.
The access stream publishes the update, the web client row shows
"Connected" or "Last seen 2h ago" inline, and the CLI listing prints it.
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L labels Sep 12, 2026
Review follow-ups for the last-seen column:

- SessionStore.verify no longer bumps last seen. EnvironmentAuth records
  it after the DPoP branch, so a proof-bound token sent without a valid
  proof is rejected without advancing the client or publishing an update.
- AuthClientSession.lastSeenAt decodes to null when absent, so a newer
  web or mobile client still reads the access stream from an older server.
- Migration 051 gets the same column test as 041.
@r4iju

r4iju commented Sep 12, 2026

Copy link
Copy Markdown
Owner Author

Follow-up from the fresh review, all three findings applied:

  • Last seen now runs after the DPoP check. SessionStore.verify no longer touches it; EnvironmentAuth.authenticateRequest calls the new recordSeen once the request has cleared every credential check. New test: a proof-bound token sent without a proof is rejected and its lastSeenAt stays null, while a plain bearer request advances it.
  • lastSeenAt is decode-tolerant (withDecodingDefault(null)), so a newer web/mobile client still decodes the access stream from a server that predates the column.
  • Migration 051 has a column test mirroring 041.

Server/contracts/web typecheck clean; touched suites 35/35.

@r4iju
r4iju merged commit aa6494e into main Sep 12, 2026
@r4iju
r4iju deleted the feat/client-last-seen branch September 12, 2026 01:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Client list shows when each paired client was last seen

1 participant