Skip to content

feat(api): Authorize private transcripts for participants#981

Merged
dcramer merged 24 commits into
mainfrom
feat/private-transcript-owner-access
Jul 23, 2026
Merged

feat(api): Authorize private transcripts for participants#981
dcramer merged 24 commits into
mainfrom
feat/private-transcript-owner-access

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Private conversation REST resources now consistently return isParticipant across feed, detail, People, and Location responses. A participant is a signed-in viewer whose verified email matches the owning root conversation actor's persisted verified email. The same resolved access value controls title, channel, and transcript exposure, so unmatched viewers remain redacted and transcript expiry still takes precedence.

Conversation roots are persisted once: roots self-reference and descendants copy their parent's existing root. Request-time access starts from the bounded set of requested conversation IDs and follows indexed conversation, root, destination, and identity keys; it does not recursively walk history or scan conversations for an email. There is no lineage fallback: malformed roots fail closed, and new child writes reject parents without a persisted root. The migration backfills existing trees before adding the root foreign key and index.

Top-level conversation summaries, details, System statistics, People statistics, and Location metrics include usage and duration from all persisted descendants even when the client has not loaded those child resources. Conversation counts and statuses remain resource-level, while a directly requested child reports only its own usage. Child resources remain independently addressable for lazy UI loading.

Junior-owned dashboard REST routes now declare strict Zod success schemas and serialize through one response boundary; expected failures use one strict JSON error contract. Resource contracts live under api/schema/{conversation,person,location,common}.ts, and embedded conversation objects all use the canonical conversation summary schema so participation and future metadata cannot drift by endpoint. Responses remain resource-native rather than adding a generic envelope, and dashboard reads and mutations validate the same schemas.

Requested by David Cramer via Junior.

--

View Junior Session

@vercel

vercel Bot commented Jul 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
junior-docs Ready Ready Preview Jul 23, 2026 6:58am

Request Review

Comment thread packages/junior/src/api/people/shared.ts Outdated
Comment thread packages/junior/src/api/conversations/list.ts Outdated
@dcramer dcramer changed the title feat(dashboard): Authorize private transcripts for participants feat(api): Authorize private transcripts for participants Jul 22, 2026
Comment thread packages/junior/src/api/locations/query.ts Outdated
Comment thread packages/junior/src/chat/conversations/sql/privacy.ts Outdated
Comment thread packages/junior/src/api/conversations/usage.ts
sentry-junior Bot and others added 23 commits July 22, 2026 23:20
Co-Authored-By: David Cramer <david@sentry.io>
Resolve participant access from verified normalized emails on the bounded conversation lineage. Return isParticipant in detail responses and keep malformed lineage fail-closed.
Make isParticipant part of the shared REST conversation schema and derive it for feed, People, and Location summaries with a direct verified identity comparison.
Persist each conversation's privacy root so REST detail reads use direct primary-key joins. Backfill existing trees once during migration and leave malformed lineages fail-closed.
Roll top-level conversation usage and model breakdowns across persisted descendants, including children absent from the current transcript projection. Scope reads through an indexed root relation while direct child resources retain their own usage.
Co-Authored-By: David Cramer <david@sentry.io>
Co-Authored-By: David Cramer <david@sentry.io>
Require top-level conversations to self-reference before participant authorization, and keep child summary privacy rooted in the owning conversation. Auth-disabled dashboard sessions no longer grant participant access.

Keep the REST contract documented and cover cross-root, child-actor, migration, and PostgreSQL fixture behavior.
Co-Authored-By: David Cramer <david@sentry.io>
Resolve participant access once for bounded conversation IDs and use the same projection across feed, detail, People, and Location responses.

Aggregate descendant usage in PostgreSQL, keep root counts stable, and reject child writes whose parent has no persisted root.
Use component counters before provider totals in SQL aggregates, matching the canonical tree rollup and avoiding inconsistent REST statistics.
Co-Authored-By: David Cramer <david@sentry.io>
Validate every dashboard REST success response at route registration and return expected errors through one strict JSON schema.

Centralize resource schemas and use the canonical conversation summary in People profiles so conversation metadata cannot drift across endpoints.
Reuse the profile tree-metric join for directory totals so duration and usage agree without double-counting same-actor descendants.
Exercise the root backfill through the Drizzle migration chain and verify the missing-root guard through the current store API after rebasing.
Exercise every top-level reporting route through the HTTP boundary and parse each response with its exported Zod schema.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 59edb90. Configure here.

Comment thread packages/junior/src/api/conversations/list.ts
Avoid returning descendant-only totals when a conversation has an invalid root reference. List serializers now fall back to the row's own metrics consistently with the detail endpoint.
@dcramer
dcramer merged commit 24bbe5a into main Jul 23, 2026
39 of 41 checks passed
@dcramer
dcramer deleted the feat/private-transcript-owner-access branch July 23, 2026 15:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk: high PR risk score: high

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant