Skip to main content

Health

Struct Health 

Source
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: bool

Whether 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: bool

False 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: bool

Whether 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: usize

How 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: u64

How 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 Debug for Health

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for Health

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl JsonSchema for Health

Source§

fn schema_name() -> Cow<'static, str>

The name of the generated JSON Schema. Read more
Source§

fn schema_id() -> Cow<'static, str>

Returns a string that uniquely identifies the schema produced by this type. Read more
Source§

fn json_schema(generator: &mut SchemaGenerator) -> Schema

Generates a JSON Schema for this type. Read more
Source§

fn inline_schema() -> bool

Whether JSON Schemas generated for this type should be included directly in parent schemas, rather than being re-used where possible using the $ref keyword. Read more
Source§

impl Serialize for Health

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.