# ADR-003: Identity Context Ownership **Status:** Accepted **Date:** 2026-03-18 ## Context Global ADR-003 establishes that context helpers must live with their data owners. `Identity` is defined in the `rbac` package. Its context key, storage function, and retrieval function must also live here to avoid requiring any other package to know the key type. If the context key for `Identity` were defined elsewhere (e.g. in an `httpauth` module), any package that wanted to retrieve an identity from context would need to import `httpauth` — creating a coupling that does not make sense for packages that have nothing to do with HTTP authentication. ## Decision The unexported type `authContextKey struct{}` and the package-level variable `authKey = authContextKey{}` are defined in `identity.go` within the `rbac` package. Two exported functions manage the context lifecycle: - `SetInContext(ctx context.Context, id Identity) context.Context` — stores the identity value under `authKey`. - `FromContext(ctx context.Context) (Identity, bool)` — retrieves it, returning `false` when absent. The key type is unexported (`authContextKey`), which prevents any external package from constructing or comparing the key directly — only `rbac` can write or read the identity in context. This eliminates the risk of key collisions with other packages that might also use an empty struct as a context key. ## Consequences - Any package that needs to read the authenticated identity imports only `rbac` — not `httpauth`, not any HTTP package. - `httpauth` middleware stores the identity via `rbac.SetInContext`; domain handlers retrieve it via `rbac.FromContext`. The two call sites share a single, well-known contract. - The unexported key type guarantees that no external package can accidentally shadow or overwrite the identity value by using the same key. - `PermissionProvider.ResolveMask` receives `ctx` explicitly; implementations that need the `TenantID` call `rbac.FromContext(ctx)` to obtain it — no need to thread tenant ID as a separate parameter.