Skip to content

fix(session-ingest): enforce current organization access#4408

Open
eshurakov wants to merge 1 commit into
mainfrom
fix/session-ingest-access-revocation
Open

fix(session-ingest): enforce current organization access#4408
eshurakov wants to merge 1 commit into
mainfrom
fix/session-ingest-access-revocation

Conversation

@eshurakov

Copy link
Copy Markdown
Contributor

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

  • Add resolveAccessibleKiloSession to the Session Ingest Worker, which joins cli_sessions_v2 with current organization_memberships and caches positive results in the per-user SessionAccessCacheDO.
  • Extend the Durable Object cache schema to store organization_id and a fixed authorization_expires_at TTL; legacy cache rows expire immediately via a generated SQLite migration.
  • Apply the access check to ingest, share, unshare, delete, and export paths; cache misses remain authoritative through Postgres.
  • Add a secret-protected POST /internal/session-access/invalidate endpoint and call it from the web app when an organization member is removed.
  • Invalidate cached entries when Cloud Agent session metadata changes organization scope or when the RPC updates an existing session mapping.
  • Preserve deprecated has/add Durable 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)
Loading

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:

  • Query Postgres on every request. Rejected because it adds measurable latency and database load for a high-frequency path.
  • Use a shared Redis/cache layer. Rejected to avoid introducing new infrastructure when the existing Durable Object already provides the required consistency model.

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

  • The generated Durable Object SQLite migration (0003_free_valkyrie.sql) expires legacy cache rows immediately by adding a non-null authorization_expires_at default of 0.
  • Deprecated has/add RPCs are kept for mixed-version deployment compatibility and should be removed once all Workers are updated.
  • The invalidation call from the web app has a 30-second timeout and is caught so member removal does not fail if Session Ingest is unavailable.
  • Deployment order follows the existing web-before-Worker pattern; cache warming during session create is best-effort and cannot fail session creation.

@kilo-code-bot

kilo-code-bot Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

Re-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 SessionAccessCacheDO cache, best-effort invalidation on member removal and org-scope changes, and the internal invalidate endpoint's secret validation and route wiring for ingest/share/unshare/delete/export were all verified correct with no new security, correctness, or reliability issues.

Files Reviewed (27 files)
  • apps/web/src/lib/cloud-agent/session-ownership.test.ts
  • apps/web/src/lib/organizations/organizations.test.ts
  • apps/web/src/lib/organizations/organizations.ts
  • apps/web/src/lib/session-ingest-client.test.ts
  • apps/web/src/lib/session-ingest-client.ts
  • apps/web/src/routers/organizations/organization-members-router.test.ts
  • apps/web/src/routers/organizations/organization-members-router.ts
  • packages/worker-utils/src/cloud-agent-session-access.ts
  • services/session-ingest/drizzle/0003_free_valkyrie.sql
  • services/session-ingest/drizzle/meta/0003_snapshot.json
  • services/session-ingest/drizzle/meta/_journal.json
  • services/session-ingest/drizzle/migrations.js
  • services/session-ingest/src/app.ts
  • services/session-ingest/src/db/sqlite-schema.ts
  • services/session-ingest/src/dos/SessionAccessCacheDO.ts
  • services/session-ingest/src/index.test.ts
  • services/session-ingest/src/ingest/metadata.ts
  • services/session-ingest/src/queue-consumer.test.ts
  • services/session-ingest/src/routes/api.test.ts
  • services/session-ingest/src/routes/api.ts
  • services/session-ingest/src/services/session-access.test.ts
  • services/session-ingest/src/services/session-access.ts
  • services/session-ingest/src/services/session-export.ts
  • services/session-ingest/src/session-ingest-rpc.ts
  • services/session-ingest/test/integration/session-access-cache-do.test.ts
  • services/session-ingest/test/test-worker.ts
  • services/session-ingest/wrangler.test.jsonc
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 Summary

Re-reviewed the full PR at HEAD 1132c3794 after the previous review's base commit was rebased away; the file set and logic are unchanged from the prior no-issues review (organization-scoped session access checks, TTL-bounded SessionAccessCacheDO cache with purge/invalidation, best-effort invalidation on member removal and org-scope changes, and the atomic share/unshare/delete-cascade queries), and no security, correctness, or reliability issues were found in the changed lines.

Files Reviewed (27 files)
  • apps/web/src/lib/cloud-agent/session-ownership.test.ts
  • apps/web/src/lib/organizations/organizations.test.ts
  • apps/web/src/lib/organizations/organizations.ts
  • apps/web/src/lib/session-ingest-client.test.ts
  • apps/web/src/lib/session-ingest-client.ts
  • apps/web/src/routers/organizations/organization-members-router.test.ts
  • apps/web/src/routers/organizations/organization-members-router.ts
  • packages/worker-utils/src/cloud-agent-session-access.ts
  • services/session-ingest/drizzle/0003_free_valkyrie.sql
  • services/session-ingest/drizzle/meta/0003_snapshot.json
  • services/session-ingest/drizzle/meta/_journal.json
  • services/session-ingest/drizzle/migrations.js
  • services/session-ingest/src/app.ts
  • services/session-ingest/src/db/sqlite-schema.ts
  • services/session-ingest/src/dos/SessionAccessCacheDO.ts
  • services/session-ingest/src/index.test.ts
  • services/session-ingest/src/ingest/metadata.ts
  • services/session-ingest/src/queue-consumer.test.ts
  • services/session-ingest/src/routes/api.test.ts
  • services/session-ingest/src/routes/api.ts
  • services/session-ingest/src/services/session-access.test.ts
  • services/session-ingest/src/services/session-access.ts
  • services/session-ingest/src/services/session-export.ts
  • services/session-ingest/src/session-ingest-rpc.ts
  • services/session-ingest/test/integration/session-access-cache-do.test.ts
  • services/session-ingest/test/test-worker.ts
  • services/session-ingest/wrangler.test.jsonc

Previous review (commit 7e750fa)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Reviewed the full PR at HEAD 7e750fa81, which adds best-effort Session Access cache invalidation in applyMetadataChanges when a session's orgId metadata changes (wrapped in try/catch, non-blocking) on top of the previously reviewed access-revocation implementation; all changes remain logically consistent with their stated intent and no security, correctness, or reliability issues were found in the changed lines.

Files Reviewed (27 files)
  • apps/web/src/lib/cloud-agent/session-ownership.test.ts
  • apps/web/src/lib/organizations/organizations.test.ts
  • apps/web/src/lib/organizations/organizations.ts
  • apps/web/src/lib/session-ingest-client.test.ts
  • apps/web/src/lib/session-ingest-client.ts
  • apps/web/src/routers/organizations/organization-members-router.test.ts
  • apps/web/src/routers/organizations/organization-members-router.ts
  • packages/worker-utils/src/cloud-agent-session-access.ts
  • services/session-ingest/drizzle/0003_free_valkyrie.sql
  • services/session-ingest/drizzle/meta/0003_snapshot.json
  • services/session-ingest/drizzle/meta/_journal.json
  • services/session-ingest/drizzle/migrations.js
  • services/session-ingest/src/app.ts
  • services/session-ingest/src/db/sqlite-schema.ts
  • services/session-ingest/src/dos/SessionAccessCacheDO.ts
  • services/session-ingest/src/index.test.ts
  • services/session-ingest/src/ingest/metadata.ts
  • services/session-ingest/src/queue-consumer.test.ts
  • services/session-ingest/src/routes/api.test.ts
  • services/session-ingest/src/routes/api.ts
  • services/session-ingest/src/services/session-access.test.ts
  • services/session-ingest/src/services/session-access.ts
  • services/session-ingest/src/services/session-export.ts
  • services/session-ingest/src/session-ingest-rpc.ts
  • services/session-ingest/test/integration/session-access-cache-do.test.ts
  • services/session-ingest/test/test-worker.ts
  • services/session-ingest/wrangler.test.jsonc

Previous review (commit 2f29745)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Reviewed the full PR at HEAD 2f297458e, including the follow-up commit that centralizes session-access invalidation inside removeUserFromOrganization (now also covering the child-organization sync removal path), bounds per-user Durable Object cache storage by purging expired rows on write, documents the timing-safe-compare branch in hasValidInternalSecret, and renames the shared session-access type to a producer-neutral AccessibleSession; all changes are logically consistent with their stated intent and no security, correctness, or reliability issues were found in the changed lines.

Files Reviewed (26 files)
  • apps/web/src/lib/cloud-agent/session-ownership.test.ts
  • apps/web/src/lib/organizations/organizations.test.ts
  • apps/web/src/lib/organizations/organizations.ts
  • apps/web/src/lib/session-ingest-client.test.ts
  • apps/web/src/lib/session-ingest-client.ts
  • apps/web/src/routers/organizations/organization-members-router.test.ts
  • apps/web/src/routers/organizations/organization-members-router.ts
  • packages/worker-utils/src/cloud-agent-session-access.ts
  • services/session-ingest/drizzle/0003_free_valkyrie.sql
  • services/session-ingest/drizzle/meta/0003_snapshot.json
  • services/session-ingest/drizzle/meta/_journal.json
  • services/session-ingest/drizzle/migrations.js
  • services/session-ingest/src/app.ts
  • services/session-ingest/src/db/sqlite-schema.ts
  • services/session-ingest/src/dos/SessionAccessCacheDO.ts
  • services/session-ingest/src/index.test.ts
  • services/session-ingest/src/queue-consumer.test.ts
  • services/session-ingest/src/queue-consumer.ts
  • services/session-ingest/src/routes/api.test.ts
  • services/session-ingest/src/routes/api.ts
  • services/session-ingest/src/services/session-access.test.ts
  • services/session-ingest/src/services/session-access.ts
  • services/session-ingest/src/services/session-export.ts
  • services/session-ingest/src/session-ingest-rpc.ts
  • services/session-ingest/test/integration/session-access-cache-do.test.ts
  • services/session-ingest/test/test-worker.ts

Previous review (commit 00d7f96)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Reviewed 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)
  • apps/web/src/lib/cloud-agent/session-ownership.test.ts
  • apps/web/src/lib/session-ingest-client.test.ts
  • apps/web/src/lib/session-ingest-client.ts
  • apps/web/src/routers/organizations/organization-members-router.test.ts
  • apps/web/src/routers/organizations/organization-members-router.ts
  • packages/worker-utils/src/cloud-agent-session-access.ts
  • services/session-ingest/drizzle/0003_free_valkyrie.sql
  • services/session-ingest/drizzle/meta/0003_snapshot.json
  • services/session-ingest/drizzle/meta/_journal.json
  • services/session-ingest/drizzle/migrations.js
  • services/session-ingest/src/app.ts
  • services/session-ingest/src/db/sqlite-schema.ts
  • services/session-ingest/src/dos/SessionAccessCacheDO.ts
  • services/session-ingest/src/index.test.ts
  • services/session-ingest/src/queue-consumer.test.ts
  • services/session-ingest/src/queue-consumer.ts
  • services/session-ingest/src/routes/api.test.ts
  • services/session-ingest/src/routes/api.ts
  • services/session-ingest/src/services/session-access.test.ts
  • services/session-ingest/src/services/session-access.ts
  • services/session-ingest/src/services/session-export.ts
  • services/session-ingest/src/session-ingest-rpc.ts
  • services/session-ingest/test/integration/session-access-cache-do.test.ts
  • services/session-ingest/test/test-worker.ts
  • services/session-ingest/wrangler.test.jsonc

Reviewed by claude-sonnet-5 · Input: 30 · Output: 9.8K · Cached: 580K

Review guidance: REVIEW.md from base branch main

@eshurakov eshurakov force-pushed the fix/session-ingest-access-revocation branch from 00d7f96 to ee0841e Compare July 7, 2026 08:37
@eshurakov eshurakov force-pushed the fix/session-ingest-access-revocation branch 2 times, most recently from 7e750fa to 1132c37 Compare July 16, 2026 13:33
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.
@eshurakov eshurakov force-pushed the fix/session-ingest-access-revocation branch from 1132c37 to f999cf6 Compare July 16, 2026 17:54
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