Agent Sandbox Registry

agent-sandbox

Decision: One resolution order for every credential

无中文版

.agents/notes/implemented/feature/2026-09-30-credential-resolution-priority.md

Decision: One resolution order for every credential

Status: implemented Implemented: 2026-09-30 Related: 2026-09-30-unified-config-keys.md, 2026-09-29-region-command-level-option.md, 2026-09-29-config-init-wizard.md, 2026-09-02-dual-auth-mode.md

Problem

A credential could be set in a function argument, a process environment variable, ./.env, and ~/.abx, and each caller picked a different winner. Sandbox.create stopped at the first .env file it found, so a project file that did not contain the sandbox key hid ~/.abx/.env. Sandbox.deploy never read ~/.abx at all. abx config list ignored ./.env and ignored vendor LLM variables when a stored llm_api_key existed. abx mcp install parsed ~/.abx/.env by hand and could return SANDBOX_API_KEY ahead of E2B_API_KEY. Public docs also disagreed: getting started listed the code parameter as the lowest priority, and the region notes wrote abx config set region and SANDBOX_REGION as one tier.

Decision

Every credential is resolved in one order. The first non-blank value wins:

  1. Explicit argument for this call (api_key=, llm_api_key=, openai_base_url=, openai_model=, --token, command --region).
  2. Process environment, in the per-key order below.
  3. ./.env, same names.
  4. ~/.abx: secrets in ~/.abx/.env (abx config set), endpoint / model / region in ~/.abx/config.toml.
  5. Built-in default, or unset when the key has none.

.env files merge per key. ./.env overrides ~/.abx/.env only for a key it sets. A key the project file omits still comes from ~/.abx/.env. A blank or whitespace-only value is unset and does not hide the next layer.

abx config list labels the winner (env) for the process environment, (user) for either file, (default) for a built-in default, and (not set) when nothing applies. Secrets stay masked.

Need Process environment, first hit wins ~/.abx when the layers above are unset Default
Sandbox API key (sandbox_api_key; SDK field stays api_key) E2B_API_KEY, then SANDBOX_API_KEY E2B_API_KEY in ~/.abx/.env none
Alibaba Cloud AK/SK ALICLOUD_ACCESS_KEY_*, then AccessKey / AccessSecret the same names in ~/.abx/.env none
GitHub token GITHUB_TOKEN GITHUB_TOKEN in ~/.abx/.env none
LLM API key ABX_LLM_API_KEY, BAILIAN_CODING_PLAN_API_KEY, DASHSCOPE_API_KEY, OPENAI_API_KEY, then legacy ABX_QWEN_CODE_API_KEY ABX_LLM_API_KEY in ~/.abx/.env, then legacy llm_api_key in config.toml, then ABX_QWEN_CODE_API_KEY in ~/.abx/.env none
LLM base URL ABX_LLM_BASE_URL, then OPENAI_BASE_URL llm_base_url in config.toml, then legacy qwen_code_base_url DashScope compatible-mode
LLM model ABX_LLM_MODEL, then OPENAI_MODEL llm_model in config.toml, then legacy qwen_code_model qwen3-coder-plus
Region SANDBOX_REGION region in config.toml cn-hangzhou

Callers that share this order:

The user-facing copy of this table is Credential resolution and 凭证解析.

API Design

load_config(**overrides) -> TransportConfig
    overrides > process env > merged .env files > ~/.abx/config.toml > defaults

project_dotenv_value(names: tuple[str, ...]) -> str | None
    first non-blank name in ./.env only

resolve_llm_env(*, llm_api_key, openai_base_url, openai_model) -> dict[str, str]
    explicit > process env > ./.env > ~/.abx > DashScope default
    raises DeployLLMKeyMissingError (E7001) when no key exists

resolve_github_token(explicit: str | None) -> str | None
    --token > process GITHUB_TOKEN > ./.env > ~/.abx/.env > None

_effective_value(key) -> (value, "env" | "user" | "default" | "unset")

TransportConfig.api_key and Sandbox.create(api_key=) stay. The CLI name is sandbox_api_key.

Alternatives considered

Dependencies

agent_sandbox.transport.config, agent_sandbox.cli.commands.config_cmd, agent_sandbox.api.deploy, agent_sandbox.cli.commands._coding_agent, agent_sandbox.cli.commands.mcp, agent_sandbox.cli.region. python-dotenv reads the .env files. Deploy must not import the CLI.

Test Strategy

tests/integration/test_credential_priority_e2e.py points the CLI, load_config, resolve_llm_env, the coding agent, resolve_github_token, resolve_region, MCP's editor-config writer, and ACR namespace resolution at one temporary home and one project .env. It checks the process environment beating both files, a project file that omits a key still yielding ~/.abx, and a key set in ./.env beating ~/.abx and losing to the process environment. It also checks same-layer order (E2B_API_KEY over SANDBOX_API_KEY, ABX_LLM_API_KEY over DASHSCOPE_API_KEY, ALICLOUD_ACCESS_KEY_ID over AccessKey), a command --region beating SANDBOX_REGION, a blank value falling through, a legacy ABX_QWEN_CODE_API_KEY, and --acr-namespace over ACR_NAMESPACE over ./.env over ~/.abx. An explicit llm_api_key= and --token still win. MCP copies SANDBOX_REGION into the editor config only when the process environment sets it.

tests/test_transport/test_config.py covers the per-key .env merge. tests/test_cli/test_config_commands.py covers a whitespace GITHUB_TOKEN falling through to the stored token. tests/test_cli/test_region_options.py already asserts SANDBOX_REGION beats config.toml.

The suite isolates transport.config._PROJECT_ENV_FILE so a developer's ./.env cannot leak into abx config list.

Acceptance criteria