# Client-Assigned IDs The question of whether resource IDs are assigned by the server or provided by the client, and what that means for declarative clients, logging, and the shape of `core.ID`. ## The core motivation Server-assigned IDs are a pain point for declarative clients (Terraform, Pulumi, etc.). If state is lost, the client has no way to reconnect to an existing resource — it can only create a new one and leave the old one orphaned. Client-assigned IDs solve this: the client generates the ID before calling create, so the reference is always recoverable. Client-assigned IDs also enable **idempotent creates**: retrying a create with the same ID returns the existing resource rather than creating a duplicate. ## Tensions **core.ID format.** `core.ID` has a type prefix (`bot-`, `usr-`, etc.) that conveys the resource type at a glance and makes IDs stand out in logs. If clients provide arbitrary strings, the prefix convention breaks unless the format is enforced server-side. If `core.ID` must accept arbitrary client strings, its nice properties may need to be relaxed or its format requirements documented and enforced at the API boundary. **Log safety.** Randomly generated IDs are safe to log. Customer-defined IDs can carry PII or internal naming conventions (`alice-personal-account`, `prod-eu-west-database`) that you'd rather not have in logs or traces. This is a meaningful risk even if the format constraint is met. **Per-resource policy.** Client-assigned IDs make sense for some resources (configuration, infrastructure-shaped resources) and not others (users, sessions). A global stance may not be right; the framework may need to express a per-resource policy. **Middle path: client suffix, server prefix.** The server enforces the type prefix and validates the suffix format (alphanumeric, length bounds); the client provides the suffix. Preserves type-at-a-glance, but doesn't fully solve PII risk since the suffix is still client-controlled. ## Open question: optional slug as an alternative Rather than allowing clients to assign the primary ID, resources could optionally carry a **slug** — a caller-provided, human-readable, immutable identifier scoped within a tenant or parent resource. The primary ID remains server-assigned; the slug is a stable, client-controlled handle that declarative tools can use as their reference. Questions this raises: - Is slug optional or required? If optional, do some resources have it and others not? - Should slug be the primary lookup key for some resources (e.g. roles already use slug as the primary key)? - Does slug address the Terraform reconnect problem fully, or only partially (you can find the resource but still need to store the server ID in state)? - How does slug interact with the immutability convention (`Name` is mutable, slugs are not)? - If slug is the stable handle for declarative clients, does `core.ID` need to exist at the API surface at all for those resources, or is it purely internal?