option-berthGuide

Facts as JSON

Every command has one machine shape: one JSON value on stdout, diagnostics on stderr, stable error codes. The same command answers a person and an agent; only the reader changes.

The rules

  • With plain --json, stdout holds exactly one JSON value. Diagnostics go to stderr and never mix into it.
  • Streaming commands use NDJSON: one value per line.
  • Exit codes are 0 for success, 1 for a run failure, 2 for an argument or option error, and 130 when interrupted by Ctrl-C.
  • An error is {"error":{"code":"…","message":"…","hint":"…"}}. The hint points at evidence that really exists.
  • No wire field is accepted without being read. A published field is a promise every client may rely on, so the protocol build fails on a field no handler acts on.

The stable codes

Stable error codes
GroupCodesWhat they mean
Inputinvalid_params, not_found, invalid_config, ambiguousThe request or the declaration is wrong; the message names the object.
Permissionpermission_deniedAn operation was refused; the hint says what to change.
Timetimeout, daemon_busyThe engine gave up, or another action holds the daemon; read the hint, then retry.
Observationobservation_empty, observation_unrecognized, observation_incomplete, observation_blind, observation_bind_deniedNone means “nothing is listening”; check observation.state and doctor --only listener_observation.
InternalinternalAn unexpected failure; report the command and the version, not just the message.

A worked read

A partial success is still one value. This up --json started one service and failed the other; the exit code is 1, and both outcomes are in the same document.

oberth up --json
{"services":[
  {"service":"api","state":"started","ready":false,"pid":1234,"port":3000},
  {"service":"worker","state":"failed","reason":"start_failed",
   "error":"exec: \"nosuchcmd\": executable file not found in $PATH",
   "hint":"check the command and its working directory"}
],"started":["api"],"skipped":[],"errors":["worker"]}
state
started, skipped or failed: the result of the start action, not a readiness claim.
reason
A stable, branchable failure reason such as start_failed, port_occupied or ready_timeout.
hint
A deterministic next step that points at evidence that exists: the service's logs only if the process really came up, the dependency's logs when a dependency was not ready, nothing when there is no executable step.
port_holder
Present on port_occupied: the port, pid, command and kind of the holder, plus the managed group, service name and run id when it is one of ours.
ready
The CLI's annotation, not a daemon fact: true after --wait confirms readiness, false when no wait was requested. false is not a failure.

What an absent field does not mean

Edit this page

Type to search every chapter and section.