Skip to main content
The credential pipeline handles authentication for both local-process agents and K8s agents. It uses a three-layer model:
  • sources: named credential origins under auth_origins
  • resolvers: runtime helper commands under runtime_auth_resolvers
  • bindings: delivery rules that tell a host adapter how to consume credentials
The names are easy to read as peers, but they are not peers in time:
LayerMain questionRuns whereCommon local-process shape
sources”What credential origin exists?”Orchestrator before spawn, or agent runtime depending on scopeOften omitted entirely
resolvers”What helper command should the adapter call later?”Inside the agent environment at runtimebedrock-auth-helper
bindings”How does this adapter expect to receive auth?”Adapter projection / spawn wiringclaude_api_key_helper or bearer_env

At a Glance

Where config lives

fracta.yaml has two auth layers:
LocationPurposeTypical contents
auth.credentials.profiles.<name>Reusable credential profileauth_origins, runtime_auth_resolvers, env, assertions, default_binding
agents.agent_runtimes.<name>Runtime entry that selects a profileadapter, auth_profile, optional auth_binding override
Typical flow:
  1. Define a reusable profile under auth.credentials.profiles.
  2. Point a runtime at it with auth_profile.
  3. Override auth_binding on the runtime only when the same profile needs a different delivery shape for a different adapter.

How to read the names

Auth config mixes three different kinds of names:
KindWhat it isExamplesWho interprets it
Schema keywordsFixed field names and enum values from Fracta’s config schemaauth_profile, auth_origins, default_binding, secret_env, bearer_env, secret_refFracta
Local labelsUser-defined identifiers that other fields in the same config refer toopencode_bedrock, seeded_token, bedrock_helperFracta, by matching references
External namesNames that must match real env vars, K8s Secrets, Secret keys, or runtime-specific variablesAWS_BEARER_TOKEN_BEDROCK, AWS_REGION, fracta-auth, bearer-tokenKubernetes, the runtime, or both
Reference fields are what connect the local labels:
FieldRefers to
auth_profileA profile name under auth.credentials.profiles.<name>
auth_originA source name under auth_origins.<name>
runtime_auth_resolverA resolver name under runtime_auth_resolvers.<name>
ASCII relationship diagram:
SCHEMA KEYWORDS / ENUMS
  agents.agent_runtimes
  auth_profile
  auth_origins
  default_binding
  auth_origin
  type: secret_env
  type: bearer_env
  secret_ref

LOCAL LABELS (defined here, then referenced elsewhere)
  opencode_bedrock
  seeded_token

EXTERNAL NAMES (must match real runtime/K8s objects)
  AWS_BEARER_TOKEN_BEDROCK
  AWS_REGION
  fracta-auth
  bearer-token

RELATIONSHIPS

  agents.agent_runtimes.opencode.auth_profile
      |
      v
  profiles.opencode_bedrock
      |
      +--> auth_origins.seeded_token
      |         |
      |         +--> type: secret_env
      |         +--> env_name: AWS_BEARER_TOKEN_BEDROCK
      |         \--> secret_ref.name/key
      |                |
      |                +--> fracta-auth
      |                \--> bearer-token
      |
      \--> default_binding
                |
                +--> type: bearer_env
                +--> auth_origin: seeded_token
                \--> env_name: AWS_BEARER_TOKEN_BEDROCK

RESULT
   Fracta resolves local labels (`opencode_bedrock`, `seeded_token`)
  and projects the external token into the runtime env var
  `AWS_BEARER_TOKEN_BEDROCK`.
Annotated example:
agents:
  agent_runtimes:
    opencode:
      adapter: opencode                    # schema value
      auth_profile: opencode_bedrock      # local label -> profile name below

auth:
  credentials:
    profiles:
      opencode_bedrock:                   # local label
        auth_origins:
          seeded_token:                   # local label
            type: secret_env              # schema enum
            scope: any                    # schema enum
            env_name: AWS_BEARER_TOKEN_BEDROCK   # external env var name
            secret_ref:                   # schema field
              name: fracta-auth             # external K8s Secret name
              key: bearer-token           # external key inside that Secret
        env:
          AWS_REGION: "ap-southeast-2"    # external env var name
        default_binding:
          type: bearer_env                # schema enum
          auth_origin: seeded_token       # local label -> source name above
          env_name: AWS_BEARER_TOKEN_BEDROCK   # external env var name
Practical rule:
  • If changing the name requires changing a matching reference elsewhere in fracta.yaml, it is probably a local label.
  • If changing the name requires changing a real Secret, env var, or runtime expectation outside fracta.yaml, it is probably an external name.
  • If the name comes from the config schema itself, it is a schema keyword and should not be renamed.

Binding types

Bindings answer: “how do credentials reach the runtime?”
Binding TypeHow it worksTypical use caseNotes
claude_api_key_helperWrites Claude’s apiKeyHelper setting plus env into user-settings.jsonClaude + Bedrock bearer tokens with refresh/TTLClaude-specific projection
bearer_envInjects a token directly as an env varOpenAI API keys, Anthropic direct keys, Bedrock bearer env varsSupports source-based, resolver-based, or env passthrough shapes
token_fileMounts credential data as a fileK8s file-mounted secretsUses a materialized source with a configured path

Credential source types

Sources answer: “where does the credential come from?”
Source TypeWhat it doesCommon scopeTypical use case
http_header_tokenMakes an HTTP request and extracts a token from a response headeragent_runtimeBedrock/corporate proxy token fetch
command_outputRuns a local command and uses stdout as the credentialhost_edge or anybedrock-auth-helper for host-side or local-process token fetch. Use scope: any for local-process mode so the token materializes at spawn time in all topologies. The command field must be a YAML array (e.g. ["bedrock-auth-helper"]), not a string.
secret_envReferences a pre-existing K8s Secret key as an env varanyStatic API keys in Kubernetes

Quick examples

Claude with refreshable Bedrock auth:
auth:
  credentials:
    profiles:
      bedrock:
        runtime_auth_resolvers:
          bedrock_token:
            type: command
            command: bedrock-auth-helper
            ttl_ms: 60000
        env:
          CLAUDE_CODE_USE_BEDROCK: "1"
          CLAUDE_CODE_SKIP_BEDROCK_AUTH: "1"
          AWS_REGION: "ap-southeast-2"
        default_binding:
          type: claude_api_key_helper
          runtime_auth_resolver: bedrock_token

agents:
  agent_runtimes:
    claude:
      adapter: claude
      auth_profile: bedrock
Codex or OpenAI-style direct API key:
auth:
  credentials:
    profiles:
      openai:
        env:
          OPENAI_API_KEY: "${OPENAI_API_KEY}"
        default_binding:
          type: bearer_env
          env_name: OPENAI_API_KEY

agents:
  agent_runtimes:
    codex:
      adapter: codex
      auth_profile: openai
K8s Secret-backed API key:
auth:
  credentials:
    profiles:
      openai_secret:
        auth_origins:
          api_key:
            type: secret_env
            scope: any
            env_name: OPENAI_API_KEY
            secret_ref:
              name: openai-api
              key: api-key
        default_binding:
          type: bearer_env
          auth_origin: api_key
          env_name: OPENAI_API_KEY

Architecture

Three Layers

Sources

Sources describe credential origins. In YAML, they live under auth_origins. Each source has a scope that determines when it is available:
ScopeMeaningExample
agent_runtimeOnly available to the agent/runtime helper after spawncorporate proxy HEAD request
host_edgePrepared on the host before spawningbedrock-auth-helper in host-orchestrated K8s
anyAvailable in any topologyPre-existing K8s Secret
Source types:
TypeDescriptionImportant fields
http_header_tokenExtract token from an HTTP response headerrequest, extract, scope
command_outputRun a command and use stdout as the credentialcommand, scope, optional delivery, optional path
secret_envReference a pre-existing K8s Secretenv_name, secret_ref, scope

Resolvers

A resolver is a runtime helper command. The primary example is fetch-bedrock-token:
  1. Try corporate proxy HEAD request (source: proxy)
  2. If that fails, read mounted fallback file (source: host_fallback)
In local-process mode, a resolver can also stand alone with no sources block at all. Example: Claude can call bedrock-auth-helper directly as its apiKeyHelper. order is deprecated. If a helper command needs fallback logic, the command should own it internally rather than duplicating it in config.

Bindings

Bindings describe how a host adapter consumes credentials:
Binding TypeAdapter ActionRequired fields
claude_api_key_helperWrite apiKeyHelper to ~/.claude/settings.json (Claude)runtime_auth_resolver
bearer_envInject token as environment variableenv_name, plus optionally auth_origin or runtime_auth_resolver
token_fileMount token as a fileauth_origin
bearer_env has three valid shapes:
  1. source + env_name: inject a materialized source into an env var
  2. resolver + env_name: inject the first materialized source that the helper uses
  3. env_name only: plain env passthrough, where the value already exists in host/profile env

Execution Flow

Source Phase Annotation

When the planner builds a credential plan, each source is annotated with an execution phase based on its scope and the current topology:
Scope / Topologyhost_edgein_cluster
host_edgeprepare_nowunavailable
agent_runtimeruntime_onlyruntime_only
anyprepare_nowprepare_now

Cross-Boundary Staging

When the orchestrator runs in-cluster (Topology B), host-edge credentials must be staged across the CP API boundary:

Staging Rules

ScenarioResult
Remote + dispatch=queued + staging neededStage via CP API, attach refs
Remote + dispatch=direct + staging neededError: requires queued
Remote + mode=stream + staging neededError: stream incompatible
Remote + dispatch omitted + staging neededError: requires explicit queued
Remote + no staging neededPass through
Local + directOrchestrator handles in-process
Local + queued + required host_edgeError: use direct or remote mode

Config Reference

Minimal local-process profile (Claude on host)

auth:
  credentials:
    profiles:
      bedrock:
        # No auth_origins: nothing needs pre-materialization in local mode.
        runtime_auth_resolvers:
          bedrock_token:
            type: command
            command: bedrock-auth-helper
            ttl_ms: 60000
        env:
          CLAUDE_CODE_USE_BEDROCK: "1"
          CLAUDE_CODE_SKIP_BEDROCK_AUTH: "1"
          AWS_REGION: "ap-southeast-2"
        assertions:
          require_env: [AWS_REGION]
        default_binding:
          type: claude_api_key_helper
          runtime_auth_resolver: bedrock_token

Env passthrough profile (OpenAI/Codex-style)

auth:
  credentials:
    profiles:
      openai:
        assertions:
          require_env: [OPENAI_API_KEY]
        default_binding:
          type: bearer_env
          env_name: OPENAI_API_KEY
This binding does not need a source or resolver. It simply states that the adapter expects OPENAI_API_KEY to already be present in the merged env.

Full credential profile (K8s host-orchestrator)

auth:
  credentials:
    profiles:
      bedrock:
        auth_origins:
          proxy:
            type: http_header_token
            scope: agent_runtime
            request:
              method: HEAD
              url: https://ai-developer-tooling-llm-proxy-bedrock-runtime.platform.corp-internal.example.com
              headers:
                X-AWS-Region: "${AWS_REGION}"
            extract:
              header: x-bedrock-token
          host_fallback:
            type: command_output
            scope: host_edge
            command: ["bedrock-auth-helper"]
            delivery: file_mount
            path: /var/run/fracta-auth/bedrock-token
            required: false
        runtime_auth_resolvers:
          bedrock_helper:
            type: command
            command: /usr/local/bin/fetch-bedrock-token
            ttl_ms: 60000
            # Deprecated: kept only for backward compatibility.
            order: [proxy, host_fallback]
        env:
          CLAUDE_CODE_USE_BEDROCK: "1"
          CLAUDE_CODE_SKIP_BEDROCK_AUTH: "1"
          AWS_REGION: "ap-southeast-2"
        assertions:
          require_env: [AWS_REGION]
          forbid_env: [CLAUDE_CODE_SIMPLE]
        default_binding:
          type: claude_api_key_helper
          runtime_auth_resolver: bedrock_helper

In-cluster profile (no host_fallback — pods self-auth via the credentials proxy)

auth:
  credentials:
    profiles:
      bedrock:
        auth_origins:
          proxy:
            type: http_header_token
            scope: agent_runtime
            request:
              method: HEAD
              url: https://ai-developer-tooling-llm-proxy-bedrock-runtime.platform.corp-internal.example.com
              headers:
                X-AWS-Region: "${AWS_REGION}"
            extract:
              header: x-bedrock-token
        runtime_auth_resolvers:
          bedrock_helper:
            type: command
            command: /usr/local/bin/fetch-bedrock-token
            ttl_ms: 60000
            # Deprecated: kept only for backward compatibility.
            order: [proxy]
        env:
          CLAUDE_CODE_USE_BEDROCK: "1"
          CLAUDE_CODE_SKIP_BEDROCK_AUTH: "1"
          AWS_REGION: "ap-southeast-2"
        assertions:
          require_env: [AWS_REGION]
          forbid_env: [CLAUDE_CODE_SIMPLE]
        default_binding:
          type: claude_api_key_helper
          runtime_auth_resolver: bedrock_helper

Host binding override (non-Claude adapter)

hosts:
  future_host:
    adapter: future
    auth_profile: bedrock
    auth_binding:
      type: bearer_env
      auth_origin: host_fallback
      env_name: FUTURE_API_TOKEN

order Deprecation

resolvers.<name>.order is deprecated. Use this rule going forward:
  • If the helper is an opaque command, fallback order belongs inside the command.
  • New configs should omit order.
  • Existing configs may keep order temporarily for backward compatibility.

Assertions

Assertions are declarative config-driven validation rules. They run against the final merged environment (host env + profile env + binding-derived env) before credentials are materialized. No Claude/Bedrock-specific logic is hardcoded in Go.
AssertionMeaning
require_env: [KEY]Fail if KEY is not in the merged env
forbid_env: [KEY]Fail if KEY IS in the merged env
require_source: [name]Fail if the named source is not available for the topology
warn_if_missing_env: [KEY]Warn (non-fatal) if KEY is missing

Diagnostics

fracta auth diagnose

fracta auth diagnose --host-type claude --config path/to/fracta.yaml
Runs the credential pipeline in dry-run mode and prints:
  • Credential origins with scope and execution phase
  • Runtime helper command, TTL, and deprecated source order when present
  • Binding type and target
  • Assertion results (pass/fail)
  • Final merged environment variables

Pod helper debug mode

Set FRACTA_CREDENTIALS_DEBUG=1 in the credential profile env to enable verbose stderr logging in fetch-bedrock-token:
[credentials.helper] trying proxy HEAD url=... region=ap-southeast-2
[credentials.helper] proxy: status=ok token_present=true

Structured log events

All credential operations emit structured logs via fractalog.Component("credentials"):
EventWhen
credentials.plan.buildPlan construction starts
credentials.plan.sourceEach source annotated with phase
credentials.plan.completePlan ready with phase counts
credentials.source.prepareHost-edge source being materialized
credentials.source.success / .failSource preparation result
credentials.assertion.pass / .failAssertion evaluation
credentials.stage.success / .failCross-boundary staging
credentials.rehydrate.success / .failWorker rehydration
credentials.binding.projectAdapter projection
credentials.spawn.materializedFinal spawn handoff

Config File Locations

After fracta init --scaffold <mode>, the per-mode config files live under your project root:
FileModeDescription
fracta.yaml (project root)All modesOperator-edited config; runtime.backend distinguishes modes
deployment/configs/controlplane.yamlDocker ComposeServer-side controlplane config (mounted into the controlplane container)
deployment/configs/gateway.yamlDocker ComposeServer-side gateway config
deployment/k8s/manifests/fracta-controlplane.yamlKubernetesControlplane ConfigMap + Deployment
deployment/k8s/manifests/fracta-gateway.yamlKubernetesGateway ConfigMap + Deployment

Known Limitations

  • auth_profile nested under kubernetesFixed: auth_profile and auth_binding now live at agents.agent_runtimes.<name>.* directly, not under agents.agent_runtimes.<name>.kubernetes.*.
  • K8sCredentialStager is not yet implemented — staging uses InMemoryCredentialStager (same-process) or RemoteCredentialStager (CP API HTTP). A K8s Secret-backed stager would be needed for multi-process production deployments without a CP API.
  • No end-to-end integration test covering the full remote staging → CP API → worker rehydration path.