Skip to content

RAID Report — 2026-05-04 (Scheduled)

Date: 04-05-2026 Subject: RAID Report

Source: docs/architecture/RISKS_GAPS_ASSUMPTIONS.md on main.

This is the first RAID report from the scheduled task. Items already marked resolved in the source doc are carried in as closed (struck-through) so future runs have a baseline to diff against. “Days open” is measured from the date the item first appeared in the document’s CHANGE SUMMARY where that can be derived; otherwise it is given as the conservative lower bound and flagged with ~.

Issue NumberIssue TypeStatusDays openSuggested Action
R-01 Monolithic shell (src/App.jsx ~500 lines)GapOpen~30Extract auth gates, route tree, modal refs into focused components per the v1.1 refactor noted in CLAUDE.md. Open a Linear issue if one does not yet exist.
R-02 Oversized hooks as service layers (useNoteForm.js, useSettings.js)GapOpen~30Split UI state from Dexie/Supabase/PostHog side-effects; introduce a thin service layer so a regression in one concern does not break sync + AI simultaneously.
R-03 SUR-60 Manual Supabase migration applyRiskOpen~30Add a CI gate that fails the build when client code references a column not present in scripts/schema-contract.js, and automate supabase db push from the migrations folder.
R-04 Managed proxy env fragility (Edge Function 500s on missing env)RiskOpen~30Surface an operator-actionable code in the toast (useNoteForm.js:254) and add a startup self-check in the Edge Function that returns a typed MISCONFIGURED error rather than 500.
R-05 Edge Function logs user identifiers (anthropic-proxy/index.ts:399-400, 423)RiskOpen8Strip user.id and action type from console.log lines, or hash them, before any production-grade load.
R-06 SUR-242 Azure Content Safety fail-open with no alertingRiskOpen3Emit a PostHog guardrail_fail_open event on every fail-open path; add a Supabase dashboard / log alert so operators learn within minutes, not days.
R-07 SUR-242 Azure Content Safety latency budget (~5–15 s on degradation)RiskOpen3Document the per-call timeout in guardrail.ts, cap total per-request guardrail latency, and degrade gracefully with a single fail-open path rather than three sequential retries.
R-08 SUR-242 Client-side PII detection is regex-only (no NER)GapOpen3Track SUR-246 (Llama Prompt Guard 2 + GLiNER NER) explicitly on the v1.5 plan; until then, expand the warn copy so users understand names/addresses are not detected.
R-09 SUR-242 v1.4 warn-not-block PII policyGapOpen3Decide whether v1.5 moves to soft-block on high-confidence card/SSN matches; document the policy in docs/architecture.
R-10 SUR-256 Blog requires full rebuild per postGapOpen3Acceptable for solo founder cadence; revisit if posting cadence rises. No action this cycle.
R-11 Rate-limit race window (check vs write not atomic)RiskOpen8Move the increment into a single upsert_ai_usage RPC with a CHECK constraint; required before paid tiers raise the cap.
R-12 SUR-92 Per-request quota lookup latencyRisk (low)Open5No action at current scale; revisit with a 30-60 s {userId → resolvedLimit} cache if managed-AI traffic grows ≥10×.
R-13 Usage opacity (no in-app remaining-quota surface)GapOpen~30Add a “remaining managed-AI calls this month” line to ProfileScreen / SettingsModal reading from ai_usage_daily.
R-14 No per-note image cleanup in note-images bucketGapOpen8Add a counterpart to uploadImage that removes the storage object on deleteNote; or a periodic sweep keyed off soft-deleted note rows.
R-15 Outbox single queue with opaque failure modeRiskOpen8Tag outbox entries by error class (RLS / schema / 5xx); quarantine poison messages after N retries and surface them in a settings panel.
R-16 Full re-decrypt on every syncGapOpen8Add an encryption-only delta path keyed off updatedAt; show a progress indicator so the cost is visible during large syncs.
R-17 Sync image fetch is sequentialGapOpen8Switch the for-loop in useAuth.js:213-221 to bounded Promise.all (e.g. 4-8 in flight); record per-image failures so they retry next cycle.
R-18 Decrypt-failure recovery is dead-endGapOpen~30Add a “retry unlock” affordance that calls getEncryptionPrfOutput() without a full sign-out; surface count and remediation copy.
R-19 Transfer-code auto-expire is best-effortRiskOpen8Add a server-side cron / scheduled function to deactivate expired transfer-v1 blobs rather than relying on the next createDeviceTransfer.
R-20 No PWA update prompt (registerType: 'autoUpdate')GapOpen8Switch to prompt mode and wire an onNeedRefresh toast; replace the deprecated apple-mobile-web-app-capable meta tag at the same time.
R-21 Accessibility gaps (long-press is the only edit/delete path)GapOpen~30Add a visible action button or context menu fallback; ensure escape paths exist on capture/note views where the bottom nav is hidden.
R-22 db.js has no dedicated unit testsGapOpen8Add direct unit tests for saveNote, updateNote, deleteBook cascade, mergeCloudRecords, importMerge/Replace, and the v1→v9 migration chain.
R-23 SUR-257 Duplicate-source UX gap (non-blocking duplicate hint)GapOpen8Decide whether v1.5 ships hard-block on exact match, plus a merge tool to clean up existing duplicates.
R-24 SUR-261 Branded auth-email template lives in repo onlyGapOpen8Add a CI step (or a Supabase Management API call) that diffs the dashboard template against supabase/email-templates/magic-link.html.
R-25 SUR-233 device_label immutable after enrolmentGapOpen2Cosmetic; add an authenticated PATCH path on wrapped_key_blobs scoped by user_id if user feedback warrants.
R-26 SUR-235 Add-idea Free-tier cap not yet enforcedGapOpen2Implement the useEntitlements() gate on the AddIdeaSheet “Create” CTA; coordinate with Pro upsell copy.
R-27 SUR-261 Approved waitlist rows with user_id IS NULL strandedGapOpen8Run the audit query on a schedule; have admin tooling auto-invite stranded rows or surface them in the queue.
R-28 SUR-209 broken in-app help linksGapClosedResolved by SUR-223 Phase 1 (2026-04-25). Phase 2 (in-app help renderer) tracked separately.
R-29 SUR-91 Direct Anthropic exposure (BYOK)RiskClosedResolved. All AI calls now go through the managed Edge Function via invokeAnthropicProxy.
R-30 SUR-61 Schema probe only runs onceRiskClosedResolved. useAuth.js:166-176 only sets schemaProbed.current = true after success.
R-31 SUR-62 Full-table sync onlyRiskClosedResolved. fetchAllCloud(since) is incremental via lastSyncRef.current.
R-32 No OCR regression coverageGapClosedResolved by api.test.js, photoAdapter.test.js, capture.test.jsx.
R-33 SUR-215 How-It-Works placeholdersGapClosedResolved. HowItWorksPage.jsx and LandingPage.jsx removed (2026-04-23).
A-01 Dexie ↔ Supabase lockstep is human-drivenAssumptionOpen~30Add a CI check that cross-references the Dexie schema declaration in db.js against scripts/schema-contract.js.
A-02 SUR-92 Free-tier monthly cap default 50, cross-actionAssumptionOpen5Validate the 50/month cap against actual usage curves once paid tiers go live; correlate with ai_usage_daily token columns to estimate cost.
A-03 ANTHROPIC_API_KEY rotation is manualAssumptionOpen~30Document the rotation runbook; consider migrating to a secrets manager so a stale key surfaces with an explicit error rather than a 500.
A-04 VitePWA autoUpdate is acceptableAssumptionOpen~30Linked to R-20; flip to prompt once a refresh-toast UI exists.
A-05 Orphaned image blobs on book delete are tolerableAssumptionOpen~30Linked to R-14 / unknown SLA; document the cleanup-sweep plan.
A-06 Single Anthropic API key, no project-level spend capAssumptionOpen~30Set a per-project Anthropic spend cap or a daily $-threshold alert tied to posthog-node.
A-07 SUR-242 Azure severity threshold ≥5 calibrated for Surfc contentAssumptionOpen3Re-evaluate threshold once managed usage exceeds ~500 calls/month using real false-positive data.
A-08 Future ingest adapters reuse existing ingest interfaceAssumptionOpen~30Validate by sketching the Readwise adapter against photoAdapter.js / manualAdapter.js; surface required Dexie additions early.
A-09 SUR-238 CaptureFabMenu speed-dial sufficientAssumptionOpen11Schedule a usability check with 3-5 users; if confused, add a persistent minimal nav or back-button.
A-10 help.surfc.app (Cloudflare Pages) remains reachableAssumptionOpen~30Linked to Phase 2 in-app fallback. Add a synthetic uptime check on the help domain.
A-11 session.access_token refresh covers all expiry racesAssumptionOpen~30Add a regression test that backgrounds the PWA past expiry and confirms the next Edge Function call succeeds without an “Invalid JWT” error.
A-12 SUR-261 Email-OTP error-string mapping is best-effortAssumptionOpen8Add a contract test that pings the live Supabase Auth endpoint with an unknown email and asserts the substring is still present.
A-13 Email signups stay disabled at Supabase platform levelAssumptionOpen8Document the dashboard toggle in the SUR-261 PR + migration playbook; add a periodic check via Supabase Management API.
A-14 Email-OTP code length/expiry are Supabase defaultsAssumptionOpen8No action while abuse signals are absent; revisit once managed AI volume gives a baseline for abuse detection.
A-15 SUR-261 isStandaloneOrTwa() decided once at mountAssumptionOpen8Acceptable today; revisit if iOS/Android ever hot-swap install context within a session.
D-01 Supabase + Anthropic key management / rotation cadenceDependencyOpen~30Owner: ops. Document Netlify + Supabase Edge env vault choice and rotation cadence in docs/architecture/.
D-02 Monitoring/alerting for sync + Edge Function errorsDependencyOpen~30Pipe console.log from the Edge Function into a log drain with an alert on error-rate spikes; surface client-side syncStatus failures via PostHog.
D-03 Project-level Anthropic spend cap vs per-user countersDependencyOpen~30Set a project-level monthly spend cap in the Anthropic console; correlate ai_usage_daily.input_tokens/output_tokens with cost in a dashboard.
D-04 Exports including storage paths or binary blobsDependencyOpen~30Decide whether buildExport ships embedded image data; if yes, scope a v1.5 task to update the export schema + import path.
D-05 SLA for clearing orphaned note-images on active accountsDependencyOpen~30Owner question. Linked to R-14.
D-06 Multi-device editing conflict UI beyond last-write-winsDependencyOpen~30Decide product policy: silent LWW vs. surfaced “your edit was overwritten” hint.
D-07 Background sync via service worker sync eventsDependencyOpen~30Spike feasibility on iOS Safari / Android TWA; today the outbox only flushes on online or manual sync.
D-08 Account recovery path when passkey destroyed and no transfer codeDependencyOpen~30Confirm the by-design “data is unrecoverable” stance with product, or define a support-side reset flow that wipes the encrypted note text.
D-09 Deletion-success audit trail beyond delete-account 200DependencyOpen~30Add a server-side audit row in a deletion_log table written by the Edge Function; reconcile against auth.admin.deleteUser outcomes.

Notes for the next run.

  • This is the baseline. Future runs should diff this report against the new main version of RISKS_GAPS_ASSUMPTIONS.md and strike through any item whose corresponding bullet has moved to the “Resolved” / ~~struck-through~~ block.
  • “Days open” for items first introduced in the document’s earliest section (“Updated:” entries with no explicit date) is rendered as ~30 — the document’s earliest dated entries are 2026-04-23, so anything older than that is at least ~30 days as of 2026-05-04. Refine with git blame on a future pass if a more precise number is needed.
  • Closed items in this baseline are kept as a record so that, if any of them re-open, the next run can flip Status back to Open without losing the issue number.