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