diff --git a/SCRATCH/2026-02-07_customer-id-schema.md b/SCRATCH/2026-02-07_customer-id-schema.md new file mode 100644 index 0000000..f9f618e --- /dev/null +++ b/SCRATCH/2026-02-07_customer-id-schema.md @@ -0,0 +1,110 @@ +# 2026-02-07 — Customer ID schema + "hashed id" confusion + +## Current reality + +### User table + +Prisma schema: + +```prisma +model User { + id String @id @default(cuid()) + email String @unique + username String @unique + passwordHash String + + firstName String? + lastName String? + displayName String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} +``` + +* When a user registers via the API (`POST /api/auth/register`), Prisma assigns a **CUID** (looks "hashed" in Prisma Studio, e.g. `cmk07b7n4000q...`). +* That value is **not derived from the JWT token**. +* During testing you manually created a user with an ID like `u000` (and other prefixed IDs), which is why you're seeing mixed formats. + +### ContainerInstance.customerId (what broke) + +You observed `ContainerInstance.customerId` being **polluted**: + +* sometimes `u000` (manual) +* sometimes `u-dev-001` (hardcoded during testing/dev) +* sometimes a Prisma CUID (new registrations) + +Root cause (confirmed): + +* Frontend / provisioning calls were hardcoded to use `u-dev-001` as `customerId`. +* New registrations correctly used Prisma's default `User.id` (CUID), but container creation wasn't consistently using it. + +## Short-term decision (now) + +✅ **Use `User.id` as the authoritative customer identifier** everywhere. + +That means: + +* `customerId` in `ContainerInstance` should always be set to `req.user.id`. +* Stop inventing or hardcoding IDs (`u-dev-001`, etc.) in the frontend. +* Keep `username` / `displayName` for human-friendly display and troubleshooting. + +This keeps the system consistent immediately and avoids adding schema complexity right now. + +## Why this is OK + +Pros: + +* Guaranteed unique +* Harder to guess (good for security) +* Already produced by Prisma automatically +* No more "what format is customerId?" problems + +Cons: + +* Not pretty/human-friendly in logs (CUIDs) + +Mitigation: + +* When displaying in UI or logs, show a composite: + + * `displayName (username)` and optionally the first 6–8 chars of the id + * Example: `Jester (jester) • cmk07b7n4` + +## Long-term (optional) improvement + +If you later want a human-readable / sequential ID: + +Add a second field, e.g.: + +* `publicCustomerId` (`u001`, `u002`, etc.) generated by a counter table or dedicated allocator + +Then: + +* Keep `id` as the internal primary key (CUID) +* Use `publicCustomerId` only for: + + * display + * support tickets + * invoices / Stripe customer metadata + * external references + +But: don't block on this now. + +## Cleanup / migration note + +If you want to clean existing polluted rows later: + +* Update all `ContainerInstance.customerId = 'u-dev-001'` to the correct `User.id` (based on ownership mapping / audit trail / known associations). +* If no reliable mapping exists, leave historic test rows as-is and only enforce the rule going forward. + +## Implementation rule (what to enforce) + +* Provisioning endpoint should **ignore any client-supplied customerId**. +* Always derive from auth context: + +```js +const customerId = req.user.id; +``` + +* If you need admin tooling to create for others, that should be a separate privileged endpoint that explicitly sets ownership (and logs it).