Kk-carrier docs

Reference

Normative wire formats, exit codes, budgets and on-disk layout. This page states what K does and requires, not why. For the reasoning, read the design; for a narrative, the guide.

Two protocols: the runner protocol on stdin and stdout, and the command controller protocol between host and controller Runner protocol Command controller protocol Callersupervisor RunnerK + adapter stdin: one JSON request stdout: one JSON response + exit code 0, 1, 2 or 3 stderr: logs actions: upgrade, recover, status HostcreateCommandHost Controlleryour program stdin: one JSON request stdout: ok + evidence bounded to 64 KiB, 30 s actions: fence, quiesce, stop, start, probe, resume
Different protocols. Upgrade, recover and status go to the runner; fence, quiesce, stop, start, probe and resume go to the application controller.

Protocol v1

One JSON request on stdin, one response on stdout, then exit. Logs go to stderr. Decoded input is bounded to 16,384 JavaScript string code units. Unknown fields, actions or protocol versions, invalid ids or targets, and a nonboolean consented are rejected before the adapter factory runs. Request ids and target strings are nonempty, trimmed strings of at most 256 code units.

Requests

{"protocolVersion":1,"action":"upgrade","id":"job-123","targetVersion":"2.0.0","consented":true}
{"protocolVersion":1,"action":"recover"}
{"protocolVersion":1,"action":"recover","expected":{"id":"job-123","targetVersion":"2.0.0"}}
{"protocolVersion":1,"action":"status"}
consented
Records approval already obtained by an authenticated caller. It is not authorization from an untrusted client. Ownership and compatibility checks still apply.
expected
Binds automated recovery to one operation. The binding is checked under the lock before any controller action. Missing or mismatched history refuses recovery. A completed original operation replays its result even when newer work is pending. An operator recover without expected runs once against the current unfinished operation.

Actions and exit codes

ActionEffect0123
upgradeInstall the exact requested version; a mismatched source result is rejectedpromoted or up-to-datefailure or rollbackheldunresolved
recoverSettle persisted work under the same lock; never starts a new upgradesuccessful or no recorded outcomerecorded failure or rollbackheld receiptunresolved
statusRead the current receipt without lifecycle callsreadable, including genesisunreadable

Responses

Execution replies carry protocolVersion, action, result, exitCode, operation and error. Input rejection or adapter-construction failure may return only protocolVersion, result, exitCode and error. Termination can leave no complete response; inspect persistent state and recover.

operation.kind is observed (with the operation record), genesis (no operation recorded) or unreadable.

Receipts and retries

operation.json holds the current operation. Before starting another, K archives a terminal receipt at receipts/<sha256(operation-id)>.json under the same lock. Archived receipts have no automatic garbage collection.

Supervisor budgets

BudgetDefaultOption
Worker execution10 minutesexecutionTimeoutMs
Each recovery attempt2 minutesrecoveryTimeoutMs
Recovery attempts2recoveryAttempts (0 to 10)
Totalexecution + 2 × recovery (14 minutes)totalTimeoutMs
Exit observation after termination1 secondfixed

All budgets are positive integers. Artifact acquisition has separate transfer budgets derived from the artifact size (artifactTransferPolicy); downloadVerified called without any budget uses the same size-derived ones. An unconfirmed worker exit forbids takeover. Exhaustion preserves state and returns exit 3 with a recovery file; it never reports success.

Engine host-call budget

Every host call, including fence, readback, resume and recovery, has a positive budget, default 120 seconds (hostCallBudgetMs). A call whose effect is uncertain raises HostCallUncertain; the worker retains its lock until it exits. Bundled workers exit after flushing their response. An in-process caller that receives HostCallUncertain must also exit rather than reuse that worker.

Command controller protocol

createCommandHost({stateDir, command, timeoutMs?}) runs an external controller via argv, without a shell.

Request on stdin:

{"protocolVersion":1,"action":"fence"}
{"protocolVersion":1,"action":"quiesce"}
{"protocolVersion":1,"action":"stop","slot":"stable","artifactPath":"<stateDir>/slots/stable/artifact.bin"}
{"protocolVersion":1,"action":"start","slot":"experiment","artifactPath":"<stateDir>/slots/experiment/artifact.bin"}
{"protocolVersion":1,"action":"probe"}
{"protocolVersion":1,"action":"resume"}

Response on stdout:

{"protocolVersion":1,"ok":true}
{"protocolVersion":1,"ok":true,"evidence":{"version":"2.0.0","pid":4242,"startId":"..."}}

Lock protocol

upgrade.lock has one live owner per state directory. Unique process-owned contender entries serialize creation and reclamation, including the partial-write window. Only entries of provably dead owners are reclaimed. Pid reuse conservatively refuses acquisition; age never proves that a live owner is dead. The protocol requires local atomic file creation and coherent directory reads. It is not a distributed or NFS lock.

State directory layout

<stateDir>/
  upgrade.lock            live owner
  upgrade.lock.claims/    contender entries
  journal.jsonl           append-only write-ahead phase record, fsync'd per line
  operation.json          current operation receipt
  receipts/<sha256(id)>.json
  slots/stable/artifact.bin
  slots/stable/VERSION
  slots/experiment/artifact.bin
  slots/experiment/VERSION
  incoming/               staging for verified downloads before slot placement
  controllers/            recorded controller pids

Each slot holds one artifact.bin and its VERSION. Package layouts and additional install hooks need a product contract. Application data belongs outside the slots. status reads operation.json without taking the lock.

Journal entries are {seq, timestampMs, intent, detail}. The handing-over entry's detail.priorStartId records the incarnation probed before the upgrade touched anything; it is absent when nothing was live. Renames and journal appends fsync the containing directory on POSIX. Slot publication also syncs the copied artifact, VERSION file and staging directory first. A failed directory sync stops the operation. Windows directory sync is a no-op and provides no equivalent power-loss durability guarantee.

Supervisor scratch layout

<scratchDir>/k-runner-<random>/
  runner.mjs | runner.bin   verified runner (mode 0700)
  recovery.json             invocation descriptor: file, interpreter, sha256, size, recover request

recovery.json is an invocation descriptor, not a transaction log. It is removed after settlement and retained on an unresolved result. resumeRunner(path) re-verifies the runner against the recorded hash before executing it.

Release metadata

{"version":"2.0.0","url":"https://.../service","sha256":"<hex>","size":123456,
 "gzip":{"url":"https://.../service.gz","sha256":"<hex>","size":45678}}

sha256 and size always describe the installed bytes. When gzip is present K downloads and verifies the compressed object, bounds decompression, then verifies the canonical size and hash. Failure of a selected gzip object is terminal; K does not fall back to the canonical URL. Resume offsets refer to the compressed object.