Town plan reference
This page is generated from the JSON Schema of the town plan, https://myrtille.eu/schemas/town-plan/v1.json, itself generated from the schema the town hall validates the file with. The town plan page explains the file with an example.
A field marked yes under “Required” must be present when its parent is. An unknown key is an error everywhere, except under meta. A list such as couriers[] repeats the fields below it for each item.
version
Section titled “version”Required. number 1. Version of the town plan format: 1.
Required. object. The town itself.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
town.id |
string, 1 to 60 characters, pattern ^[a-z0-9][a-z0-9-]*$ |
yes | Stable id of the town. Once the first account exists, it must be the id of that town. | |
town.name |
string, 1 to 120 characters | yes | Name of the town. | |
town.locale |
one of en, fr |
no | en |
Language of the reports of the town. |
town.timezone |
string, 1 to 60 characters | no | UTC |
Time zone of the reports of the town, for example Europe/Paris. |
couriers
Section titled “couriers”Required. list of objects, 1 to 50 items. The sources of the town, at least one. Their credentials never appear in this file.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
couriers[].id |
string, 1 to 60 characters, pattern ^[a-z0-9][a-z0-9-]*$ |
yes | Stable id of the courier, named by the districts and buildings it feeds. Ids starting with agent- belong to the enrolled agents. | |
couriers[].type |
one of coolify, claude-code, webhook, docker, git-github, mcp, dokploy, otel, mock |
yes | The kind of source this courier reads. | |
couriers[].instance |
string, 1 to 60 characters, pattern ^[a-z0-9][a-z0-9-]*$ |
no | Name of the source instance, the courier id when absent. Its address and tokens live encrypted in the town hall, never in this file. | |
couriers[].discover |
boolean | no | true |
Whether the resources the source reports are added to the town. With false, a citizen it reports joins the town only when citizens lists it by its id; a citizen already in the town stays. |
couriers[].poll |
object | no | Polling cadence of a Coolify courier. | |
couriers[].poll.idleSeconds |
integer, 10 to 3600 | no | 45 |
Seconds between two polls while nothing runs. |
couriers[].poll.activeSeconds |
integer, 1 to 60 | no | 3 |
Seconds between two polls while a deployment runs, and for a minute after an action. |
couriers[].agents |
list of strings, at most 100 items, each 1 to 60 characters, pattern ^[a-z0-9][a-z0-9-]*$ |
no | Names of the enrolled agents this courier reads. On a coolify courier, the Docker agents deployed on its servers: they may report the crashes and stream the logs of its citizens. | |
couriers[].mcp |
object | no | The MCP server of a courier of type mcp: its citizen and the tools the town may call. | |
couriers[].mcp.transport |
one of http, stdio |
no | http |
http: the town hall calls the streamable HTTP endpoint stored for this courier. stdio: a local agent runs the server. |
couriers[].mcp.agent |
string, 1 to 60 characters, pattern ^[a-z0-9][a-z0-9-]*$ |
no | stdio only: name of the enrolled local agent that runs the server. | |
couriers[].mcp.server |
string, 1 to 60 characters, pattern ^[a-z0-9][a-z0-9-]*$ |
no | stdio only: name of the server in the configuration of that agent, the courier id when absent. | |
couriers[].mcp.name |
string, 1 to 120 characters | no | Name of the citizen shown in the town, the courier id when absent. | |
couriers[].mcp.district |
string, 1 to 60 characters, pattern ^[a-z0-9][a-z0-9-]*$ |
yes | Id of the district the citizen of the server lives in. | |
couriers[].mcp.building |
string, 1 to 60 characters, pattern ^[a-z0-9][a-z0-9-]*$ |
no | Id of the building the citizen of the server belongs to. | |
couriers[].mcp.timeoutSeconds |
integer, 1 to 120 | no | 30 |
Seconds the town hall waits for one answer of the server. |
couriers[].mcp.tools |
list of objects, at most 50 items | no | [] |
The tools the town may call. A tool not listed here is never offered nor called. |
couriers[].mcp.tools[].name |
string, 1 to 111 characters, pattern ^[A-Za-z0-9_-]+(\.[A-Za-z0-9_-]+)*$ |
yes | Name of the tool exactly as the MCP server lists it. It becomes the action mcp.tool.<name in lowercase>. | |
couriers[].mcp.tools[].danger |
one of safe, destructive |
no | destructive |
destructive (the default) always asks for a confirmation; safe follows the confirmation policy of the district. |
couriers[].mcp.tools[].minRole |
one of operator, admin |
no | admin |
The lowest role allowed to call the tool: admin (the default) or operator. |
couriers[].repositories |
list of objects, at most 200 items | no | Repositories of a git-github courier: pull requests become temporary citizens of the previews district. | |
couriers[].repositories[].name |
string, pattern ^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})\/[A-Za-z0-9._-]{1,100}$ |
yes | ||
couriers[].repositories[].district |
string, 1 to 60 characters, pattern ^[a-z0-9][a-z0-9-]*$ |
no | ||
couriers[].repositories[].building |
string, 1 to 60 characters, pattern ^[a-z0-9][a-z0-9-]*$ |
no | ||
couriers[].repositories[].preview |
object | no | ||
couriers[].repositories[].preview.courier |
string, 1 to 60 characters, pattern ^[a-z0-9][a-z0-9-]*$ |
yes | ||
couriers[].repositories[].preview.application |
string, pattern ^[A-Za-z0-9_-]{1,64}$ |
yes | ||
couriers[].district |
string, 1 to 60 characters, pattern ^[a-z0-9][a-z0-9-]*$ |
no | District where an otel courier puts the services it discovers by their service.name. Default: the first district whose source is this courier. | |
couriers[].meta |
object of free keys | no | Free keys of your own, never a secret: a key that looks like one is refused. |
districts
Section titled “districts”Required. list of objects, 1 to 50 items. The districts of the town, at least one: one per environment, plus previews and local ones.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
districts[].id |
string, 1 to 60 characters, pattern ^[a-z0-9][a-z0-9-]*$ |
yes | Stable id of the district. | |
districts[].name |
string, 1 to 120 characters | yes | Name shown in the town. | |
districts[].kind |
one of environment, previews, local |
yes | environment for an environment of a source, previews for preview deployments, local for developer workstations. | |
districts[].source |
object | no | Where the citizens of the district come from. | |
districts[].source.courier |
string, 1 to 60 characters, pattern ^[a-z0-9][a-z0-9-]*$ |
yes | Id of the courier the district mirrors. | |
districts[].source.environment |
string, 1 to 120 characters | no | Name of the environment in the source, for example production. | |
districts[].source.previews |
boolean | no | True when this district holds the preview deployments of the courier. | |
districts[].confirmation |
one of always, destructive, never |
no | Which actions ask for a confirmation: always, destructive, or never (refused for a production district). Turning actions on is never done here: an admin does it in the town. | |
districts[].overlay |
object | no | Local overlay of a local district. | |
districts[].overlay.shadows |
string, 1 to 60 characters, pattern ^[a-z0-9][a-z0-9-]*$ |
yes | Id of the district whose twin services a running local service greys out. | |
districts[].position |
object | no | Fixed place of the district, in tiles, with rendering.layout manual. | |
districts[].position.x |
integer, at least 0 | yes | Column, in tiles. | |
districts[].position.y |
integer, at least 0 | yes | Row, in tiles. | |
districts[].meta |
object of free keys | no | Free keys of your own, never a secret: a key that looks like one is refused. |
buildings
Section titled “buildings”Optional. list of objects, at most 200 items. The buildings of the town: one per project of a source, or groups made by hand.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
buildings[].id |
string, 1 to 60 characters, pattern ^[a-z0-9][a-z0-9-]*$ |
yes | Stable id of the building. | |
buildings[].name |
string, 1 to 120 characters | yes | Name shown in the town. | |
buildings[].source |
object | no | The project of a source this building stands for. Without it, the building only groups citizens. | |
buildings[].source.courier |
string, 1 to 60 characters, pattern ^[a-z0-9][a-z0-9-]*$ |
yes | Id of the courier the building mirrors. | |
buildings[].source.project |
string, 1 to 200 characters | yes | Name of the project in the source. | |
buildings[].position |
object | no | Fixed place of the building, in tiles, with rendering.layout manual. | |
buildings[].position.x |
integer, at least 0 | yes | Column, in tiles. | |
buildings[].position.y |
integer, at least 0 | yes | Row, in tiles. | |
buildings[].meta |
object of free keys | no | Free keys of your own, never a secret: a key that looks like one is refused. |
citizens
Section titled “citizens”Optional. list of objects, at most 800 items. Overrides of discovered citizens, by id, and citizens declared by hand. Discovered citizens need not be listed.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
citizens[].id |
string, 3 to 300 characters, pattern ^[a-z0-9][a-z0-9-]*:[^\s:]\S*$ |
yes | Id of the citizen: <courier>:<kind>:<ref>. A discovered citizen is overridden by its exact id. | |
citizens[].name |
string, 1 to 120 characters | no | Name shown in the town. | |
citizens[].kind |
one of service, database, automation, agent, mcp, server, preview, local |
no | The kind of citizen. | |
citizens[].avatar |
string, at most 60 characters | no | The look of the citizen. | |
citizens[].building |
string, 1 to 60 characters, pattern ^[a-z0-9][a-z0-9-]*$ |
no | Id of the building the citizen belongs to. | |
citizens[].district |
string, 1 to 60 characters, pattern ^[a-z0-9][a-z0-9-]*$ |
no | Id of the district the citizen belongs to. | |
citizens[].expectedEvery |
string, pattern ^\d+(s|m|h|d)$ |
no | How often the citizen gives news, such as 90s, 10m, 6h or 1d. Past it, the citizen turns unknown. | |
citizens[].actions |
list of objects, at most 20 items | no | Outgoing actions of a citizen declared by hand. | |
citizens[].actions[].type |
string task.run |
yes | The kind of action: task.run. | |
citizens[].actions[].label |
string, 1 to 80 characters | yes | Label of the action in the town. | |
citizens[].actions[].webhook |
string, at most 2048 characters, format uri | yes | https address the town hall calls, with a signed request, to run the action. | |
citizens[].repo |
object | no | The repository of an agent citizen. | |
citizens[].repo.path |
string, 1 to 500 characters | yes | Path of the repository on the workstation, which places the Claude Code sessions run there. | |
citizens[].repo.agent |
string, 1 to 60 characters, pattern ^[a-z0-9][a-z0-9-]*$ |
yes | Name of the local agent of that workstation. | |
citizens[].repo.localPorts |
list of integers, at most 20 items, each 1 to 65535 | no | Ports the local agent watches on that workstation for the local overlay. | |
citizens[].missions |
object | no | Missions of an agent citizen. | |
citizens[].missions.enabled |
boolean | no | false |
Whether members may give this agent a mission. |
citizens[].missions.maxConcurrent |
integer, 1 to 1 | no | 1 |
Missions at once in this repository: 1. |
citizens[].missions.branchPrefix |
string, at most 60 characters | no | mission/ |
Prefix of the branch each mission creates. |
citizens[].position |
object | no | Fixed place of the citizen, in tiles, with rendering.layout manual. | |
citizens[].position.x |
integer, at least 0 | yes | Column, in tiles. | |
citizens[].position.y |
integer, at least 0 | yes | Row, in tiles. | |
citizens[].meta |
object of free keys | no | Free keys of your own, never a secret: a key that looks like one is refused. |
Optional. object. Settings of the mail.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
mail.quietHours |
object | no | Hours when the notifications of the mail wait: the push of an item of another urgency than except is sent when they end, if the item is still open, and the daily summary waits too. The mail shows every item at once. | |
mail.quietHours.from |
string, pattern ^([01]\d|2[0-3]):[0-5]\d$ |
yes | Start of the quiet hours, HH:MM in the time zone of the town, included. | |
mail.quietHours.to |
string, pattern ^([01]\d|2[0-3]):[0-5]\d$ |
yes | End of the quiet hours, HH:MM in the time zone of the town, excluded. Earlier than from, they span midnight; equal to from, there are none. | |
mail.quietHours.except |
list of values among low, normal, high, critical |
no | ["critical"] |
Urgencies that still notify during the quiet hours. critical always does, listed or not. |
mail.defaults |
object | no | Default lifetimes of the mail. | |
mail.defaults.incidentTtlHours |
integer, 1 to 720 | no | 24 |
Hours an incident with a fixed lifetime stays open before it expires: failed deployments, container, backup and task incidents. Without this block, 24 h, and 48 h for backups and tasks. An incident that ends on an event (server unreachable, courier disconnected) waits for it. |
rendering
Section titled “rendering”Optional. object. How the town is drawn.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
rendering.layout |
one of auto, manual |
no | auto |
auto lays the town out by itself; manual keeps the positions given in this file. |
rendering.tileset |
string, at most 60 characters | no | default |
Name of the tileset the town is drawn with. The town ships default only: another name is drawn with default, and the town hall logs a warning. |
rendering.palette |
string, at most 60 characters | no | endesga32 |
Name of the palette of the art. The town ships endesga32 only (a fixed palette): another name is drawn with endesga32, and the town hall logs a warning. |
rendering.decor |
list of objects, at most 200 items | no | Decorations of buildings, drawn on the back wall of each copy of the building, ten props at most. | |
rendering.decor[].building |
string, 1 to 60 characters, pattern ^[a-z0-9][a-z0-9-]*$ |
yes | Id of the building to decorate. | |
rendering.decor[].props |
list of strings, at most 50 items, each at most 60 characters | yes | Props to place, in order: a tile of the furniture (plant-1, poster-2, coffee-machine, shelf, lamp), or a family and any name after it (plant, poster-travel), which always gives the same tile for the same name. A prop the town has no tile for is left out, and the town hall logs a warning. |
Optional. object of free keys. Free keys of your own, never a secret: a key that looks like one is refused.