Downcity
Reference

downcity.json Reference

Current downcity.json layering, loading behavior, and key fields

downcity.json Reference

Model config is now split into two layers:

  1. ~/.downcity/downcity.db (console-global SQLite) manages the full model pool
  2. Project downcity.json (agent-local) declares execution and project plugins

Agent project minimal shape

{
  "$schema": "./.downcity/schema/downcity.schema.json",
  "name": "my-agent",
  "version": "1.0.0",
  "execution": {
    "type": "api",
    "modelId": "quality"
  },
  "plugins": {}
}

execution.type is fixed to api, and execution.modelId must exist in ~/.downcity/downcity.db.

Project downcity.json keeps plugin configuration under plugins.*.

Console-global model pool

Full model management stays in ~/.downcity/downcity.db:

  • providers table
  • models table
  • provider apiKey is encrypted at rest (api_key_encrypted)

Use CLI:

studio model list
studio model get provider openai_main

Agent startup resolution

When an agent starts:

  1. Check whether the project declares execution
  2. If execution.type = "api", resolve the model in the SQLite models table by id
  3. Resolve provider in the SQLite providers table by providerId

execution must be valid, otherwise startup fails fast.

Plugin scope

Plugin behavior is configured in project downcity.json under plugins.*. If a plugin needs extra models, command templates, or local paths, keep them under that plugin's own config block.

Example for asr:

{
  "plugins": {
    "asr": {
      "enabled": true,
      "provider": "local",
      "modelId": "SenseVoiceSmall"
    }
  }
}

Chat channel binding

Chat channels in downcity.json should bind to a global channel account id:

{
  "plugins": {
    "chat": {
      "channels": {
        "telegram": {
          "enabled": true,
          "channelAccountId": "telegram-main"
        }
      }
    }
  }
}

channelAccountId resolves to channel_accounts.id in ~/.downcity/downcity.db.

Sandbox

Project-level sandbox adjusts the execution boundary for shell / CLI commands. It does not represent an approval flow or a chat-user permission system.

Example:

{
  "sandbox": {
    "networkMode": "off",
    "writablePaths": [".", ".downcity"],
    "envAllowlist": ["PATH", "LANG", "TERM", "SHELL"]
  }
}

Field notes:

  • sandbox.networkMode
    • Default is full
    • off: blocks network access
    • restricted: currently handled the same as open network
    • full: allows network access
  • sandbox.writablePaths
    • Writable path list
    • Relative paths resolve from the project root
    • Current implementation only allows paths inside the project root
  • sandbox.envAllowlist
    • Environment variable names that may be exported into the sandbox

Current boundary:

  • Current first-class support is macOS using sandbox-exec
  • Shell commands are required to go through sandbox and do not fall back to an unrestricted host process
  • kind=script task bodies also run through the same sandbox backend
  • Host paths not mounted into the sandbox are not visible
  • Isolated HOME / TMPDIR live under .downcity/shell/<shellId>/sandbox/