Skip to content

Events ​

Each mode keeps one event log: what acted on its stack, and what the session did, one JSON object per line.

bash
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 them

The file is node_modules/.stackctl/modes/<mode>/events.log. The control API's GET /events streams the same lines as they are written.

jsonc
{"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/status and 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 reset deletes 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:

FieldWhat it is
timeWhen, ISO 8601 with the machine's offset, to the millisecond.
modeThe mode.
sessionIdThe mode's running session, as /query/status names it, or null.
pidThe 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).

typeFields
service-stateservice, status, ready, previousStatus, previousReady
infra-stateservice, status, previousStatus
session-stateready: 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.

typeAskedend adds
service-commandaction (start, stop, restart), serviceoutcome: done, busy, unknown, unavailable
infra-commandaction (start, stop, restart, reset, destroy), requested ([] for all)services, results (each ready, stopped, destroyed or failed), everything, error
compose-commandargs: what followed infra docker-composeexitCode
one-off-commandkind (exec, run), service or dir, command, or script and argsexitCode, signal
session-commandaction (quit, reset)
  • A restart ends when the service has been started again. Whether it got ready follows in service-state lines.
  • A quit ends when the session's services are down, just before its session-end.
  • A reset's lines open the new file, since the reset deleted the old one.

Every command says who asked in by:

by.kindFieldsWho
uikey: r, s, R, m or ^cA person at the terminal UI.
control-apirequest (POST /command/…), userAgentA control API request: curl/8.7.1, or stackctl/<version> from a stackctl command (service, stop, a takeover).
cliargv, cwd, agentA stackctl command run in a shell: its arguments, where it ran, and whether a coding agent ran it.
sessionThe 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.

typeFields
session-startservices, headless, startedByAgent, argv, version, logFile, replaced: the sessionId it took over, or null
session-endcause (quit, signal, error), durationMs
service-exitservice, exitCode, signal: its process exited, and stackctl hadn't stopped it
env-changedir: where a .env file changed, services: the ones it restarts

Released under the MIT License.