# AGENTS.md — AI collaboration (tcg-vault) Guidance for agents and humans working in this repo. Prefer existing patterns over new abstractions. > Branding note: the repo, README, and seed data say "TCG Vault" and `admin@tcgvault.com`, but the Layout component renders "Deck Hearth". Pick one before launch — see `.convoys/` for tracking. ## 1. Project overview A web app for managing trading-card-game collections (Magic, Pokémon, Lorcana). Users authenticate, build collections + decks, scan physical cards via a camera+AI-OCR flow, and share publicly. Admin users curate the card database. - **Framework:** Next.js 15 (Pages router) + React 18, JavaScript (not TypeScript) - **Data:** Neon Postgres, accessed two different ways — `@neondatabase/serverless` (`lib/database.js`) AND raw `@vercel/postgres` (`pages/api/**`). Pick ONE; see Gotcha #1. - **Auth:** Custom JWT (jsonwebtoken + bcryptjs), token stored in `localStorage`, sent as `Authorization: Bearer …`. No NextAuth. - **UI:** Tailwind CSS + custom CSS variables for theming (light/dark via `lib/theme-context.js`) - **Hosting:** Vercel (`vercel.json`, `.vercel/` present) ## 2. Architecture quick reference | Area | Path | Notes | | --- | --- | --- | | Pages router views | `pages/*.js` | Public + auth views; uses `components/Layout.js` | | API routes | `pages/api/**/*.js` | Express-style `handler(req, res)`. **30+ handlers depend on `lib/permission-middleware.js::getUserFromRequest`** | | Shared UI | `components/*.js` | `Layout`, `CardItem`, `CameraScanner`, modal family | | Auth + DB libs | `lib/*.js` | `auth-context`, `admin-auth`, `use-auth` (three parallel auth surfaces), `database`, `permission-middleware` | | Migration scripts | `scripts/*.js` | 27+ one-off "add column" / "seed" scripts. No formal migration tool | | Card-import jobs | `pages/api/cards/import-*.js`, `scripts/import-*.js` | Scryfall / Lorcana / Pokémon TCG APIs | | Database schema | `scripts/setup-neon-db.js` | Bootstrap SQL DDL — the source of truth until a real migration tool lands | | Schema map | `docs/SCHEMA_MAP.md` | Hand-curated; regenerate after schema changes | Code graph is indexed by `user-code-review-graph` MCP (122 files, 628 nodes, 5602 edges). Ask: *"what calls `getUserFromRequest`?"* before refactoring auth. ## 3. Key conventions - **Auth (server):** `import { getUserFromRequest } from '../../lib/permission-middleware'` → returns `{ userId, email, role }` or `null`. **IMPORTANT: the current implementation returns a hardcoded admin user when no Bearer token is present — treat that as a known prod bug, do NOT copy the pattern.** - **Auth (client):** `import { useAuth } from '../lib/use-auth'`. Avoid `lib/auth-context.js` and `lib/admin-auth.js` for new code — they are legacy parallel implementations. - **Auth helper (JWT only):** `import { ... } from '../../lib/api/auth-utils'` (`generateToken`, `verifyToken`, `hashPassword`, `verifyPassword`). - **Permission gate for collection routes:** wrap handlers with `withCollectionPermission('viewer' | 'editor' | 'owner')` from `lib/permission-middleware.js`. - **DB access:** Use **tagged-template** style — `import { sql } from '@vercel/postgres'`. Avoid the legacy `lib/database.js` `db.query(string, params)` API; its parameter interpolation uses `sql.unsafe` and is a SQL-injection vector. - **Activity logging:** `logCollectionActivity(collectionId, userId, action, details)` — call it from any handler that mutates a collection. - **File names:** `kebab-case.js` for libs/scripts; `PascalCase.js` for React components. - **Imports:** No path aliases configured; use relative imports. - **Slugs:** `lib/slug-utils.js::generateUniqueSlug` for any user-facing identifier (collections, decks). - **CSS theme tokens:** Components read `var(--bg-primary)`, `var(--text-primary)`, `var(--accent-ember)`, etc. — defined in `styles/`. Don't hardcode hex colors. ## 4. Common gotchas - **#1 — Two SQL clients live in parallel.** `@neondatabase/serverless` (used by `lib/database.js`) and `@vercel/postgres` (used by most `pages/api/**` handlers). New code: prefer `@vercel/postgres` tagged templates. Migration to a single client is tracked in `.convoys/`. - **#2 — `getUserFromRequest` has a dev fallback shipped to prod.** When no Bearer token is present it returns user 1 as admin. This is a critical security issue, NOT a feature. Don't rely on it; treat unauthenticated requests as 401. - **#3 — JWT_SECRET default is hardcoded across 7 files.** If `process.env.JWT_SECRET` is unset, tokens are signed with `'your-secret-key-change-in-production'`. The Vercel project MUST set `JWT_SECRET`; CI/staging too. - **#4 — Default admin credentials are in the seed.** `admin@tcgvault.com` / `admin123` from `scripts/setup-neon-db.js`. Change the password immediately after running setup. - **#5 — `pages/api/setup-database.js` is a public endpoint.** Anyone hitting it triggers DB DDL. Either delete or gate behind admin auth before public launch. - **#6 — Migrations are bare scripts.** `scripts/add-*.js` and `scripts/fix-*.js` are run-once jobs with no idempotency tracking. Adopt `node-pg-migrate`, `kysely`, or `drizzle-kit` before more schema changes. - **#7 — Dual `is_public` semantics.** Collections and decks both have `is_public` columns; check which controls discovery vs. anonymous read in the relevant route. - **#8 — Layout has hardcoded default user.** `Layout({ user = { email: 'me@randallstillwell.com', role: 'user' } })`. Anything rendering Layout without passing `user` will impersonate the maintainer. Pass `user` explicitly from every page. ## 5. Running locally - **Runtime:** Node 20 (Vercel default). - **Setup:** `npm install`, copy `.env.local` template (POSTGRES_URL + JWT_SECRET + RESEND_API_KEY + BLOB_READ_WRITE_TOKEN), then `npm run setup-db` once. - **Dev server:** `npm run dev` → http://localhost:3000. ## 6. Testing - **Runner:** None yet. Adding `vitest` + `@playwright/test` is in `.convoys/`. Until then: manual smoke per `TESTING_GUIDE.md`. ## 7. Deployment - **Vercel** auto-deploys `main` and creates Preview deployments for every PR. `vercel.json` and `.vercel/` are committed. CI in `.github/workflows/` runs lint + types (no duplicate build — Vercel handles it). ## 8. Code graph A local code-knowledge-graph MCP server (`user-code-review-graph`) is set up for this repo. Ask "what calls X?" or "show me the flow from /api/auth/login" instead of grepping. See [`docs/agent-context/README.md`](docs/agent-context/README.md).