Appearance
Config inheritance
A Host has exactly two layers of configuration. There is no hierarchical config tree, no MATRIX_HOME walk-up at the Host level, no .env-style overlays. Just two files per runtime, and the Host writes the lower file deterministically from the upper one.
The two layers
Layer 1: host.json
<host-home>/host.json. Hand-edited. Owned by the operator. Schema is IMatrixHostConfig (see Host config).
This is where transport root, NATS URL, HTTP host/port, auth mode, and storage path overrides live. The Host normalizes it on read (HostStateStore.loadOrCreateConfig, host-state-store.ts:31-44) and writes back any normalization changes. The file is the source of truth for the Host process.
Layer 2: <host-home>/runtime-env/<runtimeId>.environment.json
Generated. Owned by the Host. Never edit by hand — every Host start and every config reload rewrites it.
json
// example
{
"name": "host",
"nats": {
"mode": "external",
"url": "nats://127.0.0.1:4270",
"wsUrl": "ws://127.0.0.1:4271",
"credentialsRef": "file:credentials.json"
},
"runtime": {
"root": "COM.OPEN-MATRIX.LOCAL.AUTHORITY",
"runtimeId": "RUNTIME-HOST-LOCAL-ABC123-CHAT",
"runtimeMount": "system.runtimes.RUNTIME-HOST-LOCAL-ABC123-CHAT",
"controlMount": "system.runtimes.RUNTIME-HOST-LOCAL-ABC123-CHAT.control"
},
"host": {
"matrixDir": "/home/me/.matrix"
},
"http": {
"enabled": true,
"port": 5183
}
}The runtime child process reads this file via the --env-file <path> argument (cli.ts:599-617). Every runtime gets its own file; mounts and the served-app port differ per runtime, but nats and host are identical across all runtimes on one Host.
Field-by-field inheritance
Field in runtime-env/*.environment.json | Comes from | Editable by operator? |
|---|---|---|
name | --env <name> flag at matrix up, persisted in record | yes (re-up) |
nats.mode | hard-coded "external" (Host always exposes NATS as external to runtimes) | no |
nats.url | host.json transport.nats (or wrapper-supervised sibling URL) | yes (host.json) |
nats.wsUrl, nats.credentialsRef | same | yes |
runtime.root | host.json transport.root | yes |
runtime.runtimeId, runtimeMount, controlMount | computed at start | no |
host.matrixDir | <home> | yes (move home; rare) |
http.host, http.port | gateway runtimes: host.json http. Served-app runtimes: per-runtime allocated port | yes for gateway; never for served apps |
When the Host rewrites runtime-env
MatrixHostService._refreshRuntimeEnvironmentFiles (matrix-host-service.ts:200-232) iterates every persisted record and calls _refreshRuntimeEnvironmentFile for each. That happens:
- On every successful
start()— the Host overwrites every existing environment file with the current Host status (so even if you editedhost.jsonwhile the Host was stopped, runtimes get the new values on next start). - On every successful
applyUpdatedHostConfig— i.e. after aconfig.reloadRPC. - Lazily on
_refreshRuntimeRecordServedAppMetadata, when a runtime's webapp metadata in the record drifts from the environment file'shttpblock.
The actual file content comes from writeRuntimeEnvironmentFile (matrix-host-service.ts:1529-1568), which preserves some per-runtime state (the served-app port the OS allocated) while overwriting NATS, runtime, and host blocks.
Practical consequences
host.jsonis the only file you edit. Everything inruntime-env/is derived. Touchingruntime-env/files directly will be undone the next time the Host updates them, and there is no guarantee of when that is.config.reloadis a real restart. It is not a "hot reload"; it stops every runtime and restarts them with the new config. Worker bootstrap and any in-flight requests are interrupted. SeeMatrixHostService._applyUpdatedHostConfigatmatrix-host-service.ts:180-198.- Per-runtime overrides do not exist. If you need one runtime on a different NATS URL, that's a different Host. The Host configuration scope is the home, not the runtime.
MATRIX_HOST_HOMEenv var only selects the home, not config contents. There is no env-var-driven config fallback inside the Host process.
When config comes from somewhere else
A few upstream sources can change host.json-relevant fields, all of them by writing to host.json directly via a documented path:
| Upstream | Writes which field | When |
|---|---|---|
hivecast install --root <root> | transport.root / authorityRoot | install time |
hivecast install --http-port <p> | http.port | install time |
hivecast login --device (via system-auth) | transport.spaceId, routeKey, publicNamespace | pairing |
host.control config.reload RPC | (none directly — re-reads existing file) | runtime |
There is intentionally no automatic merging from ~/.matrix/config.d/, no host.json.local, no MATRIX_* env-overlay. Two layers, one source of truth per layer.