Skip to main content

Architecture: Share with Everyone

Date: 2026-03-03

Decision

Activate the existing is_public boolean field end-to-end rather than introducing a new sharing mechanism. This approach:

  • Requires zero schema migration (field already exists with default false)
  • Maintains backward compatibility
  • Follows the same API pattern as user/team sharing
AlternativeProsConsDecision
Activate is_public fieldNo migration, simple, consistentBinary (all or nothing)Chosen
"Organization" pseudo-teamReuses team modelRequires special team management, confusing UXRejected
Separate public endpointClean separationAPI duplication, extra routes to maintainRejected

Access Level Decision

Public access grants the same full access (read + write) as user/team sharing. Users who access a public conversation can both view messages and send new ones. Read-only enforcement for public viewers was considered but deferred — the current design matches the existing shared conversation behavior where all shared users can participate.

Solution Architecture

Access Check Flow

requireConversationAccess(conversationId, userId)

├─ Owner? ──────────────── ✅ GRANT

├─ is_public? ─────────── ✅ GRANT (new)

├─ shared_with user? ──── ✅ GRANT

├─ shared_with_teams? ─── ✅ GRANT (if member)

├─ sharing_access? ────── ✅ GRANT (if record exists)

└─ ────────────────────── ❌ DENY

API Changes

POST /api/chat/conversations/[id]/share

New: accepts is_public as a standalone sharing action:

{ "is_public": true }

Previously required user_emails or team_ids with permission. Now accepts any combination of:

  • is_public (standalone toggle)
  • user_emails + permission
  • team_ids + permission

Query Changes

Both conversation listing and shared listing endpoints add a public condition:

// GET /api/chat/conversations
const ownershipConditions = [
{ owner_id: user.email },
{ 'sharing.shared_with': user.email },
{ 'sharing.is_public': true }, // ← new
// + team conditions if applicable
];

// GET /api/chat/shared
const sharedConditions = [
{ 'sharing.shared_with': user.email },
{ 'sharing.is_public': true }, // ← new
// + team conditions if applicable
];

UI Changes

ShareDialog — New toggle switch between the copy-link section and the people/teams search:

┌─────────────────────────────────────────┐
│ Share Conversation │
│ My Conversation Title │
├─────────────────────────────────────────┤
│ Share Link [https://...] [Copy] │
├─────────────────────────────────────────┤
│ 🌐 Share with everyone [━━●] │ ← new toggle
│ Anyone in the organization... │
├─────────────────────────────────────────┤
│ People, Teams │
│ [Search by email or team name...] │
├─────────────────────────────────────────┤
│ Access (Everyone) │
│ 🌐 Everyone — All org members Can view│ ← new entry
│ 👤 user@example.com [🗑] │
│ 👥 Platform Engineering [🗑] │
└─────────────────────────────────────────┘

Sidebar — Differentiated sharing indicator:

  • Public: Green Globe icon with "Shared with everyone" tooltip
  • Private share: Blue Users icon with "Shared conversation" tooltip

Components Changed

FileChange
ui/src/types/mongodb.tsAdded is_public?: boolean to ShareConversationRequest
ui/src/lib/api-middleware.tsrequireConversationAccess checks is_public after owner check
ui/src/app/api/chat/conversations/[id]/share/route.tsHandles is_public toggle; relaxed validation for standalone toggle
ui/src/app/api/chat/conversations/route.tsAdded { 'sharing.is_public': true } to $or conditions
ui/src/app/api/chat/shared/route.tsAdded { 'sharing.is_public': true } to $or conditions
ui/src/components/chat/ShareDialog.tsxToggle switch, handleTogglePublic, "Everyone" access entry
ui/src/components/layout/Sidebar.tsxGlobe icon (green) for public, Users icon (blue) for private sharing
ui/src/app/api/__tests__/chat-sharing-teams.test.tsUpdated expectations for new is_public query condition
ui/src/app/api/__tests__/chat-sharing-public.test.tsNew — 19 tests for public sharing

Migration Notes

  • No migration requiredis_public already exists in the schema with default false
  • Backward compatible — existing sharing with users and teams is unchanged
  • Gradual adoption — public sharing is opt-in per conversation by the owner