-
Notifications
You must be signed in to change notification settings - Fork 1
Add admin memory verification console, loader, guard contract, docs, and tests #96
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| import Link from "next/link"; | ||
| import { AppShell } from "@/components/layout/app-shell"; | ||
| import { PageHeader } from "@/components/ui/page-header"; | ||
| import { SectionCard } from "@/components/ui/section-card"; | ||
| import { resolvePandoraServerSession, createRepositoryContextFromPandoraSession } from "@/lib/auth/pandora-server-session-resolver"; | ||
| import { createSupabaseServerClient } from "@/lib/supabase/server"; | ||
| import { SupabasePersistedMemoryReadRepository } from "@/lib/db/supabase-persisted-memory-read-repository"; | ||
| import { resolvePandoraRuntimeSafetyConfig } from "@/lib/config/pandora-runtime-safety-config"; | ||
| import { loadAdminMemoryVerification, type VerificationLine } from "@/lib/services/admin-memory-verification-loader"; | ||
|
|
||
| export const dynamic = "force-dynamic"; | ||
|
|
||
| const Badge = ({ line }: Readonly<{ line: VerificationLine }>) => <li><strong>{line.label}:</strong> {line.status} — {line.detail}</li>; | ||
|
|
||
| export default async function AdminMemoryVerificationPage() { | ||
| const namespace = "real_life"; | ||
| const session = await resolvePandoraServerSession(); | ||
| const context = createRepositoryContextFromPandoraSession({ sessionResult: session, namespace }); | ||
| const runtime = resolvePandoraRuntimeSafetyConfig(); | ||
| const supabase = await createSupabaseServerClient(); | ||
| const dto = await loadAdminMemoryVerification({ session, context: context.ok ? context.context : { namespace }, repository: new SupabasePersistedMemoryReadRepository(supabase as never), runtime }); | ||
| const loginPath = `/auth/login?next=${encodeURIComponent("/admin/memory/verification")}`; | ||
| return ( | ||
| <AppShell> | ||
| <PageHeader eyebrow="Internal admin" title="Production memory verification" description="Read-only, authenticated/admin-only verification route for Phase 3D closure safety. It reports deployment proof, read availability, route guard status, persisted read gate, unsafe mutation/integration gates, public read status, and a close/no-close recommendation without enabling public reads or writes." /> | ||
| {!session.ok ? <SectionCard title="Authentication required" description="No persisted rows or proof data are exposed without a Supabase operator session."><Link className="button-link button-link--primary" href={loginPath}>Start operator session</Link></SectionCard> : null} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When Useful? React with 👍 / 👎. |
||
| <SectionCard title="Safety summary" description="Missing values are shown as not configured, unavailable, or blocked instead of crashing."> | ||
| <ul> | ||
| <Badge line={dto.commitSha} /> | ||
| <Badge line={dto.persistedMemoryReadGateStatus} /> | ||
| <Badge line={dto.supabaseReadAvailability} /> | ||
| <Badge line={dto.memoryBrowserRouteStatus} /> | ||
| <Badge line={dto.auditRouteStatus} /> | ||
| <Badge line={dto.unsafeGateStatus} /> | ||
| <Badge line={dto.publicReadStatus} /> | ||
| <Badge line={dto.recommendation} /> | ||
| </ul> | ||
| </SectionCard> | ||
| <SectionCard title="Vercel/environment proof" description="Only configured/not-configured proof is displayed; secret values are never printed."><ul>{dto.vercelEnvProof.map((line) => <Badge key={line.label} line={line} />)}</ul></SectionCard> | ||
| <SectionCard title="Manual verification checklist" description="Compact production checklist for closure review."><ul>{dto.checklist.map((line) => <Badge key={line.label} line={line} />)}</ul></SectionCard> | ||
| <SectionCard title="Route guard consistency" description="All Phase 3D admin memory routes share these expectations: Supabase session, server-derived user context, namespace scope, and read-only behavior."> | ||
| <ul>{dto.guardExpectations.map((guard) => <li key={guard.route}><strong>{guard.route}</strong>: authenticated={String(guard.authenticatedSupabaseSessionRequired)}, adminOnly={String(guard.adminOnly)}, readOnly={String(guard.readOnly)}, namespaceScoped={String(guard.namespaceScoped)}, serverDerivedUserOnly={String(guard.serverDerivedUserOnly)}, publicReadAllowed={String(guard.publicReadAllowed)}, serviceRoleAllowed={String(guard.serviceRoleAllowed)}, mutationAllowed={String(guard.mutationAllowed)}</li>)}</ul> | ||
| </SectionCard> | ||
| </AppShell> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| # Phase 3D production verification | ||
|
|
||
| Phase 3D adds a read-only production verification console for closure hardening. It does not enable public memory reads, public persistence, unsafe writes, model calls, embeddings, semantic retrieval, GPT Actions, or MCP. | ||
|
|
||
| ## Exact production URLs | ||
|
|
||
| Replace `<production-host>` with the deployed Vercel host. | ||
|
|
||
| - `https://<production-host>/admin/memory/verification` | ||
| - `https://<production-host>/admin/memory/browser?namespace=real_life` | ||
| - `https://<production-host>/admin/memory/audit?namespace=real_life` | ||
| - `https://<production-host>/memory/browser` | ||
|
|
||
| ## Manual login test steps | ||
|
|
||
| 1. Open `https://<production-host>/admin/memory/verification` in a private browser window. | ||
| 2. Confirm unauthenticated access shows an authentication-required/login state and no persisted memory rows. | ||
| 3. Log in with the reviewed Supabase operator account. | ||
| 4. Re-open `/admin/memory/verification` and confirm the safety summary loads. | ||
| 5. Open `/admin/memory/browser?namespace=real_life` and `/admin/memory/audit?namespace=real_life` from the same authenticated session. | ||
|
|
||
| ## Expected auth behavior | ||
|
|
||
| - Admin memory routes require a Supabase-authenticated operator session. | ||
| - User identity must be server-derived from the session. | ||
| - Client-supplied `user_id`, `userId`, `client_user_id`, or `clientUserId` values must not be accepted. | ||
| - Unauthenticated sessions must see login/auth-required states, not memory rows. | ||
|
|
||
| ## Expected public redirect behavior | ||
|
|
||
| - `https://<production-host>/memory/browser` redirects to `/admin/memory/browser?namespace=real_life`. | ||
| - The public route must not instantiate a Supabase read repository. | ||
| - The public route must not render persisted rows, source proof, patch proof, or audit proof. | ||
| - No public proof or audit route should exist for memory browser data. | ||
|
|
||
| ## Expected read-only behavior | ||
|
|
||
| The admin browser, audit viewer, and verification route must remain read-only. They must not expose edit, delete, persist, execute, patch-write, model, embedding, semantic retrieval, GPT Actions, MCP, or public-read controls. | ||
|
|
||
| ## Unsafe gate env variables to inspect | ||
|
|
||
| These should be absent or not set to `true` unless a separate reviewed production operation explicitly requires them: | ||
|
|
||
| - `PANDORA_ENABLE_MEMORY_INGEST_PRODUCTION_WRITES` | ||
| - `PANDORA_ENABLE_APPROVED_REVIEW_MEMORY_PERSISTENCE` | ||
| - `PANDORA_ENABLE_PUBLIC_MEMORY_READ` | ||
| - `PANDORA_ENABLE_PUBLIC_MEMORY_PERSISTENCE` | ||
| - `PANDORA_ENABLE_MODEL_CALLS` | ||
| - `PANDORA_ENABLE_EMBEDDINGS` | ||
| - `PANDORA_ENABLE_SEMANTIC_RETRIEVAL` | ||
| - `PANDORA_ENABLE_GPT_ACTIONS` | ||
| - `PANDORA_ENABLE_MCP` | ||
|
|
||
| Useful proof-only values: | ||
|
|
||
| - `VERCEL_GIT_COMMIT_SHA` or `GIT_COMMIT_SHA` | ||
| - `VERCEL_ENV` | ||
| - `VERCEL_URL` | ||
| - `PANDORA_SKILLS_COMMIT_SHA` | ||
| - `PANDORA_SKILLS_PROOF_STATUS` | ||
|
|
||
| ## Supabase/RLS proof expectations | ||
|
|
||
| - `PANDORA_ENABLE_PERSISTED_MEMORY_READ` is the read-availability gate; verification must report disabled when it is false and must not force a database read proof. | ||
| - Reads are performed through the authenticated Supabase server client. | ||
| - Reads are scoped by server-derived user ID and namespace. | ||
| - Missing tables, missing proof fields, or RLS denial should display `unavailable` or `blocked`; the page must not crash. | ||
| - Browser proof should include source and patch fields where available. | ||
| - Audit proof should include audit route availability or an explicit unavailable state. | ||
|
|
||
| ## Close/no-close decision criteria | ||
|
|
||
| Phase 3D can close after deployment only if: | ||
|
|
||
| - The deployed commit SHA is visible or otherwise captured in release proof. | ||
| - `/admin/memory/verification` loads for an authenticated operator. | ||
| - `/admin/memory/browser?namespace=real_life` and `/admin/memory/audit?namespace=real_life` remain authenticated and read-only. | ||
| - `/memory/browser` redirects to the admin browser and does not render rows publicly. | ||
| - The persisted-memory read gate is enabled for the production proof pass and Supabase/RLS read proof is available. | ||
| - Public reads and unsafe mutation/integration gates are disabled. | ||
| - Source, patch, audit, and skills commit proof are available or explicitly recorded as not configured with a follow-up decision. | ||
|
|
||
| Do not close if the persisted-memory read gate is disabled for the proof pass, public reads are enabled, unsafe mutation/integration gates are enabled without review, admin routes expose mutation controls, RLS blocks expected operator reads without explanation, or deployed proof cannot be tied to a commit. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| export type AdminMemoryRouteGuardExpectation = { | ||
| route: "/admin/memory/browser" | "/admin/memory/audit" | "/admin/memory/verification"; | ||
| authenticatedSupabaseSessionRequired: true; | ||
| adminOnly: true; | ||
| readOnly: true; | ||
| namespaceScoped: true; | ||
| serverDerivedUserOnly: true; | ||
| publicReadAllowed: false; | ||
| serviceRoleAllowed: false; | ||
| mutationAllowed: false; | ||
| }; | ||
|
|
||
| export const adminMemoryRouteGuardExpectations: AdminMemoryRouteGuardExpectation[] = [ | ||
| "/admin/memory/browser", | ||
| "/admin/memory/audit", | ||
| "/admin/memory/verification", | ||
| ].map((route) => ({ | ||
| route: route as AdminMemoryRouteGuardExpectation["route"], | ||
| authenticatedSupabaseSessionRequired: true, | ||
| adminOnly: true, | ||
| readOnly: true, | ||
| namespaceScoped: true, | ||
| serverDerivedUserOnly: true, | ||
| publicReadAllowed: false, | ||
| serviceRoleAllowed: false, | ||
| mutationAllowed: false, | ||
| })); | ||
|
|
||
| export function getAdminMemoryRouteGuardExpectation(route: AdminMemoryRouteGuardExpectation["route"]) { | ||
| return adminMemoryRouteGuardExpectations.find((item) => item.route === route); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| import { resolvePandoraRuntimeSafetyConfig, type PandoraRuntimeGate, type PandoraRuntimeSafetyConfigResult } from "@/lib/config/pandora-runtime-safety-config"; | ||
| import { loadPersistedMemoryBrowserView } from "@/lib/services/persisted-memory-browser-loader"; | ||
| import type { PersistedMemoryReadRepository } from "@/lib/db/persisted-memory-read-repository-contract"; | ||
| import type { PersistedMemoryReadContext } from "@/lib/services/persisted-memory-read-contract"; | ||
| import type { PandoraServerSessionResult } from "@/lib/auth/pandora-server-session-contract"; | ||
| import { adminMemoryRouteGuardExpectations } from "@/lib/services/admin-memory-route-guard-contract"; | ||
|
|
||
| export type VerificationStatus = "available" | "not configured" | "unavailable" | "blocked" | "disabled"; | ||
| export type VerificationLine = { label: string; status: VerificationStatus; detail: string }; | ||
| export type AdminMemoryVerificationDto = { | ||
| readOnly: true; | ||
| route: "/admin/memory/verification"; | ||
| commitSha: VerificationLine; | ||
| vercelEnvProof: VerificationLine[]; | ||
| persistedMemoryReadGateStatus: VerificationLine; | ||
| supabaseReadAvailability: VerificationLine; | ||
| memoryBrowserRouteStatus: VerificationLine; | ||
| auditRouteStatus: VerificationLine; | ||
| unsafeGateStatus: VerificationLine & { enabledDangerousGates: string[] }; | ||
| publicReadStatus: VerificationLine; | ||
| recommendation: VerificationLine & { closeRecommended: boolean }; | ||
| checklist: VerificationLine[]; | ||
| guardExpectations: typeof adminMemoryRouteGuardExpectations; | ||
| }; | ||
|
|
||
| const dangerousGates: PandoraRuntimeGate[] = [ | ||
| "adminPersistenceConsoleEnabled", | ||
| "approvedReviewPersistenceEnabled", | ||
| "operatorQaFlowEnabled", | ||
| "ingestProductionWriteEnabled", | ||
| "publicMemoryReadEnabled", | ||
| "publicMemoryPersistenceEnabled", | ||
| "modelCallsEnabled", | ||
| "embeddingsEnabled", | ||
| "semanticRetrievalEnabled", | ||
| "gptActionsEnabled", | ||
| "mcpEnabled", | ||
| ]; | ||
|
|
||
| const value = (v?: string) => v && v.trim() ? v : undefined; | ||
| const configured = (label: string, envVar: string, env: Partial<NodeJS.ProcessEnv>): VerificationLine => { | ||
| const v = value(env[envVar]); | ||
| return { label, status: v ? "available" : "not configured", detail: v ? `${envVar}=configured` : `${envVar}=not configured` }; | ||
| }; | ||
| const enabledDangerousGates = (runtime: PandoraRuntimeSafetyConfigResult) => dangerousGates.filter((gate) => runtime.config[gate]).map((gate) => `${runtime.gates[gate].envVar} (${gate})`); | ||
|
|
||
| export async function loadAdminMemoryVerification(input: { session: PandoraServerSessionResult; context?: Partial<PersistedMemoryReadContext>; repository?: PersistedMemoryReadRepository; runtime?: PandoraRuntimeSafetyConfigResult; env?: Partial<NodeJS.ProcessEnv> }): Promise<AdminMemoryVerificationDto> { | ||
| const env = input.env ?? process.env; | ||
| const runtime = input.runtime ?? resolvePandoraRuntimeSafetyConfig(env); | ||
| const commit = value(env.VERCEL_GIT_COMMIT_SHA) ?? value(env.GIT_COMMIT_SHA) ?? value(env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA); | ||
| const persistedReadEnabled = runtime.config.persistedMemoryReadEnabled; | ||
| const persistedMemoryReadGateStatus: VerificationLine = { label: "Persisted memory read gate", status: persistedReadEnabled ? "available" : "disabled", detail: `${runtime.gates.persistedMemoryReadEnabled.envVar}=${persistedReadEnabled ? "true" : "false"}` }; | ||
| const browser = await loadPersistedMemoryBrowserView({ authenticated: input.session.ok, context: input.context, repository: input.repository, runtime, filters: { namespace: input.context?.namespace ?? "real_life" } }); | ||
| const authBlocked = !input.session.ok; | ||
| const dbBlocked = browser.blockers.find((b) => b.code !== "auth_required"); | ||
| const readGateBlocked = browser.blockers.find((b) => b.message.toLowerCase().includes("read gate is disabled")); | ||
| const supabaseReadAvailability: VerificationLine = authBlocked | ||
| ? { label: "Supabase read availability", status: "blocked", detail: "Authenticated admin/operator session is required before reading through RLS." } | ||
| : !persistedReadEnabled || readGateBlocked | ||
| ? { label: "Supabase read availability", status: "disabled", detail: "Persisted-memory read gate is disabled; no read proof was forced." } | ||
| : dbBlocked | ||
| ? { label: "Supabase read availability", status: "unavailable", detail: dbBlocked.message } | ||
| : { label: "Supabase read availability", status: "available", detail: browser.empty ? "Read path returned no rows for this user/namespace." : "Read path returned user-scoped rows." }; | ||
| const enabledUnsafe = enabledDangerousGates(runtime); | ||
| const unsafe = enabledUnsafe.length > 0; | ||
| const publicRead = runtime.config.publicMemoryReadEnabled; | ||
| const browserOk = input.session.ok && persistedReadEnabled ? "available" : "blocked"; | ||
| const auditOk = input.session.ok && persistedReadEnabled ? "available" : "blocked"; | ||
| const readProofAvailable = supabaseReadAvailability.status === "available"; | ||
| const closeRecommended = Boolean(commit) && !unsafe && input.session.ok && readProofAvailable; | ||
| return { | ||
| readOnly: true, | ||
| route: "/admin/memory/verification", | ||
| commitSha: { label: "Latest deployed commit SHA", status: commit ? "available" : "not configured", detail: commit ?? "VERCEL_GIT_COMMIT_SHA/GIT_COMMIT_SHA not configured" }, | ||
| vercelEnvProof: [configured("Vercel environment", "VERCEL_ENV", env), configured("Vercel URL", "VERCEL_URL", env), configured("Skills commit proof", "PANDORA_SKILLS_COMMIT_SHA", env), configured("Skills proof status", "PANDORA_SKILLS_PROOF_STATUS", env)], | ||
| persistedMemoryReadGateStatus, | ||
| supabaseReadAvailability, | ||
| memoryBrowserRouteStatus: { label: "Memory browser route", status: browserOk, detail: persistedReadEnabled ? "/admin/memory/browser is authenticated, namespace-scoped, and read-only by contract." : "/admin/memory/browser remains gated because persisted-memory reads are disabled." }, | ||
| auditRouteStatus: { label: "Audit route", status: auditOk, detail: persistedReadEnabled ? "/admin/memory/audit is authenticated, namespace-scoped, and read-only by contract." : "/admin/memory/audit remains gated because persisted-memory reads are disabled." }, | ||
| unsafeGateStatus: { label: "Unsafe mutation/integration gates", status: unsafe ? "blocked" : "disabled", detail: unsafe ? `Enabled dangerous gates: ${enabledUnsafe.join(", ")}. Do not close without review.` : "All dangerous mutation/integration gates are disabled.", enabledDangerousGates: enabledUnsafe }, | ||
| publicReadStatus: { label: "Public read status", status: publicRead ? "blocked" : "disabled", detail: publicRead ? "PANDORA_ENABLE_PUBLIC_MEMORY_READ=true; public reads are not closure-safe." : "Public memory reads are disabled; /memory/browser redirects to the admin route." }, | ||
| recommendation: { label: "Final recommendation", status: closeRecommended ? "available" : "blocked", detail: closeRecommended ? "Close after deployed manual checklist passes." : "Do not close until commit proof, auth/session, persisted read proof, and dangerous gate blockers are resolved.", closeRecommended }, | ||
| checklist: [ | ||
| { label: "Authenticated browser", status: input.session.ok && persistedReadEnabled ? "available" : "blocked", detail: "Open /admin/memory/browser?namespace=real_life while logged in." }, | ||
| { label: "Unauthenticated admin denial/login", status: "blocked", detail: "In a private browser, admin routes must show login/auth-required and no rows." }, | ||
| { label: "Public redirect", status: "disabled", detail: "/memory/browser must redirect to /admin/memory/browser?namespace=real_life." }, | ||
| { label: "Read-only behavior", status: "available", detail: "No edit, delete, persist, execute, model, embedding, retrieval, GPT Actions, or MCP controls." }, | ||
| { label: "Persisted read gate", status: persistedMemoryReadGateStatus.status, detail: persistedMemoryReadGateStatus.detail }, | ||
| { label: "Disabled unsafe gates", status: unsafe ? "blocked" : "disabled", detail: unsafe ? `Review enabled dangerous gates: ${enabledUnsafe.join(", ")}.` : "All dangerous gates are disabled." }, | ||
| { label: "Audit proof availability", status: auditOk, detail: "Audit route should show audit rows or an explicit unavailable state." }, | ||
| { label: "Source/patch proof availability", status: supabaseReadAvailability.status, detail: "Browser should show source and patch proof fields or an explicit unavailable state." }, | ||
| { label: "Skills commit proof availability", status: value(env.PANDORA_SKILLS_COMMIT_SHA) ? "available" : "not configured", detail: "PANDORA_SKILLS_COMMIT_SHA should be configured for closure proof." }, | ||
| ], | ||
| guardExpectations: adminMemoryRouteGuardExpectations, | ||
| }; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This route treats any successful Supabase session as sufficient and then loads the verification DTO/repository, while the new guard contract and page copy describe the route as admin/operator-only. In this codebase
resolvePandoraServerSessioncreates ordinary real sessions with emptyadminCapabilitiesandisInternalOperator: false, and other admin APIs such as readiness explicitly require those capabilities, so a non-operator authenticated user can still access the closure proof for their namespace here.Useful? React with 👍 / 👎.