3.1 KiB
3.1 KiB
2026-02-07 — Customer ID schema + "hashed id" confusion
Current reality
User table
Prisma schema:
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-001ascustomerId. - 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:
customerIdinContainerInstanceshould always be set toreq.user.id.- Stop inventing or hardcoding IDs (
u-dev-001, etc.) in the frontend. - Keep
username/displayNamefor 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
idas the internal primary key (CUID) -
Use
publicCustomerIdonly 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 correctUser.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:
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).