Downcity
Configuration

Env and ship.db Database Design

Detailed schema, encryption model, and load/write flow for global_env, agent_env, channel_accounts, and runtime config resolution

Env and ship.db Database Design

This page is the single source of truth for environment-variable storage and ~/.ship/ship.db table design.

1. Goals

  1. Keep all persistent env values encrypted in ~/.ship/ship.db.
  2. Keep agent runtime isolation strict (no cross-agent env leakage).
  3. Keep ship.json as binding config, not credential storage.
  4. Keep Console UI and CLI on the same storage model.

2. Data Model Overview

3. Table-by-Table Schema

3.1 global_env

Purpose: console-shared env values.

ColumnTypeNotes
keyTEXT PKEnv key (for example OPENAI_API_KEY)
value_encryptedTEXTAES-GCM encrypted value
created_atTEXTISO timestamp
updated_atTEXTISO timestamp

3.2 agent_env

Purpose: agent-private env values managed in DB.

ColumnTypeNotes
agent_idTEXTAgent identity (project root absolute path)
keyTEXTEnv key
value_encryptedTEXTAES-GCM encrypted value
created_atTEXTISO timestamp
updated_atTEXTISO timestamp

Constraints:

  1. Primary key: (agent_id, key)
  2. Index: agent_id

3.3 channel_accounts

Purpose: central credential vault for chat channels.

ColumnTypeNotes
idTEXT PKBot account id used by ship.json binding
channelTEXTtelegram / feishu / qq
nameTEXTDisplay name
identityTEXTOptional human-readable identity
ownerTEXTOptional owner info (auto-sync when available)
creatorTEXTOptional creator info (auto-sync when available)
bot_token_encryptedTEXTTelegram token (encrypted)
app_id_encryptedTEXTApp id (encrypted)
app_secret_encryptedTEXTApp secret (encrypted)
domainTEXTOptional domain (mainly Feishu/Lark)
sandboxINTEGERQQ sandbox flag (0/1)
auth_idTEXTOptional master auth id
created_atTEXTISO timestamp
updated_atTEXTISO timestamp

3.4 Existing tables still used

  1. model_providers / models: global model pool.
  2. console_secure_settings: non-env secure JSON (currently extensions_config).

4. Encryption Boundaries

  1. Encrypted fields:
  • global_env.value_encrypted
  • agent_env.value_encrypted
  • channel_accounts.*_encrypted
  • console_secure_settings.value_encrypted
  • model provider api_key_encrypted
  1. Decryption only happens in runtime memory or API service logic.

  2. ship.json does not store plaintext chat credentials.

5. ship.json Binding Model

ship.json stores binding ids, not secrets:

{
  "model": {
    "primary": "default"
  },
  "services": {
    "chat": {
      "channels": {
        "qq": {
          "enabled": true,
          "channelAccountId": "qq-main"
        }
      }
    }
  }
}

Rules:

  1. model.primary binds to models.id.
  2. services.chat.channels.<channel>.channelAccountId binds to channel_accounts.id.
  3. Channel startup requires:
  • channel enabled
  • channelAccountId exists
  • required credential fields present in the bound account

6. Runtime Load and Merge Order

7. Where Data Is Written

  1. Console UI / Channel Accounts page:
  • writes channel_accounts
  1. Console UI / Model page and model CLI:
  • writes model_providers, models
  1. Console config (extensions scope) and extension commands:
  • writes console_secure_settings.extensions_config
  1. Agent channel configure action:
  • writes ship.json binding (channelAccountId, enabled)

8. Operational Notes

  1. <agent>/.env is user-managed and runtime-only overlay.
  2. DB agent_env is also agent-private; both are merged with .env taking precedence.
  3. Console shared env is only from global_env.
  4. For new agents, no credential auto-reuse should happen unless explicitly bound to a channel account.

9. Quick Verification Checklist

  1. channel_accounts has expected rows.
  2. Target agent ship.json channel has channelAccountId.
  3. Runtime chat status shows:
  • enabled=true
  • configured=true
  • channel link status not config_missing
  1. New agent without binding should show channel disabled or config missing, not inherited secrets.

Table of Contents