{
"type": "object",
"description": "The fully resolved configuration the daemon runs on.",
"additionalProperties": false,
"properties": {
"chat": {
"type": "object",
"description": "Chat connection and who may drive the bot.",
"additionalProperties": false,
"properties": {
"token": {
"type": "string",
"description": "Bot token. Secret. Never enters a sandbox."
},
"channelId": {
"type": "string",
"description": "The single channel the daemon serves. Everything else is ignored."
},
"allowedUserIds": {
"description": "Accounts permitted to drive sessions. Must not be empty."
},
"blockedUserIds": {
"description": "Accounts refused before anything else is considered."
},
"operatorUserIds": {
"description": "Accounts that may control any session, not only their own."
},
"startOnMention": {
"type": "boolean",
"description": "Require a message to mention the bot before it starts a session."
}
},
"required": [
"token",
"channelId",
"allowedUserIds",
"blockedUserIds",
"operatorUserIds"
]
},
"agent": {
"type": "object",
"description": "Which model the agent talks to, and the credential it reaches it with.",
"additionalProperties": false,
"properties": {
"provider": {
"type": "string",
"description": "Provider id, such as `anthropic` or `zai-coding-cn`."
},
"model": {
"type": "string",
"description": "Model pattern or id. Omitted to use the provider's default."
},
"visionModel": {
"type": "string",
"description": "Model an image is shown to when the working model cannot see one."
},
"credentialName": {
"type": "string",
"description": "Environment variable the agent reads, such as `ANTHROPIC_API_KEY`."
},
"credential": {
"type": "string",
"description": "The credential value. Secret."
},
"delegate": {
"type": "object",
"description": "A cheaper model to ask about one artefact, or none.",
"additionalProperties": false,
"properties": {
"model": {
"type": "string",
"description": "The model asked. Must be one the provider serves under the same key."
},
"perTurn": {
"type": "number",
"description": "How many delegations one turn may make before the rest stay at home."
},
"deadlineMs": {
"type": "number",
"description": "How long one delegation may take before it is abandoned."
},
"baseUrl": {
"type": "string",
"description": "Where the provider is reached, when the model store does not say."
}
},
"required": [
"model",
"perTurn",
"deadlineMs"
]
},
"rulesPath": {
"type": "string",
"description": "File of standing instructions given to every session, or none."
},
"providers": {
"description": "Providers the operator defines, beyond the ones the agent knows. Written into the agent's own configuration verbatim, in the shape that configuration uses, because the schema belongs to the agent."
},
"aliases": {
"description": "Short names for models, so a session is started without spelling one."
}
},
"required": [
"provider",
"credentialName",
"credential",
"providers",
"aliases"
]
},
"github": {
"type": "object",
"description": "How a session reaches GitHub, or none when unconfigured.",
"additionalProperties": false,
"properties": {
"token": {
"type": "string",
"description": "Token the agent authenticates with. Secret, and reachable by the agent."
},
"userName": {
"type": "string",
"description": "Name commits are authored with."
},
"userEmail": {
"type": "string",
"description": "Email commits are authored with."
}
},
"required": [
"token",
"userName",
"userEmail"
]
},
"projectRoot": {
"type": "string",
"description": "Absolute path under which every session's project directory lives."
},
"stateDir": {
"type": "string",
"description": "Where per-session state directories are created on the host. Neither this nor `projectRoot` may sit inside the other. A session may write both, and the transcript is kept beside this directory precisely so that it cannot."
},
"sandbox": {
"type": "object",
"description": "What a session may consume, and what the backend enforces.",
"additionalProperties": false,
"properties": {
"backend": {
"type": "string",
"enum": [
"podman",
"bailey"
],
"description": "Which backend confines sessions."
},
"requireFullEnforcement": {
"type": "boolean",
"description": "Refuse to start when the backend cannot enforce every configured guarantee on this host, rather than reporting the gap and continuing."
},
"network": {
"type": "string",
"enum": [
"restricted",
"none"
],
"description": "Network exposure granted to a session."
},
"egressPorts": {
"description": "Ports a session may open outbound, when the network is not `none`."
},
"egress": {
"type": "object",
"description": "What a session may reach outbound, and whether it is brokered.",
"additionalProperties": false,
"properties": {
"mode": {
"description": "Whether egress is port-only or forced through the broker."
},
"allow": {
"description": "Hosts the broker permits, on top of the provider. A leading `*.` matches subdomains; a lone `*` admits any host."
},
"allowInternal": {
"type": "boolean",
"description": "Whether the broker may dial an address on the host's own network."
}
},
"required": [
"mode",
"allow",
"allowInternal"
]
},
"image": {
"type": "string",
"description": "Container image the podman backend runs. Inert under bailey."
},
"memory": {
"type": "string",
"description": "Memory ceiling per session, in size syntax such as `4g`."
},
"cpus": {
"type": "number",
"description": "CPU ceiling per session, in cores."
},
"pids": {
"type": "number",
"description": "Process count ceiling per session."
},
"fileMax": {
"type": "string",
"description": "Largest single file a session may write, in size syntax."
},
"disk": {
"type": "string",
"description": "How much a session may add to its project and state together. Measured rather than enforced, because no backend caps what a process tree writes in aggregate without root. Passing it ends the session."
},
"diskCheckMs": {
"type": "number",
"description": "How often a session's disk use is measured, in milliseconds."
},
"gracePeriodMs": {
"type": "number",
"description": "How long a sandbox may take to stop before it is killed."
},
"hideHostAddress": {
"type": "boolean",
"description": "Hide the host's network identity from a session, when it has a network."
},
"policyExtra": {
"type": "object",
"description": "Paths granted on top of the generated policy, or none.",
"additionalProperties": false,
"properties": {
"read": {
"description": "Directories or files a session may read."
},
"write": {
"description": "Directories or files a session may write."
},
"execute": {
"description": "Directories a session may execute from."
}
},
"required": [
"read",
"write",
"execute"
]
},
"pathExtra": {
"description": "Directories added to a session's PATH, or none."
},
"env": {
"description": "Variables set in every session's environment, or none."
}
},
"required": [
"backend"
]
},
"output": {
"type": "object",
"description": "How much of the agent's activity reaches the thread.",
"additionalProperties": false,
"properties": {
"forwardToolOutput": {
"type": "boolean",
"description": "Whether tool output bodies are posted, not just that a tool ran."
},
"maxToolOutputChars": {
"type": "number",
"description": "Longest tool output posted before it is truncated and marked as such."
},
"maxAttachmentBytes": {
"type": "number",
"description": "Largest attached file taken into a session, in bytes."
},
"maxAttachmentsPerMessage": {
"type": "number",
"description": "Most attached files taken from one message. The rest are refused."
},
"postDiffs": {
"type": "boolean",
"description": "Post a diff after the agent changes a file."
}
}
},
"shutdown": {
"type": "object",
"description": "Who may power off the host. Empty means nobody, which is the default.",
"additionalProperties": false,
"properties": {
"allowedUserIds": {
"description": "Account ids permitted to power off the host."
}
},
"required": [
"allowedUserIds"
]
},
"web": {
"type": "object",
"description": "The web interface, or none when one is not served.",
"additionalProperties": false,
"properties": {
"host": {
"type": "string",
"description": "Address to bind to. Must be loopback, private, or a tailnet address."
},
"port": {
"type": "number",
"description": "Port to listen on."
},
"observer": {
"type": "boolean",
"description": "When true the interface may watch and read but not start, prompt, or control anything."
},
"publicUrl": {
"type": "string",
"description": "Where the interface is reachable from outside, such as behind a tunnel."
}
}
},
"limits": {
"type": "object",
"description": "Bounds on how much work exists at once.",
"additionalProperties": false,
"properties": {
"maxConcurrentTurns": {
"type": "number",
"description": "Sessions that may have a model turn in flight simultaneously."
},
"maxLiveSessions": {
"type": "number",
"description": "Sessions that may exist at all."
},
"maxQueueLength": {
"type": "number",
"description": "Prompts that may wait for a turn slot."
},
"maxQueueWaitMs": {
"type": "number",
"description": "How long a queued prompt may wait before it expires unsent."
}
}
},
"timeouts": {
"type": "object",
"description": "Deadlines that end or unblock a session.",
"additionalProperties": false,
"properties": {
"idleMs": {
"type": "number",
"description": "No message and no agent activity for this long ends the session."
},
"startupMs": {
"type": "number",
"description": "How long the agent has to become ready before it is abandoned."
},
"questionMs": {
"type": "number",
"description": "How long a question posted to a thread waits for an answer."
},
"abortMs": {
"type": "number",
"description": "How long an abort waits for the agent before it is forced."
}
},
"required": [
"startupMs",
"questionMs",
"abortMs"
]
},
"problems": {
"description": "Every problem found, in the order the checks ran."
},
"$schema": {
"type": "string",
"description": "Where this schema lives."
}
},
"required": [
"chat",
"agent",
"projectRoot",
"stateDir",
"sandbox",
"output",
"shutdown",
"limits",
"timeouts",
"problems"
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://raw.githubusercontent.com/QaidVoid/errand/main/config.schema.json",
"title": "errand configuration"
}