TCG Vault - Trading Card Game Collection Management with OCR Scanning
Find a file
Randall Stillwell d640795959 docs: post-convoy cleanup for pick-a-name — first post-P0 P1 convoy
Reflects the merged pick-a-name convoy (PR #21, squash commit 9abbab6)
in repo documentation. **This is the first post-P0 cleanup** —
add-rate-limiting (squash 708ef45) closed the LAST P0 ship-blocker on
2026-05-24, and pick-a-name immediately follows as the first post-P0
P1 quality-bar convoy, closing P1 #12 (brand-consistency — the
inconsistency `AGENTS.md` line 5 had flagged since project setup).
Two file-disjoint briefs landed serially (B1 commit ac8c998
display + comment sweep, B2 commit 1c18d21 infrastructure + email
migration); five canonical-string D-decisions ratified verbatim at
gate-1 plus Risk 4 PRESERVE on the negative regression-lock literal.
Architect commit 50ce9ab; no scope expansions and no implementer
deviations from the verbatim spec beyond the parallel-vs-serial
dispatch choice (parallelizable in theory; serial in practice for
lockfile-regen + migration-script audit cleanliness).

.convoys/pick-a-name.md:
  - frontmatter Status: in-progress -> shipped (added Shipped:
    2026-05-24 line, matching the file's existing **Status:** /
    **Opened:** markdown-line shape rather than YAML frontmatter).
  - new ## As-shipped section replacing the placeholder
    "(To be appended post-merge by role-doc-writer.)" stub. Opens
    with the post-P0 transition language pointing at ship-readiness's
    "8 of 8 RESOLVED" baseline + this convoy's role as the first
    P1 quality-bar closure. Decisions section captures all five
    D-ratifications verbatim (D1 Deck Hearth two-word internal-cap;
    D2 deck-hearth kebab matching the existing CSS class + Node
    convention; D3 deckhearth single-token for IDs/Redis; D4
    admin@deckhearth.com placeholder TLD pre-domain-acquisition,
    swept across admin/alice/bob uniformly; D5 full deckhearth
    Redis prefix not abbreviated dh, with the architect's
    self-documenting-prefix-vs-memory-savings rationale preserved)
    plus the Risk 4 PRESERVE on test/lib/permission-middleware.test.js
    line 87's negative regression-lock literal — including the
    project-pattern justification (Gotchas #2/#3/#4/#5/#8 in
    AGENTS.md all preserve historical-shape locks for the same
    audit-trail reason). As-shipped surface broken into B1/B2 split
    with file-disjoint inventory + the serial-in-practice dispatch
    note (the conductor's parallel-via-multitask path stayed open as
    fallback). As-shipped diff captures B1 (7+/7-) + B2 (133+/30-)
    + lockfile STOP-on-churn confirmation (only the two `name` lines
    diffed; npm ci clean against regen). Empirical CI metrics from
    post-merge run 26387909625 (Aggregate gate 10s, Lint 45s 128
    baseline, forbidden-endpoints + forbidden-cors-headers 4s each,
    Playwright smoke 1m4s 3/3, Screenshot diff 1m5s continue-on-error
    swallow per Decision 4, vitest 21/21 in 35s with Risk 4 PRESERVE
    test green). Cross-validation finding: 4th convoy in a row where
    the same 3-test smoke spec defends the auth surface through a
    sweeping change (PR #15 Layout -> #19 CORS -> #20 rate-limit ->
    #21 brand-rename). Implementer-vs-architect calibration note on
    the B2 forecast paragraph vs § Full surface inventory — both
    correct, cross-reference between them could be tighter for the
    next architect. Operator-action-required-going-forward: run the
    new migration script before the next admin login attempt
    (idempotent + UNIQUE-collision-safe). What did NOT change audit
    trail (~25 lines of historical .convoys/ tcgvault references stay
    byte-identical per no-go-zones, plus seed-collections-*.js
    historical scripts with stale WHERE-clauses that now no-op
    harmlessly post-migration, plus repo-name references deferred to
    rename-repo-and-vercel-project, plus the docs/agent-context
    + add-api-route SKILL.md repo-name references that sweep with
    the same downstream convoy).

.convoys/ship-readiness.md:
  - § Status summary: P1-quality-bar-work-in-progress line added
    below the existing 8/8 RESOLVED milestone footer, citing
    pick-a-name as the first such convoy.
  - § P0 launch sequence step 5 (pick-a-name): marked RESOLVED
    2026-05-24 with the squash commit + brief shape + smoke metrics
    + operator-action callout inline, mirroring the format of step 4
    (add-rate-limiting RESOLVED) introduced by the previous
    doc-writer pass.
  - § Queued convoys: four new entries surfaced by pick-a-name
    inserted as a logical group after purge-weak-creds-from-helpers
    (the natural neighbor — all brand-bound + credential-bound
    post-pick-a-name follow-ups):
    - rename-repo-and-vercel-project (P2 polish; GitHub repo +
      Vercel project rename; auto-redirects make low-urgency; lists
      the 8 architect-verified literal-repo references that sweep
      together; Risk 5 .agent-context-manifest.yml source-tag
      re-evaluation noted).
    - point-domain-at-deckhearth (P2 polish; blocked on domain
      acquisition; DNS pointer + Vercel domain claim + admin email
      TLD swap if non-`.com`; current placeholder-email rationale
      preserved — auth uses email as identity not as mail target).
    - regenerate-brand-assets (P2 polish; favicon, OG images, social
      cards; requires design pass; cross-references the original
      Role-design-system-auditor finding about unifying DH logo +
      AnimatedFireLogo).
    - convert-reset-db-to-esm (P2 hygiene; CJS-in-ESM bug at
      scripts/reset-db.js:142; fold-into-purge-weak-creds-from-helpers
      noted since both convoys touch the same file).
  - seed-visual-baselines-on-linux entry: extended with the
    AFTER-pick-a-name ordering note (now satisfied — 9abbab6 merged
    2026-05-24); first Linux baseline will capture Deck Hearth strings
    not pre-rename TCG Vault strings.
  - purge-weak-creds-from-helpers entry: scope narrowed — the
    @tcgvault.com email-half is now satisfied by pick-a-name B2;
    remaining scope is the admin123/alice123/bob123 weak password
    literals only (which survived pick-a-name deliberately per
    Brief 2's scope boundary). Fold-into-convert-reset-db-to-esm
    suggested (overlapping file).

AGENTS.md:
  - line 5 (top-of-file branding note): rewritten in full from the
    B1-installed transitional shape ("Deck Hearth is the canonical
    product brand (ratified 2026-05-24...)") to the post-merge
    documented shape with the squash commit + PR # citation + the
    Vercel-project parenthetical + the Risk 4 PRESERVE caveat
    citing test/lib/permission-middleware.test.js. **This is the
    showstopper change** — it's the line that's been flagged as
    inconsistent since project setup; the rewrite finally closes
    the brand-consistency loop end-to-end.
  - line 13 (§ 1 Project overview Auth bullet): stale
    `admin@tcgvault.com` reference updated to `admin@deckhearth.com`
    to match the post-B2 seed state. Per Brief 2's "Files explicitly
    out of scope" line: "AGENTS.md line 13 = doc-writer scope".
  - Gotcha #4 (Default admin credentials in the seed): extended
    with a new paragraph below the existing operator-caveat copy
    documenting the post-pick-a-name email rename + the migration
    script + the migration-FIRST-then-setup-db ordering rule + the
    UNIQUE-collision-safe failure mode. Existing drop-public-setup
    operator-caveat copy preserved verbatim above the new content.
  - Gotcha #12 (Rate-limit env vars): per-class limiter table
    extended with a Redis-prefix column showing the renamed
    `deckhearth:*` values (was implicit/missing); one-liner about
    the rename + accepted counter reset added immediately below the
    table. Original env-var contract paragraph
    (KV_REST_API_URL / KV_REST_API_TOKEN, fail-closed-in-prod /
    warn-and-noop-in-dev) preserved verbatim above the new table.
    The defensive THROW + gate-ordering-rule + sixth-class-shape
    paragraphs below stay byte-identical from the
    add-rate-limiting milestone doc-writer pass.

.cursor/rules/api-routes.mdc:
  - § Rate limiting opening lineage line: extended to add the
    pick-a-name attribution (squash 9abbab6, PR #21, 2026-05-24)
    and to note the rename is namespace-only — call shape, return
    shape, and gate-ordering rules below are byte-identical
    post-rename. The convoy-attribution lineage now reads:
    fix-auth-bypass Brief 4 (auth class) -> add-rate-limiting (the
    other four classes + 7 routes) -> pick-a-name (Redis prefix
    rename). No example code blocks in this rule had `tcgvault:`
    literals to swap (verified via re-read); the per-class table
    earlier in the rule doesn't enumerate prefix values either,
    so the lineage line is the only edit needed.

Files explicitly NOT changed: package.json, package-lock.json,
lib/rate-limit.js, lib/**, pages/**, components/**, scripts/**,
test/**, tests/**, .github/workflows/**, README.md, TESTING_GUIDE.md,
playwright.config.js, eslint.config.mjs,
scripts/migrations/2026-05-24-rename-admin-email.js. Doc-writer
pass touches docs only. The four queued downstream convoys
(rename-repo-and-vercel-project, point-domain-at-deckhearth,
regenerate-brand-assets, convert-reset-db-to-esm) own the remaining
brand-bound + helper-bound source-code work; this commit only
records them in the queued-convoys ledger.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-25 03:04:29 -05:00
.convoys docs: post-convoy cleanup for pick-a-name — first post-P0 P1 convoy 2026-05-25 03:04:29 -05:00
.cursor docs: post-convoy cleanup for pick-a-name — first post-P0 P1 convoy 2026-05-25 03:04:29 -05:00
.github fix(security): drop wildcard CORS + redundant OPTIONS from 24 API routes (P0 #5) 2026-05-24 20:41:38 -05:00
components fix(layout+pages): default user=null + page audit sweep (P0 #7) (#15) 2026-05-24 14:31:37 -05:00
docs bootstrap: agent pipeline v0.5.0 + ship-readiness review 2026-05-23 02:31:26 -05:00
lib feat(brand): unify on Deck Hearth across in-repo strings + infra (P1 brand decision) 2026-05-25 02:28:29 -05:00
pages feat(brand): unify on Deck Hearth across in-repo strings + infra (P1 brand decision) 2026-05-25 02:28:29 -05:00
public Major redesign: Enhanced card display with particle effects, improved filters, and search functionality 2025-07-23 21:26:54 -05:00
scripts feat(brand): unify on Deck Hearth across in-repo strings + infra (P1 brand decision) 2026-05-25 02:28:29 -05:00
styles 🚀 Implement Mobile-First Navigation System 2025-08-01 18:18:21 -05:00
test feat(brand): unify on Deck Hearth across in-repo strings + infra (P1 brand decision) 2026-05-25 02:28:29 -05:00
tests feat(test): adopt @playwright/test + ship playwright.config.js + visual scaffold (P1 #10 step 2) 2026-05-24 19:25:18 -05:00
.agent-context-manifest.yml bootstrap: agent pipeline v0.5.0 + ship-readiness review 2026-05-23 02:31:26 -05:00
.gitignore feat(test): adopt @playwright/test + ship playwright.config.js + visual scaffold (P1 #10 step 2) 2026-05-24 19:25:18 -05:00
.npmrc Fix build compatibility issues 2025-07-22 10:32:15 -05:00
AGENTS.md docs: post-convoy cleanup for pick-a-name — first post-P0 P1 convoy 2026-05-25 03:04:29 -05:00
eslint.config.mjs bump: next 15.4.3 -> 16.2.6, ESLint flat config (v9 fallback), typescript devDep 2026-05-23 02:31:26 -05:00
next.config.js bump: next 15.4.3 -> 16.2.6, ESLint flat config (v9 fallback), typescript devDep 2026-05-23 02:31:26 -05:00
package-lock.json feat(brand): unify on Deck Hearth across in-repo strings + infra (P1 brand decision) 2026-05-25 02:28:29 -05:00
package.json feat(brand): unify on Deck Hearth across in-repo strings + infra (P1 brand decision) 2026-05-25 02:28:29 -05:00
playwright.config.js feat(test): adopt @playwright/test + ship playwright.config.js + visual scaffold (P1 #10 step 2) 2026-05-24 19:25:18 -05:00
postcss.config.js Major redesign: Enhanced card display with particle effects, improved filters, and search functionality 2025-07-23 21:26:54 -05:00
README.md feat(brand): unify on Deck Hearth across in-repo strings + infra (P1 brand decision) 2026-05-25 02:28:29 -05:00
tailwind.config.js Ultra-Smooth Hover System with Expanding Actions 2025-07-26 09:00:26 -05:00
TESTING_GUIDE.md feat(brand): unify on Deck Hearth across in-repo strings + infra (P1 brand decision) 2026-05-25 02:28:29 -05:00
vercel.json Simplify Vercel config to resolve API routing issues 2025-07-23 09:22:14 -05:00
vitest.config.js fix(layout+pages): default user=null + page audit sweep (P0 #7) (#15) 2026-05-24 14:31:37 -05:00

Deck Hearth

A modern trading card game collection manager built with Next.js and Neon Database.

🚀 Features

  • Card Management: Track your MTG, Pokémon, and Lorcana cards
  • Collection Organization: Create and manage card collections
  • Deck Building: Build and share decks
  • Authentication: Secure user accounts with JWT
  • Admin Panel: Manage cards and users
  • Real-time Pricing: Track card values

🛠️ Tech Stack

  • Frontend: Next.js 16 (Pages router), React 18, JavaScript (TypeScript is a devDep only — see AGENTS.md Gotcha #9)
  • Backend: Next.js API Routes
  • Database: Neon PostgreSQL (serverless)
  • Authentication: JWT with bcrypt (24-hour expiry; lib/auth-secret.js is the single source of truth for JWT_SECRET)
  • Rate limiting: @upstash/ratelimit on /api/auth/login + /api/auth/register (5 attempts / 15 min per IP)
  • Testing: Vitest (unit); Playwright queued
  • Styling: Tailwind CSS
  • Deployment: Vercel

📦 Installation

  1. Clone the repository

    git clone <repository-url>
    cd tcg-vault
    
  2. Install dependencies

    npm install
    
  3. Set up environment variables

    cp .env.example .env.local
    

    Update .env.local with your Neon database URL and a real JWT secret:

    POSTGRES_URL="postgresql://your-username:your-password@your-host/your-database"
    JWT_SECRET="<generate with: openssl rand -hex 32>"
    # Required for `npm run setup-db` — used once to hash the initial admin password.
    # Set in .env.local for local dev, or as a CI secret if you run setup from CI.
    ADMIN_INITIAL_PASSWORD="<generate with: openssl rand -base64 24>"
    # Optional — exercise the rate limiter locally. Without them, `lib/rate-limit.js`
    # warn-and-no-ops in dev. In production these are auto-provisioned by the
    # Vercel Upstash Marketplace integration.
    KV_REST_API_URL="https://<your-upstash-host>.upstash.io"
    KV_REST_API_TOKEN="<your-upstash-rest-token>"
    

    JWT_SECRET is requiredlib/auth-secret.js throws at import time if it's unset. ADMIN_INITIAL_PASSWORD is required for npm run setup-db — the script exits with code 1 if it's unset.

  4. Set up the database

    npm run setup-db
    
  5. Start development server

    npm run dev
    

🗄️ Database Schema

The application uses the following tables:

  • users - User accounts and authentication
  • cards - Card information and metadata
  • user_cards - User's card collections
  • collections - Named card collections
  • collection_cards - Cards in collections
  • decks - Deck definitions
  • deck_cards - Cards in decks

🔧 API Endpoints

Authentication

  • POST /api/auth/register - User registration
  • POST /api/auth/login - User login

Admin

  • GET /api/admin - Admin panel data

Health Check

  • GET /api/health - Application health

Note: Earlier revisions of this README also listed GET /api/test-db (and three other unauthenticated dev endpoints: /api/simple, /api/test-auth, /api/setup-database). All four were deleted in fix-auth-bypass Brief 3 (commit fc0dd73) and CI now blocks their reintroduction. Don't recreate them.

🚀 Deployment

This app is configured for deployment on Vercel:

  1. Connect your repository to Vercel
  2. Set environment variables in Vercel dashboard
  3. Deploy automatically on push to main branch

📁 Project Structure

tcg-vault/
├── pages/                 # Next.js pages and API routes
│   ├── api/              # API endpoints
│   │   ├── auth/         # Authentication routes
│   │   └── admin/        # Admin routes
│   ├── _app.js           # App wrapper
│   └── index.js          # Home page
├── lib/                  # Utility libraries
│   └── database.js       # Database adapter
├── scripts/              # Database setup scripts
├── public/               # Static assets
└── .env.local           # Environment variables

🔐 First-time admin setup

npm run setup-db creates a single admin user the first time it runs. The password is read from the ADMIN_INITIAL_PASSWORD environment variable; the script exits with code 1 (and does not open a database connection) if the variable is unset or empty.

  • Local dev: set ADMIN_INITIAL_PASSWORD in .env.local before running npm run setup-db. Use openssl rand -base64 24 (or any other strong source) to generate the value.
  • CI / Vercel: set ADMIN_INITIAL_PASSWORD as a project secret if setup ever runs from CI. The env var is only read by the seed script; runtime auth uses the per-user password stored in the database.
  • Admin email: the seed creates admin@deckhearth.com. Change the password immediately after first login via the app's profile settings.

Operators of envs that pre-date this change: npm run setup-db is idempotent (ON CONFLICT (email) DO NOTHING) — re-running it with ADMIN_INITIAL_PASSWORD set will not rotate an existing admin row's password. If your environment was set up before this change and still has the prior weak default, rotate the password manually via the app after logging in, or wait for the queued rotate-default-admin follow-up convoy.

Operators of envs that pre-date the pick-a-name convoy (2026-05-24): the admin row was renamed from admin@tcgvault.com to admin@deckhearth.com. Run node scripts/migrations/2026-05-24-rename-admin-email.js once after deploy to UPDATE any existing @tcgvault.com user rows (the admin row, plus alice/bob if npm run create-test-users was ever run). Re-running the migration after the first run is idempotent and prints "Nothing to migrate." Verify post-migration with psql $POSTGRES_URL -c "SELECT email FROM users WHERE email LIKE '%@tcgvault.com'" — expect zero rows.

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Submit a pull request

📄 License

MIT License - see LICENSE file for details