feat(seed): require ADMIN_INITIAL_PASSWORD + convert setup-db to ESM (drop-public-setup) #13

Merged
varutasu merged 5 commits from convoy/drop-public-setup into main 2026-05-23 18:02:44 -04:00
Showing only changes of commit d2f5c4f9af - Show all commits

View file

@ -0,0 +1,88 @@
---
name: drop-public-setup
classification: convoy
success_metric: |
No hardcoded admin credentials in repo (source or README). setup-neon-db.js
requires ADMIN_INITIAL_PASSWORD env var. New env-var setup documented in
README. Existing deployed admin user (if hash matches the known-weak default)
is either rotated or flagged for manual rotation.
skip:
- role-design-system-auditor
- role-a11y-auditor
- role-ux-reviewer
- role-ia-architect
- browser-smoke
status: open
created: 2026-05-23
parent: ship-readiness
addresses: P0 #3
depends_on:
- bump-next-js (shipped)
- fix-auth-bypass (shipped — already removed pages/api/setup-database.js)
---
# Drop public setup
Close P0 #3 from `.convoys/ship-readiness.md`: remove the hardcoded admin
credentials (`admin@tcgvault.com` / `admin123`) from the seed script and
the README.
## Scope (verbatim from ship-readiness P0 #3)
- **`scripts/setup-neon-db.js` lines 130-138** — currently creates the admin
user with a hardcoded `admin123` bcrypt hash. Change to require an
`ADMIN_INITIAL_PASSWORD` env var with no default. Fail loudly (exit
non-zero with a clear message) if the env var is unset.
- **`README.md`** — strip the "Default Admin Account" section. Replace
with "run `npm run setup-db` and follow the prompt" (or however the
architect decides to phrase it; the spec is "no credentials in README").
- **`pages/api/setup-database.js`** — already deleted by fix-auth-bypass
Brief 3 (commit fc0dd73). Not in scope here; just noting for completeness.
## Out of scope
- Migration tooling (`migration-tool` convoy, P1 #11)
- Renaming the admin email from `admin@tcgvault.com` to anything else
(branding decision belongs to `pick-a-name` convoy, P1 #12)
- Any other `scripts/seed-*.js` or `scripts/add-*.js` files (those are
one-off historical jobs per no-go-zones)
- Any change to `pages/api/auth/*.js` (fix-auth-bypass shipped; further
hardening lives in `cors-tighten` / `add-rate-limiting`)
## Architect's questions
1. **Existing-admin story.** If a deployed environment already has the
admin row with `bcrypt.hash('admin123', ...)`, this convoy alone
does NOT rotate that password — it only prevents the weak default
on *new* envs. Options:
a. Going-forward only. Flag for manual rotation; assume any
environment with the default already needs operator attention.
b. Add a one-time idempotent script under `scripts/migrations/`
that checks the admin row's password_hash against the known
weak default and forces a reset / requires `ADMIN_RESET_PASSWORD`
env var.
c. Add a startup check to the deploy pipeline that fails the
deploy if the admin row matches the weak hash.
Recommend the architect pick (a) for simplicity and queue (b) as
a follow-up if needed. (a) is consistent with how Brief 1 of
fix-auth-bypass handled the JWT_SECRET — going-forward fix-loud,
not a sweep of existing data.
2. **`setup-neon-db.js` no-go-zones rule.** The rule says "do NOT
edit `scripts/setup-neon-db.js` in place — it's idempotent and
meant for first-time setup only." That rule is about *schema*
changes (adding columns, etc.). This convoy edits the script's
admin-creation logic to add an env-var gate — operational, not
schema. Architect should confirm this reading and document the
distinction in the brief.
3. **Test coverage.** Brief 5 of fix-auth-bypass introduced vitest.
Does this convoy add tests for the env-var-required path? Probably
not (setup scripts are typically tested via manual smoke, not unit
tests). Architect decides.
## Expected size
1-2 briefs, ~3 files total, no UI, no API surface, no migration. Should
ship as a single PR (no fan-out).