fix(session-ingest): enforce current organization access#4408
Conversation
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Executive SummaryRe-reviewed the full PR (the previous review's base commit was rebased away) using 6 parallel sub-agents sharded across all 27 changed files; the organization-scoped session access checks, TTL-bounded Files Reviewed (27 files)
Previous Review Summaries (4 snapshots, latest commit 1132c37)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit 1132c37)Status: No Issues Found | Recommendation: Merge Executive SummaryRe-reviewed the full PR at HEAD Files Reviewed (27 files)
Previous review (commit 7e750fa)Status: No Issues Found | Recommendation: Merge Executive SummaryReviewed the full PR at HEAD Files Reviewed (27 files)
Previous review (commit 2f29745)Status: No Issues Found | Recommendation: Merge Executive SummaryReviewed the full PR at HEAD Files Reviewed (26 files)
Previous review (commit 00d7f96)Status: No Issues Found | Recommendation: Merge Executive SummaryReviewed the organization-membership access-revocation changes across the Session Ingest Worker, its Durable Object cache, the shared session-access query helper, and the web app's invalidation call and member-removal flow with high confidence; the new authorization checks, TTL-bounded cache, and best-effort invalidation paths are logically consistent with the stated design and no security, correctness, or reliability issues were found in the changed lines. Files Reviewed (25 files)
Reviewed by claude-sonnet-5 · Input: 30 · Output: 9.8K · Cached: 580K Review guidance: REVIEW.md from base branch |
00d7f96 to
ee0841e
Compare
7e750fa to
1132c37
Compare
Scope session-ingest operations to the user's current organization and revoke access when org membership changes. Adds resolveAccessibleKiloSession and a putValidated cache entry carrying organizationId, used across the session delete/ingest/share/unshare routes. Moves the org member removal rowCount check ahead of the audit log so failures bail early. Addresses review findings on access revocation.
1132c37 to
f999cf6
Compare
Summary
Why
Session Ingest operations (ingest, share, unshare, delete, and export) previously verified only that a session row existed for the requesting user. They did not check whether the user still has access to the owning organization, so a removed member could continue to publish events or manage sessions until their token expired. This change makes current organization membership part of the authorization decision.
What was done
resolveAccessibleKiloSessionto the Session Ingest Worker, which joinscli_sessions_v2with currentorganization_membershipsand caches positive results in the per-userSessionAccessCacheDO.organization_idand a fixedauthorization_expires_atTTL; legacy cache rows expire immediately via a generated SQLite migration.POST /internal/session-access/invalidateendpoint and call it from the web app when an organization member is removed.has/addDurable Object RPCs as no-ops for mixed-version rollout compatibility.High-level architecture
sequenceDiagram participant Client as CLI / Extension participant Web as Web App (tRPC) participant SIW as Session Ingest Worker participant DO as SessionAccessCacheDO participant PG as Postgres (Hyperdrive) Client->>SIW: ingest / share / unshare / delete SIW->>DO: getAccess(sessionId) alt cache hit DO-->>SIW: cached organizationId else cache miss SIW->>PG: query session + current membership PG-->>SIW: accessible session SIW->>DO: putValidated(sessionId, organizationId) end SIW-->>Client: result Web->>Web: remove organization member Web->>SIW: POST /internal/session-access/invalidate SIW->>DO: invalidateOrganization(organizationId)Architecture decision
Decision: Cache positive authorization results in a per-user Durable Object with a fixed 60-second TTL and best-effort invalidation.
Context: Session Ingest is on the hot path for every CLI/extension event. A Postgres lookup on every request adds latency and load, but checking only the session row would miss organization membership revocation.
Rationale: The Durable Object is already part of the Session Ingest architecture and provides strongly consistent storage per user. A fixed TTL bounds the worst-case staleness, while Postgres remains authoritative on cache misses.
Alternatives considered:
Consequences: Ingest paths avoid most Postgres authorization lookups; removed members lose access within the 60-second TTL window (or immediately on successful invalidation). Best-effort invalidation means membership removal does not fail if the cache is unreachable.
Verification
No manual verification was performed. Behavior is covered by Session Ingest unit tests, Worker integration tests, and targeted web tests.
Visual Changes
N/A
Reviewer Notes
0003_free_valkyrie.sql) expires legacy cache rows immediately by adding a non-nullauthorization_expires_atdefault of0.has/addRPCs are kept for mixed-version deployment compatibility and should be removed once all Workers are updated.