feat(realtime): drop a user's enclave topic subscription on access loss (LC-176) #221

Merged
nrupard merged 1 commit from feat/lc-176-topic-unsubscribe into main 2026-05-26 19:37:24 +02:00
Owner

Closes LC-176. Raised in the LC-170 review. The hub only exposed disconnect(conn_id); there was no way to drop a specific user from a single topic. So a member kicked from / who left an enclave (or whose enclave was deleted) kept their enclave:{id} topic subscription until they disconnected, and kept receiving its member/room-list OOB updates until they navigated (any navigation then 403s on the lost access). Low severity, but a clean cutoff removes the wart and de-risks the LC-170/172/174 surfaces that rely on the topic.

Changes

  • Hub::unsubscribe_topic(conn_id, topic) drops one connection from a topic (idempotent).
  • Hub::unsubscribe_user_from_topic(user_id, topic) drops every one of a user's connections. Connection ids are collected before mutating topics, so no user_conns borrow is held across the topic update.
  • post_kick, post_leave, and post_delete call unsubscribe_user_from_topic(target, "enclave:{id}") right after the member-removed broadcast - so the remaining members' fan-out still happens, then the departed user is cut off. post_delete drops every former member from the now-defunct topic.

This is the robustness backstop the LC-174 design already accounted for (its enclave-keyed nav id meant correctness did not depend on this landing first); now stale subscriptions are also actively cleaned up at the access-loss moment rather than only on disconnect.

Verification

just test and just test-saas both green (106 test result: ok each, zero failures); cargo clippy / cargo fmt clean. New hub unit tests cover the multi-tab cutoff (both of a user's tabs stop receiving while another user on the topic is unaffected) and the unknown-user no-op.

Note (environment, not code)

The dev host root disk was 100% full (Docker build cache, shared multi-project host). With operator approval I reclaimed ~48 GB of regenerable build cache + dangling images to unblock the build. No repository or volume data was touched.

Closes LC-176. Raised in the LC-170 review. The hub only exposed `disconnect(conn_id)`; there was no way to drop a specific user from a single topic. So a member kicked from / who left an enclave (or whose enclave was deleted) kept their `enclave:{id}` topic subscription until they disconnected, and kept receiving its member/room-list OOB updates until they navigated (any navigation then 403s on the lost access). Low severity, but a clean cutoff removes the wart and de-risks the LC-170/172/174 surfaces that rely on the topic. ## Changes - `Hub::unsubscribe_topic(conn_id, topic)` drops one connection from a topic (idempotent). - `Hub::unsubscribe_user_from_topic(user_id, topic)` drops every one of a user's connections. Connection ids are collected before mutating `topics`, so no `user_conns` borrow is held across the topic update. - `post_kick`, `post_leave`, and `post_delete` call `unsubscribe_user_from_topic(target, "enclave:{id}")` right after the member-removed broadcast - so the remaining members' fan-out still happens, then the departed user is cut off. `post_delete` drops every former member from the now-defunct topic. This is the robustness backstop the LC-174 design already accounted for (its enclave-keyed nav id meant correctness did not *depend* on this landing first); now stale subscriptions are also actively cleaned up at the access-loss moment rather than only on disconnect. ## Verification `just test` and `just test-saas` both green (106 `test result: ok` each, zero failures); `cargo clippy` / `cargo fmt` clean. New hub unit tests cover the multi-tab cutoff (both of a user's tabs stop receiving while another user on the topic is unaffected) and the unknown-user no-op. ## Note (environment, not code) The dev host root disk was 100% full (Docker build cache, shared multi-project host). With operator approval I reclaimed ~48 GB of regenerable build cache + dangling images to unblock the build. No repository or volume data was touched.
feat(realtime): drop a user's enclave topic subscription on access loss (LC-176)
All checks were successful
check-secrets / Nosey parker (push) Successful in 2s
check-secrets / TruffleHog (push) Successful in 4s
check-secrets / Kingfisher (push) Successful in 5s
check-secrets / Nosey parker (pull_request) Successful in 3s
check-secrets / TruffleHog (pull_request) Successful in 4s
check-secrets / Kingfisher (pull_request) Successful in 5s
Check / clippy + fmt + tests (pull_request) Successful in 1m34s
Create release / Create release from merged PR (pull_request) Has been skipped
9e3457b3b5
Raised in the LC-170 review. The hub only had disconnect(conn_id); there was no way to drop a specific user from one topic. So a member kicked from / who left an enclave (or whose enclave was deleted) kept their enclave:{id} topic subscription until they disconnected, and kept receiving its member/room-list OOB updates until they navigated (any navigation then 403s on the lost access). Low severity, but a clean cutoff removes the wart.

- Hub::unsubscribe_topic(conn_id, topic) drops one connection; Hub::unsubscribe_user_from_topic(user_id, topic) drops every one of a user's connections (conn ids collected before mutating `topics` so no user_conns borrow is held across the update).
- post_kick, post_leave, and post_delete now call unsubscribe_user_from_topic(target, "enclave:{id}") right after the member-removed broadcast (so the remaining members' fan-out still happens, then the departed user is cut off). post_delete drops every former member from the now-defunct topic.

Unit tests cover the multi-tab cutoff (both of a user's tabs stop receiving while another user on the topic is unaffected) and the unknown-user no-op.

Note (environment): the dev host root disk was 100% full (Docker build cache); reclaimed ~48 GB of regenerable build cache + dangling images with operator approval to unblock the build. No repo or volume data touched.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
nrupard deleted branch feat/lc-176-topic-unsubscribe 2026-05-26 19:37:25 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/lets-chat!221
No description provided.