Skip to content

Config contract

A package that takes configuration declares a config block in matrix.json plus optional config.schema.json / config.defaults.json files. The block tells the runner where to look for values and how to merge them.

The config block

The shape used in production today (paraphrased from packages/chat/matrix.json):

json
{
  "config": {
    "schema": "./config/config.schema.json",
    "defaults": "./config/config.defaults.json",
    "envPrefix": "MATRIX_CHAT_",
    "providers": [
      { "kind": "env",     "prefix": "MATRIX_CHAT_" },
      { "kind": "file",    "env": "MATRIX_CHAT_CONFIG_FILE", "optional": true },
      { "kind": "service", "service": "system.config",
        "namespace": "@open-matrix/chat",
        "env": "MATRIX_CHAT_CONFIG_SERVICE", "optional": true }
    ]
  }
}
FieldPurpose
schemaRelative path to a JSON Schema file describing the legal config shape
defaultsRelative path to a JSON file with the default values
envPrefixConvention used by the env provider
providers[]Ordered list of resolvers; later providers override earlier ones

Status: present state, partial. The CLI validator (manifestValidator.ts) does not currently inspect the config block. The block exists by convention; the chat runtime reads it directly. Schema validation across packages is target state.

Provider kinds in use

kindMeaning
envRead environment variables that start with prefix
fileRead a file path from env (the env var holds the path)
serviceLook up config under service/namespace via a config-service actor

Providers compose: a value present in service overrides one from file, which overrides one from env, which overrides the default.

Per-environment runtime config

Separate from the config block, package authors ship per-environment runtime config under .matrix/<env>.environment.json. This is what mx run . --env <env> reads (packages/mx-cli/src/utils/package-environment.ts):

json
{
  "name": "dev",
  "nats": {
    "mode": "embedded", "port": 4330, "wsPort": 4331,
    "dataDir": ".matrix/state/dev/nats",
    "pidFile": ".matrix/state/dev/nats-server.pid"
  },
  "runtime": {
    "root": "COM.OPEN-MATRIX.LOCAL.DIRECTOR",
    "runtimeId": "director-dev"
  },
  "package": { "publicRoot": "director" },
  "http": { "enabled": true, "port": 4332 }
}
Top-levelPurpose
nameRequired identifier (dev, integration, hivecast)
natsEmbedded or external NATS config (mode, port, wsPort, url, credentialsRef, dataDir, pidFile, binaryPath)
runtime.rootRequired. The wire root used for the bus (e.g. COM.NIMBLETEC.RICHARD-SANTOMAURO)
runtime.runtimeIdOptional override of the runtime id
runtime.runtimeMount, runtime.controlMountOptional overrides
package.publicRootOptional public namespace root for the package
httpPer-environment HTTP port for the standalone runner
hostOptional matrixDir pointing at a Host home

Runtime config resolution order

At runtime startup, when the package's factory is called:

  1. The runner loads .matrix/<env>.environment.json and uses it for transport and runtime identity.
  2. The factory reads config.defaults (if declared) as a base.
  3. The factory iterates config.providers[] in order, merging each provider's values onto the base.
  4. The merged config is validated against config.schema (if present), then handed to the actor instances via props.

Provider 1 → 2 → 3 is the documented order; the chat runtime implements it. Other packages with simpler needs may only declare defaults + envPrefix.

What goes in config vs in .matrix/<env>.environment.json

Belongs in config blockBelongs in .matrix/<env>.environment.json
Tunables a deployer might change (timeouts, feature flags, retries)Transport: NATS URL, WebSocket port, root
Provider connection optionsHTTP port for the runtime
Per-package limitsPer-Host paths (matrixDir, dataDir)
Schema-validated user configIdentity (runtimeId, root)

The split mirrors CLAUDE.md "Four Operational Units": package owns its config schema; environment owns the wire/transport identity.

Credentials never go in either file

CLAUDE.md Rule 4: credentials go through Factotum only. Do not reference API keys, OAuth tokens, or NATS credentials directly in matrix.json:config or .matrix/<env>.environment.json. Reference them through secretRefs in matrix.service.json (see mx-cli/src/utils/service-manifest.ts:IMatrixServiceSecretRef).

See also

Source: The config block convention is documented by use in packages/chat/matrix.json; runtime config layout is enforced by packages/mx-cli/src/utils/package-environment.ts.