Skip to content
Hoody.com

The daemon exposes five HTTP endpoints that describe its own health and the runtime state of every program it supervises: an unauthenticated health probe, two program-status readouts (one for the whole kit, one for a single program), and two log readers (a one-shot snapshot, and a live SSE tail). Every route is served from the daemon’s per-container hostname and is reached only through the kit’s URL; requests that do not arrive through the URL are refused by the Source IP Guard before the handler runs.

Service health check. Returns the standardized JSON health response when the daemon is up. ingressReplyRoute and the sandbox block report conditions the daemon cannot repair by itself, but neither one flips status because the daemon is serving either way. Unauthenticated, but still subject to the Source IP Guard.

This endpoint takes no parameters.

Terminal window
curl https://{projectId}-{containerId}-daemon-1.node-example-1.containers.hoody.com/api/v1/daemon/health

Service is healthy.

status is the gate: "ok" means the daemon is up and serving. Sandbox and ingress problems are surfaced as separate fields rather than by flipping status.

{
"status": "ok",
"service": "kit-daemon",
"built": "2026-02-10T12:00:00Z",
"started": "2026-02-10T17:39:50Z",
"memory": {
"rss": 25165824,
"heap": null
},
"fds": 14,
"pid": 1234,
"ingressReplyRoute": "healthy",
"ip": "203.0.113.42",
"userAgent": null,
"sandbox": {
"bwrap": true,
"nft": true,
"systemd_run": true,
"landlock_abi": 9,
"bwrap_version": "0.12.0",
"bwrap_tmpfs_size": true,
"firewall": "ok"
}
}
FieldTypeRequiredDescription
statusstringYesHealth status.
servicestringYesService name.
startedstringYesProcess start time as RFC3339 string.
pidintegerYesProcess ID.
ipstringYesContainer IP.
builtstringNoExecutable mtime as RFC3339 string.
memoryobject | nullNoMemory breakdown (rss in bytes, heap in bytes or null for Rust).
fdsintegerNoCount of open file descriptors.
ingressReplyRoutestringNoState of the container’s ingress reply routing: healthy, degraded, unverified, or not_installed. degraded means published services for this container are unreachable because a VPN’s routing rules outrank the daemon’s; it is not self-repairable, and it is reported here rather than by flipping status.
userAgentstringNoThe user agent that made this request, when applicable.
sandboxobjectNoAvailability of the mechanisms the program sandbox needs, probed once and cached. Sandbox problems do not change this endpoint’s HTTP status or its top-level status.
sandbox.bwrapbooleanNoWhether the bubblewrap binary is present and executable.
sandbox.nftbooleanNoWhether /usr/sbin/nft is present and executable (needed by every ingress policy).
sandbox.systemd_runbooleanNoWhether /usr/bin/systemd-run is present and executable.
sandbox.landlock_abiintegerNoLandlock ABI level reported by the kernel; 0 when Landlock is unavailable. TCP bind/connect restriction (network.mode: restricted) needs 4 or higher.
sandbox.bwrap_versionstringNoVersion reported by the bubblewrap binary, or null when it cannot be read.
sandbox.bwrap_tmpfs_sizebooleanNoWhether that bubblewrap has --size (0.10.0 or newer), required by process.tmp_size.
sandbox.firewallstringNoContainer-wide state of the ingress table: ok, degraded, or none.

Get all program statuses. Retrieves the current runtime status of every configured program: whether each one is running, stopped, or in another state, along with PID and uptime for the ones that are up. A port-range program is reported running whenever any instance is in a state other than stopped, including backoff or fatal. A status read whose supervisorctl call fails can report stopped or no instances, which is not proof the process ended.

This endpoint takes no parameters.

Terminal window
curl https://{projectId}-{containerId}-daemon-1.node-example-1.containers.hoody.com/api/v1/daemon/status

Successfully retrieved status for all programs.

Programs in various states:

{
"success": true,
"statuses": [
{
"id": 1,
"name": "web-server",
"enabled": true,
"status": {
"id": 1,
"status": "running",
"pid": 1234,
"uptime": "2:15:30"
}
},
{
"id": 2,
"name": "nodejs-app",
"enabled": false,
"status": {
"id": 2,
"status": "stopped"
}
},
{
"id": 3,
"name": "worker",
"enabled": true,
"status": {
"id": 3,
"status": "backoff"
}
}
]
}

All programs running:

{
"success": true,
"statuses": [
{
"id": 1,
"name": "web-server",
"enabled": true,
"status": {
"id": 1,
"status": "running",
"pid": 1234,
"uptime": "5:30:15"
}
},
{
"id": 2,
"name": "api-server",
"enabled": true,
"status": {
"id": 2,
"status": "running",
"pid": 1235,
"uptime": "5:30:10"
}
}
]
}
FieldTypeRequiredDescription
successbooleanYesWhether the read succeeded.
statusesarrayYesStatus information for all programs.
statuses[].idintegerYesProgram identifier.
statuses[].namestringYesProgram name.
statuses[].enabledbooleanYesWhether the program is enabled.
statuses[].statusobjectYesRuntime status for the program.
statuses[].status.idintegerYesProgram identifier (mirrors statuses[].id).
statuses[].status.statusstringYesCurrent runtime status of the program: running, stopped, starting, stopping, backoff, exited, fatal, or unknown.
statuses[].status.pidintegerNoProcess ID when running; omitted otherwise.
statuses[].status.uptimestringNoUptime as supervisord renders it. Omitted when the program is not running.
statuses[].status.supervisor_statestringNosupervisord’s own state, present when it differs from status (e.g. a run has ended but the launcher is still finishing).
statuses[].status.last_runobjectNoThe last ended run. Absent before the first run ends.
statuses[].status.last_run.exit_codeintegerYesThe program’s exit code, or null when ended by a signal.
statuses[].status.last_run.signalstringYesName of the signal that ended the program, or null.
statuses[].status.last_run.errorstringYesWhy the program never ran, the daemon’s reason (ttl expired, daemon restarted before the program finished), or null.
statuses[].status.last_run.ended_atintegerYesWhen the run ended, in milliseconds since the Unix epoch.
statuses[].status.last_run.finalbooleanYestrue when no automatic restart or retry follows.
statuses[].status.last_run.sourcestringNoWhere the result came from: launcher or supervisor.

Get specific program status. Retrieves the runtime status of one program. A standard program returns status (with PID and uptime when running). A port-range program returns instance when port is given, otherwise instances with running_count and total_count. The instances list omits stopped entries and includes every other state, so running_count counts backoff, exited, and fatal instances too; check each instance’s own status for liveness. A status read whose supervisorctl call fails can report stopped or an empty list, which is not proof the process ended.

NameInTypeRequiredDescription
idpathintegerYesUnique numeric identifier of the program. An id that is not a valid i32 (a word, an empty segment, a number too large) is refused by the path extractor with a plain-text 404 on every route that takes an id, before the handler runs. An id that parses but names no program is handled by the route, not the extractor.
portqueryintegerNoFilter to a specific port instance (for port-range programs only).
include_statsquerystringNoInclude resource stats (CPU, memory, process tree) for running programs. Accepts the literal strings "true" and "false". Where the stats land depends on the program: standard programs get a top-level stats; port-range programs get one stats per instance, on the instance itself (instance.stats or instances[].stats), never at the top level. Each carries pid, started_at, cpu_percent, memory_rss_bytes, process_count, and a per-process breakdown.
Terminal window
curl "https://{projectId}-{containerId}-daemon-1.node-example-1.containers.hoody.com/api/v1/daemon/status/100"

Successfully retrieved program status.

The shape returned depends on the program: standard programs carry status; a port-range program queried with a port carries instance; a port-range program queried without one carries instances plus running_count/total_count. Only success is common to all three, which is why it is the sole required field.

Running

{
"success": true,
"status": {
"id": 1,
"status": "running",
"pid": 1234,
"uptime": "2:15:30"
}
}

Stopped

{
"success": true,
"status": {
"id": 1,
"status": "stopped"
}
}

Fatal

{
"success": true,
"status": {
"id": 1,
"status": "fatal"
}
}
FieldTypeRequiredDescription
successbooleanYesWhether the read succeeded.
statusobjectNoRuntime status for a standard (non-port-range) program.
status.idintegerYesProgram identifier.
status.statusstringYesCurrent runtime status of the program.
status.pidintegerNoProcess ID when running; omitted otherwise.
status.uptimestringNoUptime as supervisord renders it.
status.supervisor_statestringNosupervisord’s own state, present when it differs from status.
status.last_runobjectNoThe last ended run. Absent before the first run ends.
status.last_run.exit_codeintegerYesThe program’s exit code, or null.
status.last_run.signalstringYesName of the signal that ended the program, or null.
status.last_run.errorstringYesWhy the program never ran, the daemon’s reason, or null.
status.last_run.ended_atintegerYesWhen the run ended.
status.last_run.finalbooleanYestrue when no automatic restart or retry follows.
status.last_run.sourcestringNoSource of the result (launcher or supervisor).
statsobjectNoResource stats for the program’s process tree (standard programs only). Present when include_stats=true and the program is running.
stats.pidintegerYesRoot process ID (the PID supervisord manages).
stats.started_atstringYesISO 8601 timestamp of when the process started.
stats.cpu_percentnumberYesTotal CPU usage across all processes in the tree. A lifetime average, not instantaneous.
stats.memory_rss_bytesintegerYesTotal RSS in bytes across all processes in the tree.
stats.process_countintegerYesNumber of processes in the tree (root + children).
stats.processesarrayYesPer-process breakdown of the process tree.
stats.processes[].pidintegerYesProcess ID.
stats.processes[].commandstringYesCommand line of the process.
stats.processes[].cpu_percentnumberYesCPU usage for this process (a lifetime average).
stats.processes[].memory_rss_bytesintegerYesRSS for this process.
instanceobjectNoPort-range program, single-port query.
instance.portintegerYesPort number this instance is running on.
instance.instance_namestringYesSupervisord process name (format: programname_port).
instance.statusstringYesCurrent runtime status of this instance.
instance.pidintegerNoProcess ID when running; omitted otherwise.
instance.uptimestringNoUptime as supervisord renders it.
instance.supervisor_statestringNosupervisord’s own state, present when it differs from status.
instance.last_runobjectNoThe last ended run.
instance.last_run.exit_codeintegerYesExit code, or null.
instance.last_run.signalstringYesSignal name, or null.
instance.last_run.errorstringYesReason, or null.
instance.last_run.ended_atintegerYesWhen the run ended.
instance.last_run.finalbooleanYestrue when no automatic restart or retry follows.
instance.last_run.sourcestringNoSource of the result.
instance.statsobjectNoResource stats for this instance’s process tree. Present when include_stats=true and the instance is running.
instance.stats.pidintegerYesRoot process ID.
instance.stats.started_atstringYesISO 8601 start timestamp.
instance.stats.cpu_percentnumberYesCPU usage.
instance.stats.memory_rss_bytesintegerYesRSS in bytes.
instance.stats.process_countintegerYesNumber of processes.
instance.stats.processesarrayYesPer-process breakdown.
instance.stats.processes[].pidintegerYesProcess ID.
instance.stats.processes[].commandstringYesCommand line.
instance.stats.processes[].cpu_percentnumberYesCPU usage.
instance.stats.processes[].memory_rss_bytesintegerYesRSS in bytes.
instancesarrayNoPort-range program, no port given: one entry per non-stopped instance.
instances[].portintegerYesPort number this instance is running on.
instances[].instance_namestringYesSupervisord process name.
instances[].statusstringYesCurrent runtime status of this instance.
instances[].pidintegerNoProcess ID when running; omitted otherwise.
instances[].uptimestringNoUptime as supervisord renders it.
instances[].supervisor_statestringNosupervisord’s own state.
instances[].last_runobjectNoThe last ended run.
instances[].last_run.exit_codeintegerYesExit code, or null.
instances[].last_run.signalstringYesSignal name, or null.
instances[].last_run.errorstringYesReason, or null.
instances[].last_run.ended_atintegerYesWhen the run ended.
instances[].last_run.finalbooleanYestrue when no automatic restart or retry follows.
instances[].last_run.sourcestringNoSource of the result.
instances[].statsobjectNoResource stats for this instance’s tree.
instances[].stats.pidintegerYesRoot process ID.
instances[].stats.started_atstringYesISO 8601 start timestamp.
instances[].stats.cpu_percentnumberYesCPU usage.
instances[].stats.memory_rss_bytesintegerYesRSS in bytes.
instances[].stats.process_countintegerYesNumber of processes.
instances[].stats.processesarrayYesPer-process breakdown.
instances[].stats.processes[].pidintegerYesProcess ID.
instances[].stats.processes[].commandstringYesCommand line.
instances[].stats.processes[].cpu_percentnumberYesCPU usage.
instances[].stats.processes[].memory_rss_bytesintegerYesRSS in bytes.
running_countintegerNoPort-range programs: number of instances that are starting or running. instances lists every non-stopped instance, so it can be longer.
total_countintegerNoPort-range programs: total number of possible instances.

Get program logs. Retrieve the last N lines from a program’s stdout or stderr log file. For a file larger than 10 MiB, reading starts 10 MiB before the end and continues to the end, so a file that is still growing can yield more, so fewer lines than requested can come back, and the first line can be partial; a cut inside a multi-byte UTF-8 character makes the read a 400. A successful lines=0 read returns empty content. Disabled logging, a missing port on a port-range program, a log path outside the log root, and a missing, unreadable, or non-UTF-8 file are 400s even when the program exists; a file error carries type and log_file too.

This is a snapshot of the file’s end. To keep receiving lines as they are written, use the SSE endpoint below.

NameInTypeRequiredDescription
idpathintegerYesProgram ID.
typequerystringNoLog stream: stdout or stderr. Default: stdout.
linesqueryintegerNoNumber of lines to return from the end of the file. Default: 100. A non-negative integer: 0 returns empty content, and the server clamps values above 10000 to 10000.
portqueryintegerNoPort number (required for port-range programs).
Terminal window
curl "https://{projectId}-{containerId}-daemon-1.node-example-1.containers.hoody.com/api/v1/daemon/programs/100/logs?type=stdout&lines=100"

Log content retrieved successfully.

{
"success": true,
"logs": "Server listening on :8080\nReady for connections\n",
"type": "stdout",
"lines": 2,
"log_file": "/var/log/kit/programs/web-server.stdout.log"
}
FieldTypeRequiredDescription
successbooleanNoWhether the read succeeded.
errorstringNoError message, or null.
logsstringNoLog content (last N lines).
typestringNoLog type that was read.
linesintegerNoNumber of lines returned.
log_filestringNoPath to the log file that was read.

GET /api/v1/daemon/programs/{id}/logs/stream

Section titled “GET /api/v1/daemon/programs/{id}/logs/stream”

Follow program logs (SSE). Live tail of a program’s stdout or stderr log over Server-Sent Events: first the last lines complete lines, then every line appended afterwards, until the client disconnects. Program, type, and port select the file exactly as in getProgramLogs, and the same requests are refused with the same JSON 400s before the stream starts. The file is re-examined four times a second.

At most 32 log streams are open across the daemon at once; the next call past that is a 429.

NameInTypeRequiredDescription
idpathintegerYesProgram ID.
typequerystringNoLog stream: stdout or stderr. Default: stdout.
linesqueryintegerNoNumber of complete lines replayed from the end of the file before following. Default: 100. 0 replays nothing and only follows; values above 10000 are clamped to 10000. The replay is found in the last 10 MiB of the file. Ignored when resuming with offset or Last-Event-ID.
portqueryintegerNoPort number (required for port-range programs).
offsetqueryintegerNoResume cursor: the id of the last line event received (a byte offset just past that line). Streaming restarts at the next line and no backlog is replayed. An offset beyond the end of the current file means the file was truncated or replaced since: the stream sends a reset event and starts from the top of the file. The cursor is a byte position, not a file identity, so a file replaced by one at least as long is not detected across a reconnect. Takes precedence over Last-Event-ID.
Last-Event-IDheaderstringNoSame cursor as offset, sent automatically by an EventSource when it reconnects. Must be a non-negative integer.
Terminal window
curl -N "https://{projectId}-{containerId}-daemon-1.node-example-1.containers.hoody.com/api/v1/daemon/programs/100/logs/stream?type=stdout&lines=100"

The stream is open. The body is text/event-stream and ends only when the client disconnects or after an error event. The Cache-Control response header carries no-cache.

event: open
data: {"log_file":"/var/log/kit/programs/web-server.stdout.log","type":"stdout","offset":2048,"exists":true}
event: line
id: 2102
data: {"line":"Server listening on :8080","offset":2102}
event: line
id: 2146
data: {"line":"Ready for connections","offset":2146}
: keepalive
event: line
id: 2196
data: {"line":"Accepted connection from 203.0.113.42","offset":2196}
FieldTypeRequiredDescription
linestringNoOne log line, without its terminator. Present on line.
offsetintegerNoByte offset: just past the line (line), where following starts (open), or 0 (reset).
reasonstringNoWhy reading restarted (reset): rotated or truncated.
log_filestringNoThe file being followed. Present on open.
typestringNoThe log stream (stdout or stderr). Present on open.
existsbooleanNoWhether the file existed when the stream opened. Present on open.
errorstringNoWhy the stream ended. Present on error.