PRD: Debug Admin Access and Generation Deletion
Objective
Make PATTTTERNS /debug admin-only, including localhost, while keeping the production site on static export and Gemini-backed regeneration ready for future paid or selected users. Replace debug “hide generation” with an admin-only hard delete flow that keeps version numbering monotonic and removes the current table 404/performance noise.
Production stays static-first (npm run build → out) for Netlify free-tier limits. /debug is a static shell with a client gate; authorization for data and mutations is enforced by Netlify Functions (and by local Next debug APIs in next dev only).
The first supported admin mechanism is a server-side environment variable allowlist:
DEBUG_ADMIN_EMAILS=pablo.armentano@gmail.com
The allowlist is comma-separated, case-insensitive, and trimmed. Do not hardcode admin emails in client code.
This PRD intentionally separates debug admin from component generator access. Admins can access operational/debug tools. Future paid or selected users may regenerate components without receiving /debug or destructive storage permissions.
Current Behavior
src/app/debug/layout.tsxwraps debug pages inDebugAuthGate, but the guard is client-side.src/components/debug/DebugAuthGate.tsxcurrently lets localhost through withlocalhost (auth skipped)and lets any authenticated production user through.src/lib/debug-supabase-auth.tscurrently returns a syntheticLocal Devuser for localhost before validating Supabase auth.netlify/functions/lib/components-auth.mtscurrently accepts any valid Supabase user for components API mutations./debugresponses havenoindex/no-storeheaders innetlify.toml, but those headers are not authorization.ComponentsGenerationsTablerenders many patternLinks; Next prefetch can request missing static-export RSC sidecars such as__next._tree.txt.- Some generation rows keep old Blobs slugs, producing plain
/patterns/...404s from stale pattern links. - Hide feels slow because the UI waits for a full
op=listrefresh after a single version mutation.
Authorization Requirements
/debugand/debug/*are a static shell with a client auth/admin gate. Do not switch the site to a Next runtime or publish.next.- Sensitive reads and all mutations are evaluated per request on Functions (production) or local debug APIs (
next dev). - Localhost follows the same admin email allowlist as production.
- Unauthenticated users see the sign-in path.
- Signed-in non-admin users see a clear admin-required state.
- Signed-in admins can use the debug UI and debug component actions.
- Server-side debug APIs that mutate or expose debug-only local data enforce the same admin allowlist.
- Public component code reads used by pattern preview/export (
op=active,op=code) remain public. - Existing noindex/no-store headers remain.
- Future enhancement: support Supabase
app_metadata.role === "admin"in addition to email allowlist.
Capability Model
Do not model all component mutations as permanently admin-only. Use separate capabilities:
debug_admin: can access/debug, list all generations, delete/hide generations, rebuild stored TSX, publish local artifacts, manage seeds, and inspect debug-only data.component_generator: can call Gemini-backed generate/regenerate flows.
Initial rollout:
debug_adminis controlled byDEBUG_ADMIN_EMAILS.component_generatormay initially delegate todebug_admin.
Future rollout:
component_generatormay include paid users, selected email allowlists, beta flags, or Supabase metadata without granting/debugaccess or destructive storage permissions.
Suggested server helpers:
requireAuthUser()
requireDebugAdminUser()
requireComponentGeneratorUser()
Suggested components API mapping:
op=whoami→requireDebugAdminUser()op=generate→requireComponentGeneratorUser()op=rebuild→requireDebugAdminUser()op=publish→requireDebugAdminUser()op=put-seed→requireDebugAdminUser()op=hide/op=delete→requireDebugAdminUser()op=list→requireDebugAdminUser()when it lists all generations globallyop=active/op=code→ publicop=versions→ public for single-pattern preview needs, or admin when used by global debug UI
Static Debug Requirements
Keep /debug on the static-export contract:
- Continue publishing
/debugas static HTML fromout/. - Gate the UI in the client (
DebugAuthGate): unauthenticated users see sign-in; signed-in non-admins see admin-required. - Probe admin access with a cheap Function/
next devwhoamicheck. Do not embedDEBUG_ADMIN_EMAILSin the client bundle as the security boundary. - Keep
X-Robots-TagandCache-Control: no-storefor/debug. - Static HTML may still be downloadable; that is acceptable. Secrets and mutations must not succeed without an allowlisted JWT.
Delete Generation Requirements
Replace “Hide generation” with “Delete generation” for non-seed versions.
Rules:
seedis immutable and cannot be deleted.- Delete requires double confirmation.
- Delete removes the version row from the index.
- Delete removes the TSX file/blob.
- If deleting the active version, promote the newest remaining non-hidden version.
- If no regenerate remains, promote
seed. - Return deleted
versionIdand newactiveVersionId.
Local delete:
- Remove
public/components/history/by-id/{patternId}/{versionId}.tsx. - Update
public/components/history/by-id/{patternId}/versions.json. - Rewrite
public/components/code/{patternId}.tsxif active changes.
Blobs delete:
- Delete
code/{patternId}/{versionId}.tsx. - Update
meta/{patternId}.json.
Version Numbering Requirements
Local and Blobs version counters are separate and must remain monotonic. Hard delete must not make a version number reusable.
Add a per-store index field:
nextVersionNumber: number
Version allocation:
const n = index.nextVersionNumber ?? inferMaxVersion(index.versions) + 1;
const versionId = `v${n}`;
index.nextVersionNumber = n + 1;
Backward compatibility:
- If
nextVersionNumberis missing, infermax(vN) + 1. - Persist
nextVersionNumberon the next index write. - Delete never decrements
nextVersionNumber. - Publishing local
v5to Blobs creates the next BlobsvN, not necessarilyv5.
Debug Table Performance and 404 Requirements
Reduce console noise and mutation latency:
- Add
prefetch={false}to debug-table pattern links to avoid missing__next._tree.txtprefetches. - Prefer current search-index metadata for
patternUrl, title, and cover in debug rows, so stale Blobs slugs do not create/patterns/...404s. - After delete/hide, optimistically remove the row and clear busy state as soon as the mutation succeeds.
- Run full list refresh in the background, not as a blocking step.
- Avoid
op=listafter single-version operations where possible; use the mutation response orop=versions&id=.... - Consider improving
op=listlater with cached/parallel index reads if the global generations list remains slow.
Implementation Notes
Add shared admin helpers for server and Function code:
- Parse
DEBUG_ADMIN_EMAILS. - Normalize email casing.
- Check a validated Supabase user against the allowlist.
- Return
401 auth_requiredwhen no valid session exists. - Return
403 admin_requiredwhen the user is valid but not allowlisted.
Keep /debug static:
- Do not change
netlify.tomlaway fromnpm ci && npm run build/publish = "out". - Do not disable production
output: "export"innext.config.ts. - Keep
X-Robots-TagandCache-Control: no-storeheaders for/debug. - Client gate + Function/
whoamiprobe; Bearer tokens from localStorage for Function and local debug API calls.
Add deletion helpers:
- Local:
deleteVersion()next tosoftDeleteVersion()insrc/lib/component-generation/versions.ts. - Blobs:
deleteCode()next toreadCode()/writeCode()innetlify/functions/lib/components-store.mts. - API: add
op=deleteor replaceop=hideinnetlify/functions/components-api.mts. - Client: replace
hideComponentVersion()withdeleteComponentVersion()insrc/lib/components-api-client.ts. - UI: replace “Hide generation” and pending-hide copy in
src/components/debug/ComponentsGenerationsTable.tsx.
Acceptance Criteria
- Production unauthenticated visitors hitting
/debugsee the sign-in gate (static HTML may still download; APIs reject them). - Production signed-in non-admin users see admin-required and cannot use debug tools or mutations.
- Production
pablo.armentano@gmail.comcan view/debugand use debug component actions. - Localhost unauthenticated access is blocked.
localhost (auth skipped)is gone.- Localhost signed-in admin users can use
/debugand publish/regenerate/rebuild. - Debug/ops mutations reject non-admin JWTs with
403 admin_required. op=generateusescomponent_generator, initially admin-only but separable for future paid/selected users.- Pattern preview/export still load public active code as before.
- Existing noindex/no-store headers remain.
- Delete rejects
seed. - Deleting inactive
vNremoves it from table and storage without changing active. - Deleting active
vNpromotes newest remaining version orseed. - Deleting
v5does not let the next generation reusev5; next version remains monotonic. - Debug table no longer floods DevTools with pattern-link prefetch 404s.
- Delete completion feels immediate after mutation success.
Key Files
src/app/debug/layout.tsxsrc/components/debug/DebugAuthGate.tsxsrc/lib/debug-supabase-auth.tssrc/lib/debug-admin.mjssrc/lib/auth-client.tsxsrc/components/debug/ComponentsGenerationsTable.tsxsrc/lib/component-generation/versions.tssrc/lib/components-api-client.tsnetlify/functions/lib/components-auth.mtsnetlify/functions/lib/components-store.mtsnetlify/functions/components-api.mts