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

Per-node periodic, liveness-aware garbage collector for per-VM host resources
that an unclean BEAM death can strand: a firecracker cgroup leaf and a
`hyper-rw-<id>` dm volume whose owning processes' `terminate/2` never ran and
whose vm_id never reboots (so `Hyper.Node.Reclaim`, which runs once at boot, and
the relaunch-time cleanup in the FireVMM path, never get a chance to clear it).

Liveness is the whole point. The reaper consults three independent sources of
truth for "this vm is alive" — the local VM supervisor's children, the cluster
routing table, and the node's live mutable layers (`Img.Mutable.active_vm_ids/0`)
— (`Plan.orphans/4` removes their union from the candidate set) and only ever
touches `hyper-rw-*` dm names, per-VM cgroup leaves, and (when networking is
enabled) per-VM netns names under `/var/run/netns` - never `hyper-thinpool`,
`hyper-img-*`, or a live VM's resources. A
candidate must also survive two consecutive ticks (`Plan.confirm/2`) before it
is reaped, so a VM caught mid-boot (resources present, not yet registered) is
given a grace tick rather than destroyed — this is what protects a netns that
`Hyper.Node.FireVMM.Daemon.prelaunch/1` just created for a VM that has not yet
appeared in `gather_live/0`: it takes two orphan sightings to reap, and a
normally-booting VM never produces a second one.

The decision logic lives in the pure `Hyper.Node.Reaper.Plan`; this module is a
thin I/O adapter that gathers the inputs, calls the plan, and executes the
best-effort, idempotent removals.

# `t`

```elixir
@type t() :: %Hyper.Node.Reaper{last_orphans: MapSet.t(String.t())}
```

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `start_link`

```elixir
@spec start_link(keyword()) :: GenServer.on_start()
```

---

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