Background
Admin already exists in the schema (id, address, createdAt, updatedAt) but has no way to actually get a row into it — there is deliberately no admin self-registration endpoint (unlike merchants, who provision themselves on first wallet sign-in). The first admin has to be created some other way: a terminal script. Every other admin issue depends on this model's final shape.
Proposed Steps
- Extend the
Admin model (see Schema Changes) and run prisma migrate dev.
- Create
scripts/create-superadmin.ts:
- Accepts
--address=<G...> and --name="..." CLI args
- Validates the address with
StrKey.isValidEd25519PublicKey (same check used in createChallengeController) — invalid address exits non-zero, no DB write
- Rejects (loudly, non-zero exit) if an
Admin row for that address already exists — this must never silently overwrite or duplicate; re-running with the same address is a mistake to catch, not a no-op to swallow
- Creates the row with
isSuperAdmin: true, active: true, createdBy: null (the bootstrap admin has no creator)
- Add
"admin:create-superadmin": "tsx scripts/create-superadmin.ts" to package.json scripts.
- No HTTP route is created anywhere for this. It is intentionally CLI-only.
Schema Changes
Admin (extend existing model)
name String
isSuperAdmin Boolean @default(false)
active Boolean @default(true)
createdBy String? (FK → Admin.id, self-relation, nullable — null only for the script-bootstrapped admin)
Acceptance Criteria
Background
Adminalready exists in the schema (id,address,createdAt,updatedAt) but has no way to actually get a row into it — there is deliberately no admin self-registration endpoint (unlike merchants, who provision themselves on first wallet sign-in). The first admin has to be created some other way: a terminal script. Every other admin issue depends on this model's final shape.Proposed Steps
Adminmodel (see Schema Changes) and runprisma migrate dev.scripts/create-superadmin.ts:--address=<G...>and--name="..."CLI argsStrKey.isValidEd25519PublicKey(same check used increateChallengeController) — invalid address exits non-zero, no DB writeAdminrow for that address already exists — this must never silently overwrite or duplicate; re-running with the same address is a mistake to catch, not a no-op to swallowisSuperAdmin: true,active: true,createdBy: null(the bootstrap admin has no creator)"admin:create-superadmin": "tsx scripts/create-superadmin.ts"topackage.jsonscripts.Schema Changes
Admin (extend existing model)
Acceptance Criteria
Adminmodel updated;prisma migrate devruns cleanlynpm run admin:create-superadmin -- --address=<valid G...> --name="Jane Doe"creates a row withisSuperAdmin: true,active: trueAdmin→ script exits non-zero, no overwrite or duplicate row