Skip to content

Credential Injection

DCAF's CredentialManager centralises all cloud credential handling — Kubernetes kubeconfigs, AWS keys, and GCP service-account JSON files — so individual agents never have to decode, write, or clean up credential material themselves.


How It Works

When a request arrives the AgentService passes the PlatformContext through a CredentialManager before calling the runtime:

  1. K8s scopes — builds a merged kubeconfig (one context per scope), writes it to a temp file, and adds kubeconfig_path to the context dict the agent receives.
  2. AWS scopes — builds per-scope env dicts (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN, AWS_DEFAULT_REGION).
  3. GCP scopes — writes per-scope service-account JSON key files, builds per-scope env dicts (GOOGLE_APPLICATION_CREDENTIALS).
  4. Cleanup — all temp files are deleted after the request completes (async context manager).

The agent runtime receives an enhanced platform_context dict containing kubeconfig_path (if applicable) and can call PreparedCredentials.get_subprocess_env(scope_name) to get a ready-to-use env dict for subprocess calls.


Scopes Wire Format

Credentials arrive in platform_context.scopes as a list of scope objects matching Pranav's credential selector payload:

EKS:

{
  "ProviderInfo": {"Type": "eks", "Name": "prod-cluster", "AccountId": "https://prod-k8s-api.example.com"},
  "Credential": {"Data": {"token": "eyJ...", "base64certdata": "LS0tLS1CRUdJTi..."}}
}

GKE (uses service-account-access-token instead of token):

{
  "ProviderInfo": {"Type": "gke", "Name": "gke-prod", "AccountId": "https://gke-api.example.com"},
  "Credential": {"Data": {"service-account-access-token": "eyJ...", "base64certdata": "LS0tLS1CRUdJTi..."}}
}

GCP (access token for command execution):

{
  "ProviderInfo": {"Type": "gcp", "Name": "my-gcp-project", "AccountId": "my-gcp-project"},
  "Credential": {"Data": {"service-account-access-token": "ya29.xxx..."}}
}

Supported Types

ProviderInfo.Type Category What CredentialManager produces
eks Kubernetes merged kubeconfig → kubeconfig_path
gke Kubernetes merged kubeconfig → kubeconfig_path
kubernetes Kubernetes merged kubeconfig → kubeconfig_path
aws AWS per-scope env dict with AWS_* vars
gcp GCP access token → CLOUDSDK_AUTH_ACCESS_TOKEN + CLOUDSDK_CONFIG; or JSON key → GOOGLE_APPLICATION_CREDENTIALS

Credential.Data Fields

K8s scopes — EKS / generic kubernetes:

Field Description
token Bearer token for the cluster API server
base64certdata Base64-encoded cluster CA certificate

K8s scopes — GKE:

Field Description
service-account-access-token GKE service account bearer token
base64certdata Base64-encoded cluster CA certificate

AWS scopes:

Field Description
access_key AWS access key ID
secret_key AWS secret access key
session_token STS session token (required for temporary/JIT credentials)
region AWS region (e.g. us-east-1)

GCP scopes — short-lived access token (preferred):

Field Description
service-account-access-token Short-lived OAuth2 access token from Pranav's credential selector

Sets CLOUDSDK_AUTH_ACCESS_TOKEN and an isolated CLOUDSDK_CONFIG directory (deleted after the request).

GCP scopes — long-lived JSON key (legacy):

Field Description
json_key Base64-encoded GCP service account JSON key file

Writes a temp file and sets GOOGLE_APPLICATION_CREDENTIALS. When both fields are present, the access token takes precedence.


Multi-Scope Requests

A single request can include multiple scopes of different types. All scopes are processed; K8s scopes are merged into a single kubeconfig with one context per scope.

"scopes": [
  {
    "ProviderInfo": {"Type": "eks", "Name": "prod-cluster", "AccountId": "https://..."},
    "Credential": {"Data": {"token": "eyJ...", "base64certdata": "LS0t..."}}
  },
  {
    "ProviderInfo": {"Type": "aws", "Name": "prod-aws", "AccountId": "123456789012"},
    "Credential": {"Data": {"access_key": "AKIA...", "secret_key": "...", "region": "us-east-1"}}
  }
]

Legacy Base64 Kubeconfig

Callers that haven't migrated to scopes can still pass a single base64-encoded kubeconfig directly:

"platform_context": {
  "kubeconfig": "<base64-encoded kubeconfig YAML>"
}

CredentialManager decodes it and writes a temp file. The kubeconfig_path key is added to platform_context exactly as with scopes. Both paths clean up the temp file after the request.

Note: If both kubeconfig_path (pre-populated by an upstream pass) and kubeconfig (base64) are present, kubeconfig_path takes precedence and no temp file is written.


GCP Access Token (LLM — Vertex AI)

When a GCP scope carries service-account-access-token, DCAF automatically uses it to authenticate the Vertex AI LLM call in addition to subprocess tools. The flow is:

  1. AgnoAdapter._extract_gcp_access_token() finds the first GCP scope with the field.
  2. The token is passed to AgnoModelFactory.create_model(gcp_access_token=...).
  3. Gemini models: google.oauth2.credentials.Credentials(token=...) is passed as the credentials kwarg — bypasses ADC entirely.
  4. Vertex Claude models: client_params={"access_token": token} is forwarded to AnthropicVertex(access_token=...) — also bypasses ADC.
  5. The model is not cached when a token is used; a fresh instance is created per request (short-lived tokens change per request).

Note: When no GCP access token is present, both models fall back to Application Default Credentials (ADC) as before. Existing ADC-based deployments (GKE Workload Identity, GOOGLE_APPLICATION_CREDENTIALS) are unaffected.


AWS Explicit Credentials (ModelFactory)

When AWS credentials are passed via environment variables (AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY), ModelFactory._create_bedrock_model() creates an explicit aioboto3.Session with those credentials. This supports DuploCloud JIT (short-lived STS) credentials:

AWS_ACCESS_KEY_ID=ASIA3XIJPJFD...
AWS_SECRET_ACCESS_KEY=...
AWS_SESSION_TOKEN=...          # required for STS/JIT credentials
AWS_REGION=us-east-1

The three credential resolution paths, in priority order:

Priority Condition Behaviour
1 AWS_PROFILE set Named profile session
2 AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY set Explicit session with those credentials
3 Neither Default IAM / credential chain (EC2 instance role, ECS task role, etc.)

Local Development (DCAF_IS_LOCAL)

In production, credentials are injected at runtime via scopes. In local development, set DCAF_IS_LOCAL=true so DCAF expects credentials from the environment instead:

DCAF_IS_LOCAL=true
AWS_PROFILE=my-local-profile
GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json

See Environment Configuration for details.


Security Notes

  • CredentialManager never mutates os.environ — credentials flow via subprocess env dicts and explicit SDK session objects, keeping concurrent async requests isolated.
  • Temp files are always cleaned up in CredentialManager.__aexit__, even if the request raises an exception.
  • Do not log raw credential values; debug logging only records key names, not values.