# `Hyper.Node.Budget.NodeState`
[🔗](https://github.com/harmont-dev/hyper/blob/main/lib/hyper/node/budget/node_state.ex#L1)

The per-node resource snapshot published into `Hyper.Cluster.Budget` and read by
`Hyper.Cluster.Scheduler` as the first pass of placement.

Approximate by design: hard headroom is exact at publish time but soft load is
an EWMA that drifts and the record gossips with lag. The authoritative decision
is always the owning node's `Hyper.Node.Budget.admit/2`. Each record carries the
node's load *and* its ceilings, so a scheduler anywhere can evaluate fit without
knowing the target's config or core count.

# `t`

```elixir
@type t() :: %Hyper.Node.Budget.NodeState{
  cpu_capacity: pos_integer(),
  cpu_load: float(),
  cpu_max_load: float(),
  disk_bw_ceiling: Unit.Bandwidth.t(),
  disk_bw_load: Unit.Bandwidth.t(),
  disk_free: Unit.Information.t(),
  layers: [Hyper.Layer.id()],
  mem_free: Unit.Information.t(),
  net_bw_ceiling: Unit.Bandwidth.t(),
  net_bw_load: Unit.Bandwidth.t(),
  node: node()
}
```

# `build`

```elixir
@spec build() :: t()
```

Snapshot this node's current budget state.

# `fits?`

```elixir
@spec fits?(t(), Hyper.Vm.Instance.Spec.t()) :: boolean()
```

Whether this snapshot's node can hold `spec`: hard memory/disk headroom plus the
soft cpu/disk-bw/net-bw load ceilings. A pure predicate over the published
snapshot; the authoritative check is still the owning node's
`Hyper.Node.Budget.admit/2`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
