# `Stevedore.Layer`
[🔗](https://github.com/oshlabs/stevedore/blob/v0.2.0/lib/stevedore/layer.ex#L1)

Read and merge image layers **in memory, without root**.

A layer is a tar (usually gzip- or zstd-compressed) describing a changeset relative to the
layer below. `merged_view/2` applies a stack of layers bottom-to-top into the effective
filesystem, honoring OCI whiteouts:

  * `.wh.<name>` removes `<name>` (and its subtree) from the lower layers
  * `.wh..wh..opq` makes its directory *opaque* — the lower layers' contents of that directory
    are hidden

This is the content-inspection surface a registry uses and the reading half a runtime's rootfs
materialization builds on. It never writes to disk.

Spec: [OCI image-spec, layer "Representing Changes"](https://github.com/opencontainers/image-spec/blob/main/layer.md#representing-changes).

# `fs_node`

```elixir
@type fs_node() :: %{
  path: String.t(),
  type: :regular | :directory | :symlink | :hardlink,
  mode: non_neg_integer(),
  size: non_neg_integer(),
  linkname: String.t() | nil,
  from_layer: non_neg_integer()
}
```

# `layer_source`

```elixir
@type layer_source() :: binary() | Stevedore.Descriptor.t()
```

# `diff`

```elixir
@spec diff(layer_source(), layer_source(), keyword()) ::
  {:ok, %{added: [String.t()], modified: [String.t()], removed: [String.t()]}}
  | {:error, term()}
```

Diffs two layers' file contents, returning the paths `:added`, `:modified`, and `:removed`
going from `a` to `b`. Whiteout entries are ignored (compare actual content).

# `entries`

```elixir
@spec entries(
  layer_source(),
  keyword()
) :: {:ok, [Stevedore.Archive.entry()]} | {:error, term()}
```

Reads a single layer's tar entries, decompressing as needed.

A `t:Stevedore.Descriptor.t/0` is decompressed by its media type and its bytes fetched from
`opts[:image]`; a binary is decompressed by `opts[:media_type]` or sniffed by magic bytes.

## Examples

    iex> tar = Stevedore.Archive.write!([%{name: "f", type: :regular, mode: 0o644, size: 1, linkname: nil, content: "x"}])
    iex> {:ok, [entry]} = Stevedore.Layer.entries(Stevedore.Archive.gzip(tar))
    iex> entry.name
    "f"

# `merged_entries`

```elixir
@spec merged_entries(
  Stevedore.Image.t() | [binary()],
  keyword()
) :: {:ok, [Stevedore.Archive.entry()]} | {:error, term()}
```

Returns the effective filesystem as a flat list of tar entries (with content), sorted by path —
the input for re-tarring a flattened image.

# `merged_view`

```elixir
@spec merged_view(
  Stevedore.Image.t() | [binary()],
  keyword()
) :: {:ok, %{optional(String.t()) =&gt; fs_node()}} | {:error, term()}
```

Computes the whiteout-aware effective filesystem of a layer stack as a map of path to
`t:fs_node/0` (metadata; use `Stevedore.Analyze.read_file/2` for bytes).

Accepts a `t:Stevedore.Image.t/0` or a list of layer binaries, ordered bottom-to-top.

---

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