fjx.json — per-project config
Each project directory the supervisor manages must contain an fjx.json at its root. It is loaded once at supervisor startup and re-read on SIGHUP / fjx supervise reload. The loader lives in src/project-config.ts.
Shape
{
"images": {
"pm": "fjx-base",
"dev": "fjx-base",
"qa": "fjx-base"
}
}
The top-level value MUST be a JSON object. Unknown top-level fields are tolerated and preserved verbatim — a newer supervisor writing extra keys won't break an older fjx reading the same file.
Fields
images (object, optional)
Maps a role name to the container image the supervisor spawns for that role's tick. Keys are role identifiers (pm, dev, qa today; more may be added). Values are image references resolvable by the host's container runtime — fjx-base, ghcr.io/example/fjx-dev:v1.2.3, a local tag from just images::build, etc.
- Missing
imagesis equivalent to{}: no role has an image override. - A role without an entry falls back to the supervisor's default image. If there is no default and no entry, that role's ticks are skipped (the supervisor logs and moves on).
- Values MUST be strings; an object or array under
images.<role>is a hard error. - Unknown role keys are preserved but ignored by the current supervisor.
Validation errors
The loader fails fast with a ProjectConfigError when:
- The file exists but is not valid JSON.
- The top-level value is not a JSON object (a bare array, string, or
null). imagesis present but not an object.images.<role>is present but not a string.
A missing fjx.json is not an error — loadProjectConfig returns undefined and the supervisor treats the project as having no overrides. This is intentional so a fresh project directory can be added to the supervisor's project list before its config lands.
Reloading
The supervisor re-reads each project's fjx.json on:
SIGHUPto the daemon processfjx supervise reloadover the control socket (see supervisor setup)
In-flight ticks finish under their previous config; the next scheduled tick picks up the new values. There is no partial reload — the whole file is re-parsed atomically.