Events
Each mode keeps one event log: what acted on its stack, and what the session did, one JSON object per line.
stackctl status # the session now, and the last commands that acted on it
stackctl events -n 20 # the last 20 lines
stackctl events -f # and follow themThe file is node_modules/.stackctl/modes/<mode>/events.log. The control API's GET /events streams the same lines as they are written.
{"type":"infra-command","phase":"begin","id":"56de9f1a","action":"restart","requested":["db"],"by":{"kind":"cli","argv":["infra","restart","-s","db"],"cwd":".","agent":false},"time":"2026-11-11T13:22:12.255-06:00","mode":"development","sessionId":"add7056e-…","pid":91514}
{"type":"infra-state","service":"db","status":"stopped","previousStatus":"running", /* … */}
{"type":"service-state","service":"backend","status":"running","ready":false,"previousStatus":"running","previousReady":true, /* … */}
{"type":"infra-command","phase":"end","id":"56de9f1a", /* … */ "ok":true,"durationMs":2249,"services":["db"],"results":{"db":"ready"}, /* … */}- What changes the stack is written; what reads it never is.
status,logs,show-config,events,GET /query/statusand Compose's read-only commands (ps,logs,config,top, …) leave no line. - Whatever does it writes it: the session for its keys, its control API and itself; a stackctl command for itself, whether a session runs or not.
- Kept across sessions. Over 5 MB, the next line starts a new file and the old one becomes
events.1.log.stackctl resetdeletes it with the mode's other state. - Arguments as given, a URL's password masked (
postgres://app:***@…). No environment value is written.
Every line
type comes first and decides the rest. Its own fields follow, and every line ends with:
| Field | What it is |
|---|---|
time | When, ISO 8601 with the machine's offset, to the millisecond. |
mode | The mode. |
sessionId | The mode's running session, as /query/status names it, or null. |
pid | The process that wrote the line. |
Paths are relative to the workspace root. A reader ignores a type it doesn't know.
State
What something is now, written by the session when it changes, with what it was (null the first time).
type | Fields |
|---|---|
service-state | service, status, ready, previousStatus, previousReady |
infra-state | service, status, previousStatus |
session-state | ready: every service that isn't stopped ready, every infra service running |
A service's status is running, waiting, restarting or stopped. A dev script that watches its files, such as node --watch, outlives the app it runs: when the app crashes the service stays running, and its ready turns false.
Commands
Something asked stackctl to act. Each command writes two lines with the same id: phasebegin with what was asked, then end with ok, durationMs and what came of it. A begin with no end is a command still running, or one whose process was killed.
type | Asked | end adds |
|---|---|---|
service-command | action (start, stop, restart), service | outcome: done, busy, unknown, unavailable |
infra-command | action (start, stop, restart, reset, destroy), requested ([] for all) | services, results (each ready, stopped, destroyed or failed), everything, error |
compose-command | args: what followed infra docker-compose | exitCode |
one-off-command | kind (exec, run), service or dir, command, or script and args | exitCode, signal |
session-command | action (quit, reset) |
- A
restartends when the service has been started again. Whether it got ready follows inservice-statelines. - A
quitends when the session's services are down, just before itssession-end. - A
reset's lines open the new file, since the reset deleted the old one.
Every command says who asked in by:
by.kind | Fields | Who |
|---|---|---|
ui | key: r, s, R, m or ^c | A person at the terminal UI. |
control-api | request (POST /command/…), userAgent | A control API request: curl/8.7.1, or stackctl/<version> from a stackctl command (service, stop, a takeover). |
cli | argv, cwd, agent | A stackctl command run in a shell: its arguments, where it ran, and whether a coding agent ran it. |
session | The session itself: bringing its infra up as it starts. |
A session's infra keys and its control API's infra requests run stackctl infra <action> as a command of its own, which writes the lines with the key or request that asked.
Lifecycle
What the session does on its own.
type | Fields |
|---|---|
session-start | services, headless, startedByAgent, argv, version, logFile, replaced: the sessionId it took over, or null |
session-end | cause (quit, signal, error), durationMs |
service-exit | service, exitCode, signal: its process exited, and stackctl hadn't stopped it |
env-change | dir: where a .env file changed, services: the ones it restarts |