From 47f8a562a1aec2c8261dd81a1b6ef42e1622298a Mon Sep 17 00:00:00 2001 From: arcabotai <261107832+arcabotai@users.noreply.github.com> Date: Sun, 31 May 2026 19:21:34 -0400 Subject: [PATCH] =?UTF-8?q?docs(agents):=20handoff=20=E2=80=94=20corrected?= =?UTF-8?q?=20env=20facts,=20isAuthenticated=20bug,=20next=20steps?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CORRECTION: Development/Preview/Production share the SAME Railway DB and the SAME Privy app (cmpke74en...). There is no dev/prod data isolation — local dev runs against live production data. Documented the risks + how 'prod on localhost' works, and that a real isolated dev env is the recommended next step. - Documented the cross-site Privy auth iframe (privy.castora.social) + the third-party-cookie gotcha that drops clean browser profiles to 'guest'. - Flagged the top-priority bug: isAuthenticated treats DB/infra errors as 'not authenticated' (→401 / guest onboarding), which was the root cause of the 'timeline not showing' incident. Needs to distinguish 401 vs 503. - Added a session handoff: shipped PRs #7-#12, prioritized next steps, and how to drive Chrome via CDP for live debugging. - .gitignore: ignore .specstory/ and .history/ (editor chat-log artifacts). --- AGENTS.md | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index ccd1d51..190ac3e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -49,10 +49,31 @@ npm run dev # http://localhost:3000 - Prisma uses `POSTGRES_PRISMA_URL` (pooled) + `POSTGRES_URL_NON_POOLING` (direct). The Prisma CLI reads `.env`, not `.env.local`, so for CLI commands: `set -a; . ./.env.local; set +a` first. -- Login is via **Privy**. For localhost login to work, `http://localhost:3000` must be in the Privy - app's allowed origins (dashboard → Configuration → App settings → Domains). +- Login is via **Privy** (app id `cmpke74en00660cjvnb9aq32k`; the auth iframe is hosted at the + custom domain `privy.castora.social`). For localhost login to work, `http://localhost:3000` must be + in the Privy app's allowed origins (dashboard → Configuration → App settings → Domains). +- Because the Privy auth iframe is **cross-site** (`privy.castora.social`), a browser that **blocks + third-party cookies** can't read the session → the app silently falls back to **guest**. If you log + in but stay "guest" in a clean/automation Chrome profile, allow 3rd-party cookies (or launch Chrome + with `--disable-features=ThirdPartyStoragePartitioning,TrackingProtection3pcd`). - Unauthenticated API routes returning `401`/`403` is expected (auth gates), not a crash. +## ⚠️ Environments are NOT isolated (important) + +The Vercel **Development**, **Preview**, and **Production** environments all use the **same Railway +Postgres database AND the same Privy app** (`cmpke74en…`). There is **no separate dev/staging data**. +Consequences: + +- Running `npm run dev` locally talks to the **live production database**. Local reads/writes, + account connections, and any posting hit **real prod data and real Farcaster accounts**. +- A `prisma migrate`/`db push` run locally would alter the **production** schema. **Never** run DB + migrations/seeds from local. +- "Run prod on localhost" = `vercel env pull .env.local --environment=production` then re-apply the + `NEXT_PUBLIC_APP_URL=http://localhost:3000` override. (A dev-env backup can be kept as + `.env.local.dev.bak`.) Treat the local window as if you're operating on castora.social directly. +- **Recommended next infra step:** stand up a real isolated dev environment (separate Railway DB + + separate Privy dev app) so local work can't damage production. + ## CI (`.github/workflows/ci.yml`) - **Hard gate:** `typecheck` + `build`. **Non-blocking:** `lint` (project sets `eslint.ignoreDuringBuilds`). @@ -80,7 +101,46 @@ npm run dev # http://localhost:3000 - **Direct Casts (DMs):** `@/utils/direct-casts.ts` sends via `api.warpcast.com/v2/ext-send-direct-cast` with a Farcaster app API key (`WARPCAST_DM_API_KEY` — rename to `FARCASTER_DM_*` when wiring it). Send-only and account-bound; there is no official read/receive DM API, so full native DM parity - inside Castora is not feasible today. Fine for one-off sends like expiration reminders. + inside Castora is not feasible today (the on-protocol E2E "Direct Casts" FIP is still just a + proposal). Fine for one-off sends like expiration reminders. + +## 🔴 Top-priority bug to fix next: `isAuthenticated` swallows infra errors + +`src/utils/auth/isAuthenticated.ts` wraps token verification **and** the +`prisma.supercastPrivyUser.findUnique()` DB lookup in one try/catch that, on **any** error, returns +`{ authenticated: false }`. So a **transient DB outage** (the Railway hobby Postgres blips) makes +every data route 401 → the timeline goes empty and the user can even get dropped into the +**guest / "connect account"** onboarding. Because local == prod data, that onboarding could write +duplicate records into production. + +**Fix:** distinguish a genuine token-verification failure (→ real `401`) from an infrastructure +error like a DB connection failure (→ surface a retryable `503`, ideally with a short retry). Do +**not** treat "database unreachable" as "not authenticated." This was the root cause of the +"timeline not showing" incident debugged on 2026-05-30. + +## Session handoff (through 2026-05-31) + +**Shipped & merged to production this run** (PRs #7, #8, #9, #10, #11, #12 — all CI-green): +- Localhost dev fixed (`hostURL` prefers `window.location.origin`); CI + gitleaks secret scan added. +- Neynar centralized behind `@/lib/neynar` (68 files migrated). +- Sensitive-route audit + cron routes gated by `CRON_SECRET` (`requireCronAuth`); `CRON_SECRET` set + in Vercel prod/dev. +- Public `/changelog` + `CHANGELOG.md`; this `AGENTS.md`. +- Superanon/Dune worker shelved; scheduled-cast senders reconstructed (OFF behind a flag). + +**What's needed next (priority order):** +1. **Fix `isAuthenticated`** (above) — highest value; it's a live footgun given shared prod data. +2. **Isolate environments** — separate dev DB + dev Privy app so local ≠ prod. +3. **Workers:** build `cancel-expired-subscriptions` (Stripe, for review/disabled); wire + `send-expiration-reminders` once a Farcaster Direct Cast API key is provided; verify the scheduled + senders end-to-end on a test account before enabling (`CASTORA_ENABLE_SCHEDULED_SENDER` + cron). +4. **Tech debt:** dedupe `react-query` v3 → `@tanstack/react-query` v5; plan Next.js 13.5 → 15/16; + re-enable `reactStrictMode` + ESLint-on-build (both "todo" in `next.config.js`). + +**Live browser debugging:** you can drive Chrome via CDP without an MCP — launch a debuggable +instance (`open -na "Google Chrome" --args --remote-debugging-port=9222 --user-data-dir=