Documentation
¶
Overview ¶
Package callerctx carries the request-scoped caller identity, policy inputs and correlation metadata that every apic-generated surface — REST, WebSocket and MCP — hands to business logic.
It depends on the standard library ONLY. securex, mcpx, wsx and every generated tree import it, so any other dependency would create a cycle.
Defensive copy at store time, shared and read-only after that ¶
ContextWithCaller takes a defensive copy of the Caller it is given: once stored, mutating the ORIGINAL struct (or its slices, its map, its nested Workload or Delegation chain) has no further effect on what the context carries. That copy is made exactly ONCE, at store time. Every later CallerFromContext or MustCaller call — on that context, or on any context derived from it (context.WithCancel, context.WithValue, ...) — returns the SAME *Caller pointer to every reader. A retrieved Caller is therefore shared and MUST be treated as read-only: mutating one of its fields races with every other goroutine that holds the same (or a derived) context, and mutating Attributes — a map — that way is a fatal concurrent write, not merely a race. A caller that needs to change a field must call Clone() first and mutate its own, independent copy.
ContextWithOwnedCaller: the one exception to "always cloned at store time" ¶
ContextWithOwnedCaller skips that defensive copy: it stores the exact *Caller pointer it is given. It exists for a caller that just constructed a Caller (e.g. a fresh value from securex.CallerFromRequest) and holds no other reference to it — skipping the clone avoids copying Roles/Scopes/AMR/Attributes/Delegation a second time for a value nothing else could have mutated anyway. Everything downstream of the store (CallerFromContext, MustCaller, the shared-pointer/read-only contract above) behaves identically either way; the only difference is which *Caller value ends up shared, and therefore who else must never mutate it. Using ContextWithOwnedCaller with a Caller the installer still holds a reference to — and might read or write again — reintroduces exactly the race ContextWithCaller's clone exists to prevent, since neither the installer's copy nor any reader's now alias a defensive copy; they alias the SAME value. Use ContextWithCaller whenever there is any doubt. Code generated by apic; DO NOT EDIT.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ContextWithCaller ¶
ContextWithCaller returns ctx carrying a defensive copy of c, cloned once at this call. A nil c returns ctx unchanged so a caller-less path stays caller-less rather than acquiring an empty-but-present principal. The clone made here — not c itself — is what every later CallerFromContext / MustCaller call returns, and it returns the SAME pointer to every reader; see the package doc for the resulting shared, read-only contract.
func ContextWithOwnedCaller ¶
ContextWithOwnedCaller returns ctx carrying c directly, with NO defensive copy. This is the fast path for a caller that just built c (e.g. a fresh value from a constructor) and holds no other reference to it: skipping the clone avoids copying Roles/Scopes/AMR/Attributes/Delegation a second time when the constructor already produced an isolated value nothing else can mutate.
The tradeoff is the same shared/read-only contract ContextWithCaller documents, PLUS an extra obligation on the caller of this function: once c is handed to ContextWithOwnedCaller, the caller must not retain a reference to c and mutate it later — that mutation would be visible through ctx (and every context derived from it) exactly like mutating a value retrieved via CallerFromContext, which the package doc already forbids. Use ContextWithCaller instead whenever c is retained, reused, or came from anywhere this function can't prove is exclusively owned by the call site (including a value read back from another context).
A nil c returns ctx unchanged, matching ContextWithCaller.
Types ¶
type Caller ¶
type Caller struct {
Subject string
Issuer string
Tenant string
Namespace string
ClientID string
Kind Kind
Roles []string
Scopes []string
Attributes map[string]string
AMR []string
ACR string
Delegation *Delegation
CorrelationID string
RequestID string
TraceID string
SpanID string
AuthMode string
Workload *Workload
}
Caller is the verified principal plus everything a handler needs to make an authorization decision, emit correlated telemetry and honour a deadline. Every field is server-derived from a verified credential — never from a request body, query parameter or unverified header.
func CallerFromContext ¶
CallerFromContext returns the caller carried by ctx: the SAME *Caller pointer for every call against this ctx, or against any context derived from it, shared across every reader. Treat the result as read-only — call Clone() before mutating any field, including writing to Attributes (see the package doc).
func MustCaller ¶
MustCaller returns the caller carried by ctx, or an unknown caller with no roles and no scopes. It never returns nil, so authorization code can be written without nil checks and still fails closed. Like CallerFromContext, the returned pointer is shared with every other reader of ctx (or of a context derived from it); treat it as read-only and call Clone() before mutating any field.
func (*Caller) Clone ¶
Clone deep-copies every mutable field, producing an independent *Caller that is safe to mutate. This is the supported way to get a private, writable copy of a value retrieved from a context (see CallerFromContext).
func (*Caller) DelegationChain ¶
DelegationChain returns the actor subjects from the innermost actor outward. A bounded walk: a malformed or cyclic chain cannot spin here.
func (*Caller) HasScope ¶
HasScope reports exact membership in Scopes. Matching is exact: scope strings are case-sensitive per RFC 6749 §3.3.
func (*Caller) LogValue ¶
LogValue renders a caller for structured logs WITHOUT tenant, namespace, attributes or the scope/role strings themselves. Tenant and namespace are high-cardinality and are forbidden as telemetry labels (ARCHITECTURE §9); scope and role names describe the authorization topology. Counts and the correlation identifiers are enough to trace a request.
type Delegation ¶
type Delegation struct {
Subject string
Issuer string
ClientID string
Parent *Delegation
}
Delegation is one link of an RFC 8693 token-exchange actor chain. Parent is the actor that delegated to this one, or nil at the root.
type Kind ¶
type Kind string
Kind classifies the principal behind a request. It is derived from verified token claims, never from request data.
const ( KindUnknown Kind = "unknown" KindHuman Kind = "human" KindService Kind = "service" KindWorkload Kind = "workload" KindDevice Kind = "device" )
The Kind values. KindUnknown is the fail-closed default -- what MustCaller returns when no caller is installed, and what a request whose credentials identified no principal is classified as. The others name the kind of verified principal (securex.CallerFromRequest documents which credential maps to which).