Agent Authentication Architecture
Context
autograph agent containers (dev-agent and metalab-agent) need to
authenticate with LLM providers to function. The current mechanism
renders the provider credential (apiKey) directly into the OpenCode
configuration file, violating rule 3 of the secrets-management ADR.
Current mechanism and its problems
The entrypoint substitutes AUGR_ADE_OPENCODE_AUTH_TOKEN into the
rendered .opencode.local.json via envsubst. The token appears in clear
text in the generated file (mode 644). The compose files also map the
same token to AWS_BEARER_TOKEN_BEDROCK for native env-var auth, making
the rendered apiKey redundant -- the credential exists in two places
when one suffices.
The entrypoint requires a provider ID and (for most providers) a token
at startup. This prevents interactive authentication via OpenCode's
/connect flow and blocks provider-free startup.
OpenCode v2 authentication
OpenCode v2.0.27 provides three authentication methods:
Native environment variables. Each provider detects specific env vars:
AWS_BEARER_TOKEN_BEDROCK for Bedrock, OPENAI_API_KEY for OpenAI,
ANTHROPIC_API_KEY for Anthropic, and others. A provider with valid
env-var credentials is usable without any configuration block. Confirmed
live: opencode auth list shows "Amazon Bedrock AWS_BEARER_TOKEN_BEDROCK
environment".
/connect (interactive). The /connect command provides interactive
provider setup (API key entry, OAuth flows, device flows). Credentials
are stored in OpenCode's internal database (opencode.db), not in
configuration files.
auth import/export. Credentials can be exported as JSON and
re-imported, enabling headless provisioning without interactive flows.
Constraints
- Agent containers run on
internal: trueDocker networks. All outbound traffic passes through the egress proxy (seeenterprise-network-proxy.md). The agent must not be able to bypass the proxy. - The secrets-management ADR requires: no credential in Docker images
(rule 1), no credential in the task runner's env (rule 2), native app
auth preferred (rule 3), compose
env_file:as fallback (rule 4). - The agent process runs tools (bash, read, etc.) as the same user. File permissions do not protect the credential from the agent itself. This is a known limitation documented in the secrets-management threat model.
- Configuration layering is handled separately (see
agent-configuration-layering.md). This ADR covers only credential management and provider connection methods.
Options considered
Option A -- Native env var only (remove token from config)
Remove apiKey from the template. Rely solely on native env-var auth
(e.g., AWS_BEARER_TOKEN_BEDROCK). Keep the compose env-var mapping.
No support for /connect.
- Advantage: minimal change (template + validate-env). Fixes the secrets-management rule 3 violation immediately.
- Disadvantage: no interactive auth, no multi-provider flexibility. Operators must always provide a token via env var. Does not support OAuth-based providers (GitHub Copilot, OpenAI OAuth) or provider-free startup.
Option B -- Multi-method auth (env var + /connect + import)
Support both v2 auth methods that matter today. Remove credentials from the config template entirely. Make provider and token optional at startup.
- Advantage: maximum flexibility. Env vars for headless/CI,
/connectfor interactive setup. Provider-free startup enables deferred auth. Satisfies secrets-management rule 3. - Disadvantage:
/connectwith OAuth requires the callback to reach the container -- a network problem in isolated containers. Medium effort to evaluate and implement the network strategy.
Option C -- Proxy-externalized credentials
The egress proxy holds the LLM credential and injects the provider's auth header. The agent container has no token at all ("cannot read what is not there"). Extends the proxy-held credential pattern used for the npm registry token.
- Advantage: strongest security posture. The agent cannot leak what it does not have.
- Disadvantage: requires TLS interception (
ssl_bump) in the proxy for HTTPS provider APIs. Complex to implement. Only works for simple API-key providers (OpenAI, Anthropic, OpenRouter) where auth is a staticAuthorization: Bearer <token>header. Does not work for AWS SigV4 (Bedrock) because SigV4 signs the entire request body and headers -- the proxy cannot sign on behalf of the application without reimplementing the AWS SDK. Does not work for OAuth refresh flows where the application must manage token lifecycle. Couples auth to the proxy configuration.
Decision
Option B -- Multi-method auth, with Option A as the immediate first step and Option C as a long-term target.
Option A is the minimum viable fix (remove token from config, rely on env
var auth). Option B extends this with /connect and auth import support,
which requires provider-free startup (enabled by the layering ADR). Option
C is the security ideal but requires non-trivial proxy work (ssl_bump
for HTTPS auth injection); it remains a target documented in the
secrets-management ADR threat model.
Decision 1: Token in config via universal apiKey
The AUGR_ADE_OPENCODE_AUTH_TOKEN variable is rendered into the provider
block as apiKey via envsubst. This is the universal auth mechanism --
it works for any provider with a single PROVIDER_ID + AUTH_TOKEN pair.
The provider-specific native env var mappings (e.g.,
AWS_BEARER_TOKEN_BEDROCK=${AUGR_ADE_OPENCODE_AUTH_TOKEN:-}) are removed
from the compose files. They were redundant with apiKey, provider-specific
(one mapping per provider), and created duplicate auth sources visible in
opencode auth list. A single auth channel avoids confusion and false
positives.
Decision 2: Provider-free startup
validate-env.sh no longer requires a provider ID or token. When neither
is set, the entrypoint renders a config without a provider block. OpenCode
starts without an active provider. The operator can then authenticate via
/connect, set native env vars and restart, or use auth import.
Decision 3: Connection methods
Two methods are supported:
| Method | Mechanism | Use case | Network requirement |
|---|---|---|---|
| Env var | Token in .env.project, rendered into config by entrypoint via envsubst (provider block) |
Headless, CI, current default | None |
/connect |
Interactive OpenCode command, credentials stored in OpenCode db | Interactive setup, OAuth providers | Callback must reach container |
Decision 4: `/connect` network strategy: the host network during the login
Using /connect with OAuth providers requires the callback to reach the
container. The agent runs on an internal: true network with egress only
through the proxy. Several approaches exist:
| Approach | How it works | Pros | Cons |
|---|---|---|---|
| Device flow | No callback needed. User enters a code on the provider's website. | No network changes. Works with current isolation. | Not all providers support it. Requires a browser on the host. |
| OpenCode server mode | Operator attaches a client from the host to the container's OpenCode service. Runs /connect from the host client. |
No container network changes. Auth propagates to container. | Requires exposing the OpenCode service port. Feasibility to be validated. |
| Temporary published port | Compose publishes the callback port during setup, removes it after. | Simple, direct. | Requires compose change + restart. Exposes a port (briefly). |
| Proxy network + host-only | Short-lived container on the gateway network of the proxy (internet) and augr-host-only (callback port published). |
No host network; egress through the proxy. | The callback port must be known before the container starts. opencode 2.0.27 picks it at login (port 0) and listens on 127.0.0.1 only, while a published port arrives on the container's network interface. Does not work as is. |
| Host network during setup | Container temporarily uses host network for the OAuth flow. | Full network access for callback. | Loses all network isolation during setup. Over-broad. |
Decided (E03.T03): the host network during the login. The tasks
augr:pkg:agent:connect and augr:pkg:metalab:connect start a short-lived
container from the agent image with --network host, run
opencode auth login, and remove it (--rm, plus a trap on exit). Credentials
land in opencode.db, on the data volume of the agent.
- Why: the providers that redirect the browser ("Sign in with ChatGPT")
use a loopback callback on a port chosen at login (port 34647 observed in E03;
listen(0, "127.0.0.1")in opencode 2.0.27). Only the network of the host makes the loopback of the container the loopback of the host. Device flow providers need no callback. - Cost: no isolation during the login: the container sees the network of
the host, its LAN and its local services. Mitigated by its short life and its
minimal mounts (data and state volumes, an
xdg-openstub). - Way out: on the host network, the container sees no Docker network,
hence no
augr-proxyby name. It reaches the provider (exchange of the code for the tokens) through the port thataugr-proxypublishes on the loopback of the host (http_proxy=http://127.0.0.1:${AUGR_PKG_PROXY_HOST_PORT}, see ADR enterprise-network-proxy, "Host entry point"). No corporate configuration enters the container;augr-proxyholds it. The task checks that the proxy runs and publishes this port. The corporate CA is in its image. - To revisit when opencode offers a fixed callback port or a server mode
usable from the host: the container could then stay on the gateway network
and
augr-host-only, with no host network.
Consequences
Positive
- Secrets compliance. The credential is removed from the rendered
config, satisfying secrets-management rule 3. It exists only as a
native env var or in OpenCode's credential store (
opencode.db). - Flexible auth. Two methods cover the key use cases: env var for
headless,
/connectfor interactive. - Provider-free startup. Operators are not forced to configure a provider before the container starts. This unblocks local-model scenarios and deferred-auth workflows.
- Path to proxy externalization. The architecture does not preclude Option C. When proxy TLS auth injection is implemented, the env var mapping can be removed, achieving "agent has no token."
Negative / Risks
/connectruns on the host network. Interactive OAuth works in containers, at the cost of no network isolation during the login, in a short-lived container (see Decision 4).- Agent can still read its own credential. The token is in a native env var, readable by the agent process. This is a known limitation of the same-user threat model (documented in secrets-management ADR). Only proxy externalization (Option C) fully resolves this.
- West private-pkg auth. Private West packages require git credentials
for
west update. This is a sibling credential class to the npm registry token (build-time: BuildKit secret; runtime: git credential). To be integrated following the same secrets-management patterns.
Implementation notes
- The existing
AUGR_ADE_OPENCODE_AUTH_TOKENenv var remains in the compose contract. Its usage changes: no longer consumed by the template, only mapped to the native provider env var. - OpenCode service mode:
opencode service statusconfirms the service runs (port observed live). Whether a host client can attach to it for auth propagation is to be validated. - The
auth export/auth importflow is the v2 replacement for the v1auth.jsonpre-seed pattern documented in E03-T01 findings.
Open questions
Proxy externalization scope. The proxy can only externalize credentials for simple API-key providers (header injection via
ssl_bump). AWS SigV4 (Bedrock) and OAuth refresh flows cannot be proxied because they require application-level request signing or token management. This limits proxy externalization to a subset of providers. For the others, the credential remains in the container (env var or OpenCode credential store), protected by network isolation./connectnetwork feasibility. Can device flow or OpenCode server mode cover enough providers to avoid any network changes? To be validated per provider.OpenCode server mode for external clients. Can the operator run a TUI or web client on the host that connects to the container's OpenCode service? If so, this solves both
/connectauth and opens the door to a richer operator UX. To be investigated.West private-pkg auth. A colleague's PR (from a fork) addresses git auth for private West packages. To be recovered, reviewed against secrets-management patterns, and integrated as a dedicated task.
Related
agent-configuration-layering.md-- config layers and precedence.secrets-management.md-- per-class credential rules and threat model.enterprise-network-proxy.md-- network isolation and egress proxy.