downcity.json Reference
Current downcity.json layering, loading behavior, and key fields
downcity.json Reference
Model config is now split into two layers:
~/.downcity/downcity.db(console-global SQLite) manages the full model pool- Project
downcity.json(agent-local) declares onlyexecution
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_mainAgent startup resolution
When an agent starts:
- Check whether the project declares
execution - If
execution.type = "api", resolve the model in the SQLite models table by id - Resolve provider in the SQLite providers table by providerId
- If
execution.type = "local", readplugins.lmp, resolve the local GGUF file, and start/reuse a llama.cpp server - 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 commandkimi acpclaude: default commandnpx -y @zed-industries/claude-code-acpcodex: default commandnpx -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~/.modelsplugins.lmp.command: launcher command, defaultllama-serverplugins.lmp.contextSize: context window, default8192plugins.lmp.autoStart: whether Downcity should auto-startllama-server, defaulttrue
Current limitations:
execution.type="acp"only changes the session/chat/task execution path.execution.type="api"requiresexecution.modelId.execution.type="local"requiresplugins.lmp.modeland a workingllama-serverinstallation.
Preferred local-model management commands:
city lmp status
city lmp models
city lmp use gemma-4-E4B-it-UD-Q4_K_XL.ggufSandbox
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 accessrestricted: currently handled the same as open networkfull: allows network access
- Default is
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=scripttask bodies also run through the same sandbox backend- Host paths not mounted into the sandbox are not visible
- Isolated
HOME/TMPDIRlive under.downcity/shell/<shellId>/sandbox/