Contextfulv0.23.0

Docs / Specification

contract connector · 40-connector.md · 41-secrets.md · 42-drive-extensions.md · guide · card

Outbound credentials

An outbound credential authenticates a pull, derive step or metered call to a vendor API. Connector implementers use this contract to declare, resolve, lease, attach, rotate and record credentials.

A credential from its reference to the vendor, keyed throughout by its logical name:

Flowchart diagram Flowchart, top to bottom. 11 nodes, 10 edges. file tree: secret declaration → resolver [secret://name] file tree: operator record → secret declaration [logical name] engine process: resolver → provider chain [first hit wins]; → run audit [answering adapter per name] engine process: provider chain → host attach [material in a redacting wrapper] engine process: rotator → operator-managed secret store [OAuth refresh, blind versioned put] engine process: host attach → vendor API [bound header, one host] engine process: outbound caller → host attach [guest, source, limiter, exec step] engine process: run audit lease endpoint → provider chain [value + expiry] deployment: operator-managed secret store → provider chain [stored secrets] file tree logical name secret declaration operator record engine process first hit wins material in a redactingwrapper guest, source, limiter, execstep answering adapter per name resolver provider chain rotator host attach outbound caller run audit deployment operator-managed secretstore secret://name value + expiry stored secrets OAuth refresh, blindversioned put bound header, one host lease endpoint vendor API

reference

Outbound credential references, the secret:// scheme and value templates.

resolve

The provider interface, adapter order, request-time resolution, credential buffers and deployment security configurations.

Flowchart diagram Flowchart, top to bottom. 9 nodes, 10 edges. secret reference → lease provider [lookup] lease provider → process environment [miss or undeclared name]; → also answered behind adapter? [first hit, first hydration] process environment → operator-managed secret store [miss or template not opted]; → also answered behind adapter? [first hit, first hydration] operator-managed secret store → unresolved reference [miss everywhere]; → also answered behind adapter? [first hit, first hydration] also answered behind adapter? → shadowed name [yes: SecretNameShadowed]; → redacting wrapper [no] redacting wrapper → resolved material [cached up to 300 s] lookup miss or undeclared name miss or template not opted miss everywhere first hit, first hydration first hit, first hydration first hit, first hydration yes: SecretNameShadowed no cached up to 300 s secret reference lease provider process environment operator-managed secretstore unresolved reference also answered behindadapter? shadowed name redacting wrapper resolved material

lease

The first credential adapter: credential lease declarations, mint requests, expiry, failures and the mint credential.

Sequence diagram Sequence diagram. 3 participants, 11 messages. Participants: resolver (R), adapters behind the lease provider (B), mint endpoint (P). 1. resolver → adapters behind the lease provider: hydrate the bootstrap mint reference 2. adapters behind the lease provider --> resolver: mint credential 3. resolver → mint endpoint: POST /leases: scope, mint credential, TLS alt success: 4. mint endpoint --> resolver: value + one expiry 5. resolver → resolver: hold in memory, retire 10 percent early else 404: 6. mint endpoint --> resolver: SecretLeaseScopeUnknown else 401 or 403: 7. mint endpoint --> resolver: SecretMintRejected · auth_expired else 429: 8. mint endpoint --> resolver: Retry-After to the run's retry else 3xx: 9. mint endpoint --> resolver: SecretLeaseRedirect · credential not replayed else other 4xx: 10. mint endpoint --> resolver: SecretLeaseRequestRejected else 5xx or transport error: 11. mint endpoint --> resolver: transient engine altsuccess404401 or 4034293xxother 4xx5xx or transport error resolverresolver adapters behind the leaseprovideradapters behind the leaseprovider mint endpointmint endpoint hydrate the bootstrap mintreference mint credential POST /leases: scope, mintcredential, TLS value + one expiry hold in memory, retire 10percent early SecretLeaseScopeUnknown SecretMintRejected ·auth_expired Retry-After to the run's retry SecretLeaseRedirect ·credential not replayed SecretLeaseRequestRejected transient

attach

Host handling of outbound requests: network transport, permitted hosts and addresses, credential headers, redirects and URL scrubbing.

rotate

Credential rotation responsibilities and mechanisms.

  • division-of-labortesttestA credential store contributes versioned storage, a trigger and room for an opaque expires_at hint beside the ciphertext. The host drives the OAuth loop from the connector’s auth policy and writes the token back as a blind versioned put.
    WhyA-connector#rotation-writes-forward
  • static-keytestA static key in an operator-managed secret store turns over with no engine step: a run hydrates the current version at its next cache miss.
    Whybecause the 300 s cache bound already caps staleness, and a per-manager change feed adds a listener and failure domain to every adapter
  • oauth-credentialtesttesttestThe host refreshes an OAuth credential at retirement-margin from its declared expiry. The host puts the refreshed version to the answering adapter, conditioned on the hydrated version, then clears the name’s cache entry.
    WhyA-connector#rotation-writes-forward
  • operator-enteredtestAn operator re-enters a development or keychain secret by updating the reference.
  • leased-credentialtestThe resolver re-mints a leased credential before its window ends without writing it back. If minting fails, the source uses no credential, including an older value.
    WhyP4
  • write-back-deniedtesttesttestA refused put or answering adapter without a write method raises SecretRotationWriteDenied. The host discards the refreshed token without serving it, and the run fails closed.
    WhyA-connector#rotation-writes-forward
  • conditional-puttestIf another writer stores a version first, the losing put rehydrates that version without raising an error.
    WhyA-connector#rotation-writes-forward
  • turnover-preserves-the-nametestEvery rotation mechanism keeps the logical name, touching no declaration and no pinned connector artifact.
  • write-halvestestOf the linked adapters only file carries a write method; env, lease and aws-secrets-manager answer reads alone.
    Whybecause a Secrets Manager put conditions on no current version, so a racing refresh overwrites a newer refresh token unseen
  • file-puttestThe file backend’s put holds an exclusive lock, compares the file’s digest with the hydrated version, and replaces the file by atomic rename with owner-only permissions; a file without write permission refuses the put.
    Whybecause a rename leaves a reader the old bytes or the new ones whole, and the lock orders two processes refreshing one name

oauth

The OAuth refresh a connector manifest declares, which the host runs for a component source’s attach templates.

Scenarios

record

Optional operator metadata, provider observations, the inventory of configured bindings, and the provider attribution a run emits.

  • operator-recordtesttesttestAn optional operator record describes one logical name’s intended grants, account or tenancy, consuming surfaces with their binding names, and rotation location, as descriptive metadata; recording it requires no credential value or decryption key.
    WhyA-connector#credential-custody-stays
  • provider-custodytestThe selected provider owns credential values, versions and rotation state; an external-manager binding requires no ciphertext copy in the repository.
    WhyA-connector#credential-custody-stays
  • optional-recordtestA binding without an operator record remains resolvable and appears in inventory with unknown descriptive fields.
  • unverified-scope-is-markedtestAn operator record marks grants as unknown when the operator has not established them.
  • inventorytestcontextful secrets list displays configured logical bindings with their configured adapters, operator metadata and available provider observations. The command reads no credential values and claims no complete provider inventory.
  • observation-provenancetestEach provider-observed field names its adapter and observation time; operator assertions remain separately labelled, including when they disagree with provider observations.
  • observation-unavailabletestInventory reports unsupported, denied or failed metadata reads as unavailable with a reason. Inventory neither substitutes operator assertions for provider observations nor hydrates values to obtain metadata.
  • expiry-unknowntestInventory reports an absent expiry as unknown. no expiry requires an explicit assertion or observation. Inventory computes days remaining only for a dated expiry and retains its provenance.
  • expiry-warningtestThe inventory marks each dated expiry falling within 30 d and each elapsed expiry, retaining the expiry’s provenance.
  • configured-is-not-observedtestA configured adapter identifies a binding’s configuration; only provider-attribution identifies the adapter that answered a run’s reference.
  • provider-attributiontestA run’s commit-phase and failure rows carry credential_providers. The field maps each hydrated logical name to the answering adapter, using names only.
  • record-tabletestAn operator record is a [secret.<name>] table in the discovered contextful.toml taking OperatorRecord, each key optional; expires = "never" is the one explicit no-expiry assertion.
    WhyA-connector#credential-custody-stays
  • record-rejectedtestA [secret.<name>] table outside discovered contextful.toml, without a manifest-bound secret:// name, or containing an unknown key, mistyped value or credential-shaped string raises SecretRecordRejected during declaration read. The error names file, name and key path, excluding values.
    Whybecause a misspelled name otherwise describes nothing while the real binding reads undescribed, and an echoed value carries a pasted token into CI logs
  • record-is-inerttestResolution, attachment, rotation and pipeline content hashing read no operator record. Editing a record changes no plan, run or hydrated value.
  • observe-porttestAn adapter’s observation interface returns presence, version, creation and expiry for a name, or unavailable with a reason. The interface holds no hydrated value. Inventory holds only observation interfaces.
    WhyA-connector#credential-custody-stays
  • inventory-outputtestsecrets list exits 0 whatever its marks; --json prints one object per binding per line: name, configured adapters, latest answered adapter with run id and instant, operator fields, and observations with adapter and time.

Scenarios

principal-secrets

Per-principal credential names and the adapter that serves their material.

Scenarios

Shapes

A declaration binding one credential through a template:

[pipeline.source]
name = "http"

[pipeline.source.config]
endpoint = "https://api.vendor.example/v1/records"   # one exact host: a credential attaches here
format   = "json"

[pipeline.source.config.headers]
Authorization = "Bearer ${secret://vendor-token}"