CredentialUpdateRequest

Group agentprimitives.authzed.com · Scope Namespaced · Short names credupdate

CredentialUpdateRequest is an agent's REQUEST that a human replace a credential it believes has stopped authenticating. The spec names only the failing TOOL -- the agent has no vocabulary for naming an identity, which is what stops it pointing at a credential it was not already using.

Namespaced. Reconciled by pkg/controllers/credentialupdaterequest, which independently re-verifies before any card is shown and refuses outright when the credential still authenticates. It is a request, never a decision: the verdict table lives in pkg/platform/identity/credupdate, and this CR only records what was asked and what was determined.

Spec

FieldTypeDescription
spec.origin *stringOrigin is the failing tool's tool.OriginTool.Origin() value, e.g. "mcpserver/github". The reconciler resolves this to a credential.
spec.requestedBy *stringRequestedBy is the canonical subject of the turn's author.
spec.sessionRef *objectSessionRef is the AgentSession that parked on this request. The CR also carries an owner-ref to it, so the request lives exactly as long as the session does — which is what makes the ask budget derivable by listing rather than storing a counter.
spec.sessionRef.name *stringName is the object's name.
spec.sessionRef.namespace *stringNamespace is the object's namespace.
spec.toolName *stringToolName is the failing tool's LLM-facing name, recorded for the card and for audit. Not used for resolution.
spec.whystringWhy is the agent's own explanation. UNTRUSTED and agent-authored: it is the ONLY agent-written string that ever reaches the card, where it is rendered in a visually distinct attributed block. Capped by the meta tool before it is written here.
* required

Status

Status is controller-owned (observed state).

FieldTypeDescription
status.collapsedIntoobjectCollapsedInto names the CredentialUpdateRequest whose card this request is waiting on, set when the reconciler resolved to a credential another request ALREADY holds a live card for: five sessions sharing one dead bot token must raise one card at one admin, not five. Empty on every request that owns its own card, so "is this a follower?" is one nil check. Stored on the FOLLOWER, never mirrored as a list on the canonical: one writer per field (a follower's own reconcile sets its own pointer, so no two contend), and the canonical's follower set stays DERIVED by listing rather than stored and kept in sync, because a mirrored list desyncs.
status.collapsedInto.name *stringName is the object's name.
status.collapsedInto.namespace *stringNamespace is the object's namespace.
status.conditions[]objectConditions carries CardDelivered; see its type constant.
status.conditions[].lastTransitionTime *string (date-time)lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
status.conditions[].message *stringmessage is a human readable message indicating details about the transition. This may be an empty string.
status.conditions[].observedGenerationinteger (int64)observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. (min 0)
status.conditions[].reason *stringreason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty.
status.conditions[].status *stringstatus of the condition, one of True, False, Unknown. (enum: True | False | Unknown)
status.conditions[].type *stringtype of condition in CamelCase or in foo.example.com/CamelCase.
status.credentialSecretKeystringCredentialSecretKey is the Secret data key the resolved credential occupies, written alongside CredentialSecretRef. Together they are the credential's full backing coordinates — namespace, name, key — the only handle that identifies a credential rather than the identity CR it was reached through. Empty for a type=oauth credential, whose value is the WHOLE Secret rather than one key of it; that emptiness is the discriminator, not a missing value. A non-empty key scopes change-detection to that key alone, because the operator projects EVERY static credential of a session into one shared Secret and a whole-Secret digest cannot tell "this credential was replaced" from "some other credential was linked".
status.credentialSecretObservedHashstringCredentialSecretObservedHash is a SHA-256 digest of the backing Secret's Data as observed when the request opened. The Secret watch compares the CURRENT hash against this recorded baseline -- a real content change (not an unrelated metadata/label edit that only bumps resourceVersion) is what marks the request Fulfilled.
status.credentialSecretRefobjectCredentialSecretRef names the backing Secret the resolved credential's value lives in, recorded once the request opens a card. It is what the reconciler's Secret watch uses to find the CredentialUpdateRequest(s) a changed Secret should unpark -- an OBSERVATION (set once, never user-supplied), not part of the ask itself.
status.credentialSecretRef.name *stringName is the object's name.
status.credentialSecretRef.namespace *stringNamespace is the object's namespace.
status.determinationstringDetermination records WHY the reconciler opened or refused. Surfaced to the agent verbatim, so a refusal is never silent.
status.interactionRefstringInteractionRef is the requestRef of the published interaction, set only when the request opened a card.
status.observedGenerationinteger (int64)ObservedGeneration is the spec generation this status reflects.
status.openedAtstring (date-time)OpenedAt is when the reconciler transitioned this request to Open — the instant a human's wait window actually starts. A controller-owned observation, written ONCE on that transition and never rewritten. Deliberately not CreationTimestamp: creation and determination are separated by however long the pipeline took (an operator restart, minutes of probe backoff against an unreachable provider), and measuring from creation let a deadline elapse before the card existed — Open written, channelsd publishing, and the next reconcile expiring it, blaming a human who had no window at all. Empty on requests opened before this field existed, where CreationTimestamp remains the fallback.
status.phasestringPhase is the request's lifecycle position. Fulfilled, Refused and Expired are terminal — see IsCredentialUpdateRequestTerminal.
status.reasonstringReason is the human-readable determination text. It doubles as the card's verdict line, and is platform-authored in every case.
status.resolvedCredentialobjectResolvedCredential is what Origin resolved to. Empty when resolution itself failed (NoCredential / AmbiguousCredential).
status.resolvedCredential.credential *stringCredential is the credential name within that identity.
status.resolvedCredential.identityKind *stringIdentityKind is the identity CR's Kind spelling: "AgentIdentity", "UserIdentity", or "SessionUserIdentity". SessionUserIdentity is the per-session projection of a UserIdentity — both are user-owned (passthrough) credentials, as distinct from an AgentIdentity's own.
status.resolvedCredential.name *stringName is the identity CR name.
status.resolvedCredential.namespace *stringNamespace is the identity CR's namespace; empty for cluster-scoped kinds.
status.resolvedCredential.providerIDstringProviderID is the provider catalog id, recorded so the card can render a title and icon without re-resolving.
* required