Documentation menu
Structured output
CLIThe four machine-readable contracts • list JSON, full snapshots, watch NDJSON, and why documents, plus what they can expose.
Kickoutchi has four machine-readable interfaces. Field names and enum values are lowercase
snake_case and are treated as a contract, so parsing them is safe.
| Command | Contract | Form |
|---|---|---|
kick list --json |
kickoutchi.list/1 |
JSON array |
kick list --snapshot-json |
kickoutchi.snapshot/1 |
One object |
kick watch --json |
kickoutchi.watch_event/1 |
NDJSON |
kick why PORT --json |
kickoutchi.why/1 |
One object |
Snapshot, watch, and why documents carry in-band schema and integer version fields. Legacy
list --json stays a bare top-level array so existing scripts keep working.
Diagnostics always go to stderr, so stdout stays parseable.
kickoutchi.list/1 • the compatibility array
list --json is the visible-row view: it honours --port, --process, --filter, --sort, and
hide_system_processes, and it lists listening TCP and bound UDP sockets only.
[
{
"protocol": "tcp",
"local_addr": "127.0.0.1",
"local_port": 3000,
"state": "listen",
"pid": 18422,
"process_name": "node",
"executable_path": "/usr/bin/node",
"command_line": "node server.js",
"parent_pid": 18001,
"parent_process_name": "pnpm",
"child_pids": [],
"protected": false,
"platform": "linux",
"permission": "full",
"label": "web dev"
}
]
The only field added in 1.3.0 is nullable label.
Missing metadata is null, never an empty string. An empty result is [].
kickoutchi.snapshot/1 • the full observation
list --snapshot-json emits one object describing everything Kickoutchi could see within its
declared scope. It deliberately bypasses list filters, sorting, hide_system_processes, and
the listening-TCP/bound-UDP projection • and it does not serialize full command lines.
kick list --snapshot-jsonTop-level keys:
| Key | Contains |
|---|---|
schema / version |
"kickoutchi.snapshot" and 1. |
scope |
Observation kind, an identifier, and permanent limitations. |
completeness |
complete or partial within that scope. |
owner_completeness |
Whether owner attribution across the snapshot is complete. |
capture |
When collection started and finished. |
sockets |
Every endpoint, with state, timer evidence, owners, and label. |
processes |
Identities, names, paths, parents, and metadata completeness. |
evidence_gaps |
What could not be established, and what each gap affects. |
omitted_evidence_gap_count |
How many gaps were dropped by the output bound. |
A socket entry looks like this:
{
"endpoint": { "protocol": "tcp", "address": "127.0.0.1", "port": 3000, "ipv6_scope": null },
"state": { "kind": "listen", "native_code": null },
"timer": {
"kind": "none",
"native_code": null,
"raw_ticks": 0,
"estimated_remaining_milliseconds": 0,
"certainty": "estimated"
},
"owners": {
"owners": [
{
"kind": "verified",
"identity": {
"pid": 18422,
"start_marker": { "kind": "linux_start_ticks", "ticks": 11977192 }
}
}
],
"omitted_owner_count": 0,
"completeness": "complete",
"reasons": []
},
"socket_token": { "kind": "linux_inode", "value": 1631547 },
"label": "web dev"
}
Owners are either verified (PID plus a start marker) or an explicitly unverified PID. That
distinction is the whole point: a bare PID is not stable identity.
Evidence gaps and affected PID counts
Every EvidenceGap has an exact nullable pid and an always-present nullable
affected_pid_count. A gap tied to one known PID uses pid and leaves affected_pid_count null.
When a bounded Linux scan loses evidence for several PIDs that have no discovered edge to a target
socket, one aggregate gap instead uses a positive affected_pid_count and leaves pid null.
Repeated consistency passes keep the maximum aggregate count rather than adding counts that may
describe the same PIDs. omitted_evidence_gap_count means something different: it counts gap
records that could not fit in the output array, never PIDs represented by an aggregate gap.
kickoutchi.watch_event/1 • one object per line
watch --json writes NDJSON. Every line is independently valid JSON and bounded to 64 KiB, so
streaming consumers never need to buffer the whole session.
{
"schema": "kickoutchi.watch_event",
"version": 1,
"sequence": 0,
"event": "bind",
"observation": {
"previous_completed_unix_ms": 1784929159673,
"attempt_started_unix_ms": 1784929159955,
"attempt_completed_unix_ms": 1784929159972
},
"data": {
"endpoint": { "protocol": "tcp", "address": "127.0.0.1", "port": 3000, "ipv6_scope": null },
"state": { "kind": "listen", "native_code": null },
"previous_owners": null,
"current_owners": {
"owners": [
{
"kind": "verified",
"identity": {
"pid": 18422,
"start_marker": { "kind": "linux_start_ticks", "ticks": 11977192 }
}
}
],
"omitted_owner_count": 0,
"completeness": "complete",
"reasons": []
},
"multiplicity": 1,
"label": "web dev",
"filter_result": "matched",
"certainty": "proven",
"evidence": [],
"omitted_evidence_count": 0,
"evidence_gaps": [],
"omitted_evidence_gap_count": 0
}
}
eventisbaseline,bind,release,replacement, orcollection_gap.sequenceincrements across the session, so gaps in it are detectable.observationbrackets a collection attempt, not an exact kernel event time.previous_owners/current_ownersare what makesreplacementmeaningful.filter_resultcan bematchedorindeterminate• see Watch & Why.
Records omit full command lines.
kickoutchi.why/1 • one document
why --json emits a single object with the same verdicts, certainty, evidence, gaps, labels, and
aggregate exit status as the human output. It never collects full command lines and never runs
Docker.
Output is streamed, so any legal bounded result shape is emitted rather than being rejected for overall size.
Privacy
Distinctions worth preserving
When you consume these documents, keep these apart • Kickoutchi went to some trouble not to conflate them:
nullvs empty vs omitted. Unknown, known-to-be-empty, and dropped-by-a-bound are three different states.partialvscomplete.completemeans complete within the declared native scope • not every socket in every container, namespace, VM, or subsystem.- Verified vs unverified owners. Only a verified identity survives PID reuse.
- Certainty. It attaches to the individual claim carrying it, not to the document.