pub struct Health {
pub version: Cow<'static, str>,
pub bootstrapped: bool,
pub disk: Option<DiskHealth>,
pub identity_degraded: bool,
pub identity_repaired: bool,
pub busy: bool,
pub execs: usize,
pub hooks: Vec<HookObservation>,
pub hooks_dropped: u64,
pub identity_steps: Vec<IdentityStep>,
pub image_env_keys: Option<usize>,
}Expand description
GET /v1/health response.
Fields§
§version: Cow<'static, str>§bootstrapped: bool§disk: Option<DiskHealth>Free space on the daemon’s working filesystem, and the reserve it is judged against.
Reported so disk pressure is something an orchestrator watches rather than
something it discovers from a failed write. anthropics/claude-code#59856
filled two 10 GB disks to 100% with never-collected session directories and
the first symptom was useradd: No space left on device — by which point
every writer in the sandbox was already broken. A number on a health endpoint
is what makes that curve visible while there is still time to act.
None when free space could not be measured, which is deliberately distinct
from zero: unmeasurable is not full, and a monitor that conflated them would
page on a missing statvfs.
identity_degraded: boolWhether any startup identity repair step failed. True means the VM is serving with a value from the shared image still in place — a duplicate machine-id or boot_id — which is a security-relevant condition an operator may want to drain the VM over, but is never a reason for the daemon to refuse to serve.
identity_repaired: boolFalse when identity repair was switched off by config. Distinguished from a repair that ran and found nothing so a monitor can tell “opted out” from “nothing to do”.
busy: boolWhether any exec is still running right now.
Here so that an orchestrator outside the VM can decide whether to keep the
VM alive, and the “outside” is the whole design. The platform measures
idleness by inbound traffic through the endpoint proxy, and that proxy
terminates outside the guest and forwards over loopback (measured;
docs/PLATFORM.md, “The platform’s own hook arrives over loopback”). A
request a guest process sends to the daemon’s own port never reaches the
proxy, so no amount of in-guest traffic can reset the idle timer. A route
that promised otherwise would be a keepalive that does not keep anything
alive, discovered when a multi-hour run auto-suspends mid-work.
What does reset it is a poll from outside, and this field is what makes such a poll informed rather than unconditional: the orchestrator polls, which is itself the inbound traffic, and reads whether the workload is busy to decide whether to keep polling. The assertion is therefore repeated and explicitly the caller’s, which is what the daemon self-keepaliving would not be — a hung process would then bill to the 8-hour ceiling with nobody asking.
Computed from the exec registry rather than remembered: true iff at least one registered exec has not yet published a result. An exec that exited and is waiting to be acked is not busy — its output is being held, not produced — so an orchestrator does not keep a VM alive for a command that finished.
#[serde(default)], unlike every field above it, and the asymmetry is not an
oversight. The daemon is baked into an image while the client is installed
separately, so a current client routinely talks to a daemon from whenever that
image was built — and a required field would make health() fail outright
against a daemon that predates it, turning a missing signal into an
unreachable VM. False is also the right absence: a daemon that cannot say
whether it is busy has not asserted that it is.
execs: usizeHow many execs are registered, in any phase.
Alongside busy because the two answer different questions and a monitor
wants both: busy: false, execs: 0 is a fresh or drained VM, while
busy: false, execs: 7 is a VM holding seven unacked results that somebody
still has to collect. Terminating the second loses output nobody read.
Defaulted for the same reason as busy: a client routinely talks to a daemon
baked into an older image, and zero is the honest reading of a daemon that
does not report a count.
hooks: Vec<HookObservation>Every lifecycle-hook invocation the daemon observed, oldest first.
The platform writes no CloudWatch logs for the validate hook, so “did my validate hook even run?” has no answer anywhere else. Each entry is the daemon’s own observation — the hook path that was posted and the daemon’s clock when it arrived — never anything the guest printed.
One trust caveat travels with this field and is stated rather than implied: the hook routes are unauthenticated and reachable over loopback from inside the guest, so a hostile workload can forge additional entries by posting the hook paths itself. It cannot remove or alter real ones — the daemon records before it responds and the log is append-capped, keeping the earliest entries — so the platform’s real firings are the front of the list and later spam is what the cap drops.
Defaulted for busy’s reason: an older daemon omits the field, and an empty
list is the honest reading of a daemon that reports no observations.
hooks_dropped: u64How many hook invocations were dropped once the log reached its cap.
Non-zero means the list above is the earliest invocations only — the cap keeps first-N so a guest spamming the unauthenticated hook routes cannot grow daemon memory or push the platform’s real firings out of the record.
Defaulted like hooks: an older daemon omits it, and zero is the honest
reading of a daemon that dropped nothing it could tell us about.
identity_steps: Vec<IdentityStep>Each identity-repair step and its outcome, in the order they ran.
Empty until repair runs, which is at the first successful run hook: repair at
daemon start would run in the image-build VM and be captured by the snapshot,
giving every VM the same “repaired” identity. identity_degraded says whether
any step failed; this says which one and why.
Defaulted for busy’s reason: an older daemon omits it.
image_env_keys: Option<usize>How many variables the daemon’s image-environment snapshot holds, or None when
it holds no snapshot.
The snapshot is the environment the daemon inherited as the container CMD, minus
every AGENTD_* variable, taken once at startup; a start request with
inherit_image_env starts its child from it. A count and never the values: this
route answers without the agent token, and an image’s ENV can hold anything its
author put there.
Defaulted for busy’s reason, and None is also what a daemon that predates
inherit_image_env reads as, which is how a caller tells whether the flag will be
honoured: such a daemon ignores the flag rather than refusing it.
Trait Implementations§
Source§impl<'de> Deserialize<'de> for Health
impl<'de> Deserialize<'de> for Health
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl JsonSchema for Health
impl JsonSchema for Health
Source§fn schema_id() -> Cow<'static, str>
fn schema_id() -> Cow<'static, str>
Source§fn json_schema(generator: &mut SchemaGenerator) -> Schema
fn json_schema(generator: &mut SchemaGenerator) -> Schema
Source§fn inline_schema() -> bool
fn inline_schema() -> bool
$ref keyword. Read more