From 7a51b109df0575f570cc3351d18552d43e2f1e9f Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Sun, 12 Jul 2026 23:31:31 +0800 Subject: [PATCH] docs: document intentional PUBLIC_WORKSPACE_OWNER_ID upload quarantine Anonymous "public" uploads pool under the non-null PUBLIC_WORKSPACE_OWNER_ID and are deliberately excluded from anonymous viewing (withOwnerReadScope) and RAG retrieval (null-owner sentinel) until an operator reviews and promotes them to owner_id IS NULL via scripts/promote-public-documents.ts. This is the documented public-workspace model (TEN-N3 in docs/tenancy-defense-in-depth-review.md). Add clarifying comments so the behavior is not mistaken for a bug and "fixed" in a way that exposes unvetted anonymous uploads to logged-out visitors or the clinical answer corpus. Comments only; no behavior change. Co-Authored-By: Claude Opus 4.8 --- src/app/api/upload/route.ts | 6 ++++++ src/lib/public-api-access.ts | 11 ++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/app/api/upload/route.ts b/src/app/api/upload/route.ts index 410715ef5..ab1d0f1f0 100644 --- a/src/app/api/upload/route.ts +++ b/src/app/api/upload/route.ts @@ -91,6 +91,12 @@ export async function POST(request: Request) { supabase = createAdminClient(); const adminSupabase = supabase; const access = await publicAccessContext(request, adminSupabase); + // Anonymous ("public") uploads are pooled under the non-null PUBLIC_WORKSPACE_OWNER_ID as a + // moderation quarantine: pooled documents are intentionally NOT anonymously viewable and NOT in + // RAG retrieval (both gated on owner_id IS NULL) until an operator reviews and promotes them via + // scripts/promote-public-documents.ts (or the promote migration). This is the documented + // public-workspace model — see TEN-N3 in docs/tenancy-defense-in-depth-review.md and + // withOwnerReadScope in src/lib/public-api-access.ts. Do not make pooled uploads public here. const uploadOwnerId = access.ownerId ?? (publicUploadsEnabled() ? publicWorkspaceOwnerId() : null); if (!uploadOwnerId) { return NextResponse.json({ error: "Public uploads are not configured for this workspace." }, { status: 503 }); diff --git a/src/lib/public-api-access.ts b/src/lib/public-api-access.ts index 330f75e80..2d238cf77 100644 --- a/src/lib/public-api-access.ts +++ b/src/lib/public-api-access.ts @@ -59,7 +59,16 @@ type OwnerScopedQuery = { or(filters: string): T; }; -/** Scope reads to public rows (owner_id IS NULL) and, when signed in, the caller's owned rows. */ +/** + * Scope reads to public rows (owner_id IS NULL) and, when signed in, the caller's owned rows. + * + * Anonymous callers intentionally see ONLY the public corpus (owner_id IS NULL). Documents pooled + * under PUBLIC_WORKSPACE_OWNER_ID by anonymous uploads (see upload/route.ts) are a deliberate + * moderation quarantine: they stay out of anonymous viewing here — and out of RAG retrieval, which + * is gated separately on owner_id IS NULL — until an operator reviews and promotes them to + * owner_id IS NULL via scripts/promote-public-documents.ts (or the 20260706120000 promote + * migration). Do not union the pool owner in here without making that content-moderation decision. + */ export function withOwnerReadScope>(query: T, ownerId: string | undefined): T { if (ownerId) return query.or(`owner_id.eq.${ownerId},owner_id.is.null`); return query.is("owner_id", null);