credsource

package
v1.17.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 21, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package credsource is the inference-plane credential-source seam: it answers WHERE a runtime's LLM PROVIDER API KEY comes from — the process environment (boot config, the historical default) or a coordinator-served broker the runtime PULLS from at connect + refresh.

A DISTINCT seam, deliberately (not a §13 parallel implementation)

This seam follows the §4.4 seam PATTERN but is NOT the OAuth-plane `internal/tools/auth/credsource.Source`. The two resolve different credentials for different consumers with different custody models:

  • The OAuth seam resolves an OAuth provider's own client_id / client_secret for the tool-plane token-exchange driver, with a PER-IDENTITY, LAZY pull (a tool bearer is identity-scoped).
  • This seam resolves an LLM provider API KEY for the bifrost `Account`, with a RUNTIME-SCOPED connect + refresh pull (a provider key is a runtime-level credential, never identity-scoped data — it does not widen the isolation tuple).

Folding both into one driver would leak the per-identity assumptions the OAuth seam bakes in onto the runtime-scoped path; the sibling is justified, not incidental. The two MAY share the HTTP-pull mechanics (this file keeps them credential-agnostic and self-contained), but the interface and its custody model are distinct.

Connect + refresh only — never a per-call KEK decrypt

The pulled key is cached in memory and rides the existing atomic key-swap (llm.LiveKey): the bifrost `Account.GetKeysForProvider` reads the live atomic pointer on the inference HOT PATH with no broker round-trip and no per-call decrypt. A broker-side rotation lands on the next call via InferenceKeySource.Refresh, with no `ReloadConfig` race.

Fail loud, no dual path, no stale cache

A broker unreachable at connect OR a failed refresh raises the typed ErrProviderKeyUnavailable. The runtime NEVER falls back to a local / boot key, and NEVER serves a stale cached key past its refresh contract: once the cache TTL is crossed with no successful refresh, the live key is zeroed so bound calls fail closed rather than serve a possibly-revoked key. A provider is brokered XOR local, config-declared (validated loud at boot). Nothing is persisted — the coordinator remains sole custodian.

Index

Constants

View Source
const (
	// RuntimePrincipalTenant is the reserved tenant component of the
	// runtime service-principal scope.
	RuntimePrincipalTenant = "_harbor_runtime"
	// RuntimePrincipalUser is the reserved user component of the runtime
	// service-principal scope.
	RuntimePrincipalUser = "_service_principal"
)

Reserved runtime-scope identity components. A runtime-scoped credential pull fires outside any (tenant, user, session) request, but the event bus requires a structural triple on every published Event. These reserved components satisfy that transport requirement WITHOUT synthesizing a real user's session: they are infrastructure identity, never an isolation principal, and storage / memory / event-filter scoping never treats them as one (they do not widen the isolation tuple, §4). The authoritative audit attribution is the payload's RuntimePrincipal, not this triple.

Variables

View Source
var ErrProviderKeyUnavailable = errors.New("llm/credsource: provider key unavailable from broker (fail-loud; no local fallback, no stale cache)")

ErrProviderKeyUnavailable — the broker was unreachable at connect OR a refresh failed. The runtime NEVER falls back to a local key and NEVER serves a stale cached key past its refresh contract. Callers compare via errors.Is.

Functions

This section is empty.

Types

type Config

type Config struct {
	// Provider is the LLM provider the key authenticates (e.g. "openai").
	Provider string
	// BrokerName is the boot-declared inference-broker name (non-secret,
	// audit + error attribution).
	BrokerName string
	// RuntimePrincipal is the per-runtime service-principal id the pull
	// audit event is attributed to. Non-secret. Falls back to AuthTokenEnv
	// (the env var NAME, still non-secret) when empty.
	RuntimePrincipal string
	// CredentialURL is the boot-pinned coordinator pull endpoint.
	CredentialURL string
	// AuthTokenEnv names the env var holding the runtime's own broker
	// credential (the Bearer presented to CredentialURL).
	AuthTokenEnv string
	// CacheTTL caps the in-memory serve horizon (0 = default 5m).
	CacheTTL time.Duration
	// Timeout bounds a single pull (0 = default 30s).
	Timeout time.Duration
	// Live is the atomic key holder the bifrost Account reads. Required.
	Live *llm.LiveKey
	// Bus + Redactor are mandatory — every pull emits a runtime-scoped
	// audit event through them, and a pull that cannot be audited fails loud.
	Bus      events.EventBus
	Redactor audit.Redactor
	// Clock overrides the time source (tests). Nil = time.Now.
	Clock func() time.Time
	// HTTPClient overrides the fetch client (tests). Nil = a fresh client
	// with the resolved timeout. A caller-supplied client is shallow-copied
	// so its CheckRedirect is set without mutating the caller's instance.
	HTTPClient *http.Client
}

Config is the constructor input for an InferenceKeySource. The endpoint / audience / scope ceiling come from the boot-declared broker (never the wire); the Live holder is the SAME atomic key the bifrost Account reads, so a pull lands on the next hot-path read with no ReloadConfig.

type InferenceKeySource

type InferenceKeySource struct {
	// contains filtered or unexported fields
}

InferenceKeySource pulls a provider key from a boot-declared broker at connect + refresh, caches it in memory (TTL-capped, single-flighted), and seeds / refreshes the LiveKey the bifrost Account reads. Immutable after construction; safe to share across N goroutines. It NEVER decrypts on the per-call hot path — the hot path reads the live atomic pointer.

func NewInferenceKeySource

func NewInferenceKeySource(cfg Config) (*InferenceKeySource, error)

NewInferenceKeySource constructs the source. It validates the block SHAPE (endpoint well-formed https / loopback, auth-token env named, Live / Bus / Redactor present) but does NOT fetch — the pull happens at Connect.

func (*InferenceKeySource) Close

func (s *InferenceKeySource) Close()

Close closes the binding: the live key is zeroed so a subsequently bound call fails loud rather than serving the old key (the provider-SET uninstall contract — never a silent no-op serving the old key). The backstop timer is stopped. A running Run scheduler is stopped separately by cancelling its ctx.

func (*InferenceKeySource) Connect

func (s *InferenceKeySource) Connect(ctx context.Context) error

Connect performs the first authenticated pull and seeds the live key. Fails loud with ErrProviderKeyUnavailable on broker-unreachable — the caller (boot wiring) exits non-zero naming the broker + missing config.

func (*InferenceKeySource) Refresh

func (s *InferenceKeySource) Refresh(ctx context.Context) error

Refresh re-pulls on the TTL boundary and atomically swaps the live key. A failed refresh raises ErrProviderKeyUnavailable; once the cache TTL is crossed with no successful refresh the live key is zeroed rather than served stale (a broker-side revocation that fails to propagate surfaces).

func (*InferenceKeySource) Run

func (s *InferenceKeySource) Run(ctx context.Context)

Run drives the refresh cadence off the broker's expiry: it re-pulls s.refreshLead ahead of the serve horizon so a fresh key is seeded before the current one expires. It is ctx-cancellable and returns when ctx is done (join it on shutdown). A refresh failure is non-fatal to the loop — onFetchFailed already drops a stale key past its horizon and the pull re-emits its audit event; the loop retries on the next tick. Call Connect once before Run so the first key is seeded synchronously (a boot-fail-loud point); Run keeps it fresh thereafter.

type KeySource

type KeySource interface {
	// Connect performs the first authenticated pull and seeds the LiveKey.
	// Fails loud with ErrProviderKeyUnavailable on broker-unreachable.
	Connect(ctx context.Context) error
	// Refresh re-pulls on the TTL boundary and atomically swaps the LiveKey.
	// A failed refresh raises ErrProviderKeyUnavailable; once the cache TTL
	// is crossed with no success the live key is zeroed rather than served
	// stale.
	Refresh(ctx context.Context) error
	// Close closes the binding: the live key is zeroed so a subsequently
	// bound call fails loud rather than serving the old key.
	Close()
}

KeySource is the §4.4-pattern seam interface an inference-plane credential source implements. It is a MANDATORY interface (no optional-capability ceremony): the source pulls the provider key at connect + refresh, seeds / refreshes the llm.LiveKey the bifrost `Account` reads, and closes the binding on uninstall.

Concurrent reuse: a KeySource is a compiled artifact built once and shared across N goroutines. It holds immutable configuration plus an internally-synchronised in-memory cache; the inference hot path reads the LiveKey atomic pointer, never the source.

Directories

Path Synopsis
Package inferencebrokertest ships the committed REFERENCE implementation of the inference-plane credential-pull contract: an httptest server that serves an LLM provider's API key to the broker-pull [InferenceKeySource].
Package inferencebrokertest ships the committed REFERENCE implementation of the inference-plane credential-pull contract: an httptest server that serves an LLM provider's API key to the broker-pull [InferenceKeySource].

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL