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 only execution

Agent project minimal shape

{
  "$schema": "./.downcity/schema/downcity.schema.json",
  "name": "my-agent",
  "version": "1.0.0",
  "execution": {
    "type": "acp",
    "agent": {
      "type": "kimi"
    }
  }
}

If you use the console-global model pool, set execution.type = "api" and make sure execution.modelId exists in ~/.downcity/downcity.db.

If you use local llama / GGUF, set execution.type = "local" and declare the model file in plugins.lmp.model. When plugins.lmp.modelsDir is omitted, Downcity reads from ~/.models.

If you use an ACP coding agent, set execution.type = "acp" and declare execution.agent.type as kimi, claude, or codex.

Project downcity.json should keep 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:

city model list
city 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
  4. If execution.type = "local", read plugins.lmp, resolve the local GGUF file, and start/reuse a llama.cpp server
  5. If execution.type = "acp", launch the corresponding ACP agent

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:

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

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

ACP Execution Mode

Currently supported ACP coding agents:

  • kimi: default command kimi acp
  • claude: default command npx -y @zed-industries/claude-code-acp
  • codex: default command npx -y @zed-industries/codex-acp

Example:

{
  "execution": {
    "type": "acp",
    "agent": {
      "type": "kimi"
    }
  }
}

You can override the launcher:

{
  "execution": {
    "type": "acp",
    "agent": {
      "type": "claude",
      "command": "claude-acp",
      "args": []
    }
  }
}

Local Execution Mode

Example:

{
  "execution": {
    "type": "local"
  },
  "plugins": {
    "lmp": {
      "model": "gemma-4-E4B-it-UD-Q4_K_XL.gguf"
    }
  }
}

Useful optional fields:

  • plugins.lmp.modelsDir: local model directory, default ~/.models
  • plugins.lmp.command: launcher command, default llama-server
  • plugins.lmp.contextSize: context window, default 8192
  • plugins.lmp.autoStart: whether Downcity should auto-start llama-server, default true

Current limitations:

  • execution.type="acp" only changes the session/chat/task execution path.
  • execution.type="api" requires execution.modelId.
  • execution.type="local" requires plugins.lmp.model and a working llama-server installation.

Preferred local-model management commands:

city lmp status
city lmp models
city lmp use gemma-4-E4B-it-UD-Q4_K_XL.gguf

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/