docs: add customer ID schema decision (use Prisma CUID everywhere)
This commit is contained in:
parent
e2d41bedcd
commit
67b46d189f
110
SCRATCH/2026-02-07_customer-id-schema.md
Normal file
110
SCRATCH/2026-02-07_customer-id-schema.md
Normal file
@ -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).
|
||||||
Loading…
Reference in New Issue
Block a user