zlh-grind/SCRATCH/2026-02-07_customer-id-schema.md

111 lines
3.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 68 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).