# ADR-002: Identity as a Value Type **Status:** Accepted **Date:** 2026-03-18 ## Context `Identity` travels through the request context from authentication middleware to handler and downstream service calls. When a type travels via `context.WithValue`, callers retrieve it with a type assertion. If the stored type were a pointer (`*Identity`), any middleware could mutate the struct fields after the value was placed in context, causing non-obvious bugs in concurrent or pipelined middleware chains. Additionally, pointer types retrieved from context require nil checks at every retrieval site. ## Decision `Identity` is declared as a plain struct, not a pointer: ```go type Identity struct { UID string TenantID string DisplayName string Email string } ``` `NewIdentity` returns `Identity` (value), not `*Identity`. `SetInContext` stores the value directly. `FromContext` retrieves it as a value and returns `(Identity, bool)`; the boolean indicates absence without requiring a nil pointer check. Enrichment methods return new values rather than mutating the receiver. `WithTenant(id string) Identity` copies the receiver, sets `TenantID` on the copy, and returns the copy. The original is unchanged, making it safe to call from concurrent middleware. The two-step construction pattern is intentional: 1. `NewIdentity(uid, displayName, email)` — populated from the auth token. 2. `id.WithTenant(tenantID)` — optionally enriched by a tenant-resolution middleware. ## Consequences - No nil checks are needed at retrieval sites. A zero-value `Identity{}` is returned when no identity is in context; the `bool` return of `FromContext` distinguishes "not authenticated" from "authenticated with empty fields". - Concurrent middleware cannot accidentally mutate an `Identity` already stored in context — each enrichment step produces a new value. - Copying `Identity` on every `WithTenant` call is negligible: four string fields, each a pointer-and-length pair internally. - The compliance test enforces that `WithTenant` returns `Identity` (not `*Identity`) at compile time, preventing a future regression to a pointer receiver.