Configuration
Environment Variable Strategy (Console Shared vs Agent Private)
Clear scope and runtime merge logic for global_env, agent_env, channel_accounts, and <agent>/.env
Canonical schema doc: Env and ship.db Database Design
Environment Variable Strategy (Console Shared vs Agent Private)
This page answers 4 practical questions:
- How many types of env-related variables exist?
- Where is each type stored?
- How are they loaded and merged at startup?
- What reads project
.env, and what does not?
1. Scope Matrix
| Layer | Source | Persisted | Scope | Typical content |
|---|---|---|---|---|
| Console shared env | ~/.ship/ship.db global_env | Yes (encrypted) | All agents | shared API keys and shared placeholders |
| Agent private env (DB) | ~/.ship/ship.db agent_env | Yes (encrypted) | One agent (agent_id) | project-level secret keys |
| Agent private env (file) | <agent>/.env | User-managed file | One agent | runtime overlay values |
| Bot credentials | ~/.ship/ship.db channel_accounts | Yes (encrypted fields) | Reusable by binding | Telegram/Feishu/QQ bot secrets |
| Runtime context vars | process memory (DC_CTX_*) | No | single request | channel/chat/user context |
Key points:
- All persisted env-like secrets are in
ship.dbencrypted tables. <agent>/.envis runtime-only overlay for that agent.ship.jsonstores bindings (model.primary,channel.channelAccountId), not plaintext credentials.
2. Data Flow (diagram)
3. Load Priority (who wins)
3.1 Runtime env merge
agent_env(DB) loads first.<agent>/.envoverlays it..envwins on key conflicts.
3.2 Channel credential resolution
ship.jsonchannel config only bindschannelAccountId.- Runtime resolves real credentials from
channel_accounts. - Missing binding or missing required secrets =>
config_missing.
4. What Reads .env and What Does Not
4.1 Reads project .env
- Project-layer
${ENV_KEY}resolution. - Agent runtime process env injection (
agent_env + .env). - Optional fallback paths in some service auth helpers.
4.2 Does not read project .env
- Global model/provider pool (
model_providers,models) fromship.db. - Extension global config from
console_secure_settings.extensions_config. - Bot account credential source of truth (
channel_accounts). - Runtime context vars (
DC_CTX_*) are generated at request time.
5. Save Paths
- Bot account CRUD (UI): writes
channel_accounts. - Model CRUD (CLI/UI): writes
model_providers,models. - Extension config (
city console config set extensions.*): writesextensions_config. - Channel configure action: writes
ship.json(enabled,channelAccountId). - User manual
.envedit: affects runtime only for that agent.
6. FAQ and Troubleshooting
6.1 Why does a new agent show existing channel credentials?
Most common reasons:
- The channel is bound to an existing
channelAccountId. - The new project
.envalready contains fallback keys.
7. Recommended Setup
- Initialize console-global data once:
city console init
city console model create
city console config set extensions.voice.enabled true- Create channel accounts in Console UI
Global / Channel Accounts. - In each agent
ship.json, bind channel tochannelAccountId. - Keep agent-private runtime keys in
<agent>/.envonly when needed.
8. Best-Practice Checklist
- Keep persisted secrets in
ship.dbencrypted tables. - Keep
ship.jsonas binding/config file, not secret storage. - Use
<agent>/.envas agent-local runtime overlay only. - Validate channel status via
chat statusafter changing bindings. - If values look inherited, check both
channelAccountIdand project.env.