---
id: w6/m7/t001
title: own- id kind + subject⇄own-id persistence (id registry, store table + migration, resolver)
worker: worker6
status: done
estimate: 45m
depends_on: []
---

## Objective

Give every identity a stable, opaque `own-<xid>` id. Register the `own` kind in
`internal/id`, persist a subject⇄own-id mapping in the control-plane store (so
the same subject always resolves to the same `own-` id and an `own-` id reverse-
maps to its subject), and expose a small resolver the owners/members code calls.

## Context

- `lego/backend/internal/id/id.go` — registered kinds today: `tea`/`srv`/`cdm`/`evg`/`dep`/`inv`/`exp`/`aud`. No `own`. A new kind is added here (and nowhere else); the guard test then holds it to the `<prefix>-<xid>` DNS-safe format (docs/ADR020-identifiers.md).
- Kratos subjects are UUIDs, not xids — so `own-<xid>` must be **minted + stored**, not derived. Precedent for a control-plane table + migration: `lego/backend/internal/store/migrations/` (0001–0007) + `internal/store` queries.
- `docs/render-artifacts/owners-api.md` pins the prefix as `own-` (not `usr-`).

## Steps

1. Add `Owner = id.Kind{prefix: "own", desc: "user/identity (Render own-)"}` to `internal/id/id.go`; the guard test picks it up.
2. New migration `internal/store/migrations/0009_owner_ids.up.sql` / `.down.sql`: a table mapping `subject` (unique) ⇄ `own_id` (unique).
3. `internal/store`: `OwnerIDForSubject(ctx, subject) (ownID string, error)` — get-or-create, mint via `id.New(id.Owner)` on first sight (race-safe upsert). (No reverse `own-→subject` lookup: it existed only for peer-resolve, a dropped non-goal — YAGNI.)
4. Surface it on the workspaces service's `WorkspaceStore` interface (satisfied by the store), so t002 has one source of truth.

## Files

- `lego/backend/internal/id/id.go`
- `lego/backend/internal/store/migrations/0009_owner_ids.{up,down}.sql`
- `lego/backend/internal/store/` (queries + interface method)
- `lego/backend/internal/workspaces/service.go` (resolver wiring)

## Acceptance criteria

- [ ] `id.Owner` registered; the id guard test passes with the new kind
- [ ] Same subject → same `own-` id across calls; distinct subjects → distinct ids
- [ ] Migration up/down apply cleanly (numbered 0009, no duplicate version)

## Out of scope

- Emitting the own- id anywhere (t002/t003) or resolving it to a workspace (t004).
