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

The node's single dm-thin pool. On start it materialises two sparse backing
files in `scratch_dir` (metadata + data), attaches them as writable loop
devices, zeroes the metadata (so the kernel treats it as a fresh pool), and
creates the `hyper-thinpool` device-mapper pool.

`create_external/3` provisions a thin volume whose unwritten blocks read
through to a read-only external origin (the composed image device) and whose
writes land in the pool - the per-VM writable rootfs. Thin device ids are a
bump pointer plus a freed-id stack, mirroring `Hyper.Node.Users`.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `create_external`

```elixir
@spec create_external(String.t(), Path.t(), pos_integer()) ::
  {:ok, %{dev: Path.t(), id: non_neg_integer()}} | {:error, term()}
```

Provision a thin volume of `sectors` over read-only `origin_dev`.

# `destroy`

```elixir
@spec destroy(String.t(), non_neg_integer()) :: :ok
```

Remove thin volume `name` and free its thin device `id`.

# `mappings`

```elixir
@spec mappings(non_neg_integer()) ::
  {:ok, %{block_sectors: pos_integer(), ranges: [[non_neg_integer()]]}}
  | {:error, term()}
```

The provisioned ranges of thin device `thin_id`, from the pool's own
metadata. Because the volume reads through an external origin, provisioned
blocks are exactly the blocks ever written — the divergence a fork publish
must copy, discovered without scanning the device.

Reserves the pool's metadata snapshot for the duration of the dump and
always releases it (one reserve may exist at a time; this call is
serialized by the pool server).

# `snapshot`

```elixir
@spec snapshot(String.t(), String.t(), non_neg_integer(), pos_integer(), Path.t()) ::
  {:ok, %{dev: Path.t(), id: non_neg_integer()}} | {:error, term()}
```

Take an internal snapshot of live thin volume `origin_name` (thin id
`origin_id`) and activate it as `name`, sharing the pool's blocks COW-style.

dm-thin requires the origin suspended while `create_snap` is sent; the origin
is resumed on *every* path out, so a failed snapshot never leaves the parent
VM's I/O frozen. The snapshot is activated with the same external origin
(`origin_dev`) as its parent — unprovisioned reads must keep resolving
through the composed RO image chain.

# `start_link`

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

---

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