# Layers

---

# Layers

_Peios / Using Peios / Layers_

> A layer is a label on a write, not a position in a stack — most layers share one precedence, and the winner is chosen per value by recency.

So far the registry has been presented as if each value had a single stored entry: you write it, you read it back. That was the *effective view* — true as far as it goes, and the right way to learn everything up to here. This page replaces it with what is actually underneath, because the registry is a **layered** store, and the layering is both the reason the subsystem exists and the part most likely to be misunderstood.

The misunderstanding is worth naming up front. It is natural to picture layers as a clean stack — sheets of glass, one above another, the top sheet's value showing through. That picture is wrong in the case that matters most, and unlearning it is most of this page.

## Layers, in one sentence

**A layer is a label stamped on a write and a handle for removing a group of writes together — not a position in a stack. The registry orders individual writes, not layers, and it chooses a winner separately for every single value.**

Hold onto both halves. First: the thing that has a position is the *write*, not the layer. Second: resolution is *per value* — the contest is run again, from scratch, for every value name.

## Every write is kept

When you write a value, the registry does not overwrite anything in place. It records your write, tagged with the layer you wrote it in. If a different layer writes the same value, that write is stored *alongside* yours, with its own tag. So a single value name can hold several stored writes at once — one for each layer that has ever written it. (Within one layer there is only ever one: writing a value again in the same layer replaces that layer's entry. One layer, one opinion per value.)

A read, though, returns exactly one value — the **effective value**. Several stored writes, one answer: there is a contest, and the rest of this page is its rules.

## How the winner is chosen

Each stored write carries two ordering keys:

- **precedence** — a number it inherits from its layer. Higher wins.
- **recency** — its place in a single, global, monotonic write-order counter. Later wins.

For one value, the registry gathers every active write to it and picks the winner by those keys **in order**: highest precedence first; among writes that tie on precedence, the most recent. That is the whole rule.

```mermaid
flowchart TD
    W1["write · layer base · seq 300"] --> R{"tie on precedence — most recent wins"}
    W2["write · layer role-jellyfin · seq 50"] --> R
    R --> E["effective value = base (seq 300)"]
```

The subtlety is entirely in how those two keys play out in practice — because in practice, one of them almost never varies.

## Precedence is the exception; recency is the rule

Here is the fact that breaks the stack picture: **almost every layer has the same precedence.** The base layer is precedence 0. Role layers are precedence 0. Only a few things — chiefly domain policy — ever sit higher. So for the overwhelming majority of contests, precedence is a tie, and the winner is decided purely by **recency, value by value.**

That has a consequence the stack picture cannot express. Two layers at the same precedence have *no fixed pecking order between them*. For one value, layer A might win because it wrote that value most recently; for the value right beside it, layer B wins because it wrote *that* one last. Same two layers, same precedence, different winners — at the same instant.

A worked example. An administrator has made some manual edits (which land in the base layer) and has installed a role (`role-jellyfin`), both at precedence 0:

| Value | base wrote (seq) | role-jellyfin wrote (seq) | Effective value | Why |
|---|---|---|---|---|
| `MaxEventSize` | 300 | 50 | **base** | tie on precedence → most recent write wins |
| `BufferCapacity` | — | 250 | **role-jellyfin** | base never wrote it |
| `MaxNestingDepth` | 100 | 280 | **role-jellyfin** | role wrote it more recently |

There is no "top layer" here to point at. `base` owns one value, `role-jellyfin` owns two, and which is which is decided one value at a time by who wrote last. This is what "interwoven, not stacked" means, and it is the normal case, not an edge case.

So the stack-of-glass-sheets picture is only ever right when precedences genuinely differ — which is the minority. Drop it as your default image.

## A picture that fits

If you want one image to keep, use a **shared document where, for each cell, the last edit wins** — many editors, no locking, the most recent edit showing. That is the same-precedence case exactly: per-cell (per-value) last-write-wins, with no editor inherently above another. Precedence, when it appears, is an **administrator who can lock a cell**: once locked, that cell holds the locked value no matter who edits afterwards. Every metaphor leaks, but this one leaks in the right places — it foregrounds that resolution is per-cell and recency-driven, and casts precedence as the rare override it actually is.

## When precedence really does differ

When two writes have *different* precedence, recency stops mattering between them: **higher precedence always wins, even over a more recent lower-precedence write.** A value set by a precedence-1 layer long ago still beats one written to a precedence-0 layer a moment ago.

This is precisely what makes precedence worth having. Recency is fine for cooperating local configuration, but it cannot express "this setting must win even if someone writes it again later" — and that is exactly what policy needs. A domain **Group Policy** is delivered as a higher-precedence layer for this reason: a local administrator cannot defeat it by re-writing the value, because their write lands at precedence 0 and loses to the higher tier regardless of how recent it is. Precedence is the mechanism behind "you cannot override this locally". (Creating a layer that outranks others is itself a privileged action, so the tiering cannot be forged from below — more on that in [Access control](/peios/using-peios/registry-security/access-control.md).)

## "Most recent" is write order, not the clock

One precision worth stating, because it is what makes the model trustworthy: recency is a **monotonic write-order counter**, not a wall-clock timestamp. Every write is handed the next number from a single counter that only ever increases. The registry does record a wall-clock "last write time" on each key, but only as human-facing metadata — it is never used to resolve a contest. So "most recent" means "later in the actual order of writes", which cannot be moved backwards or spoofed by a clock change.

## It is not only values

Everything above is framed around values, but the **same contest decides whether a key exists at a path.** Each layer can make its own claim about a name — "a key lives here" — and the winner is chosen by the same precedence-then-recency rule. A layer can even claim that *nothing* lives there, masking a key another layer provides. That is how a layer adds, replaces, or hides a key, and it resolves exactly as a value does. The markers that express absence — tombstones for values, hidden entries for keys — are the subject of the next page.

## Where to go next

If you want the payoff — why all of this exists — read [What layers are for](/peios/using-peios/registry-layers/what-layers-are-for.md): the base layer, tombstones, the automatic-revert property, and how roles and Group Policy are built on it.

If you want the one thing layers do *not* revert — security — read [Access control on keys](/peios/using-peios/registry-security/access-control.md). A security change made while a layer existed is *not* undone when the layer is removed, and the reason is worth understanding.

If you want to know how a watcher sees a layer change — it observes effective-state changes, with the layering made invisible — read [Watching for changes](/peios/using-peios/registry-concepts/watches.md).

---

# What layers are for

_Peios / Using Peios / Layers_

> Layering makes configuration removable — delete a layer and its writes vanish, the next-best write resurfaces, and nothing is left behind.

[Layers](/peios/using-peios/registry-layers/layers.md) explained how the registry resolves competing writes. This page is the reason it bothers. The layered model buys one thing above all: **configuration you can add and remove as a unit, with revert that is automatic and leaves nothing behind.**

## The payoff, in one sentence

**Because every write is tagged with a layer and nothing is overwritten in place, deleting a layer makes its writes disappear and the next-best write for each value resurface on its own — so installing a bundle of configuration and removing it again are just creating and deleting a layer.**

Everything below is a consequence of that.

## The base layer

Most of the time you are not thinking about layers at all, and the **base layer** is why. It is the default layer — precedence 0 — and it is where writes go when you do not ask for anything else. Manual administrative edits land here; so do the system's own defaults. It always exists, and it cannot be deleted or disabled; it is the floor the rest of the model stands on.

When earlier pages showed you "write a value, read it back", that was the base layer doing its job. You can work with the registry for a long time and only ever touch base.

## Saying "no value", not just "another value"

A plain write competes to *be* the value. But configuration sometimes needs to say something a plain write cannot: *this value must not be set at all.* Overriding `MaxEventSize` with a different number is easy; asserting that `MaxEventSize` should be absent is a different statement.

A layer makes it with a **tombstone** — a write whose meaning is "no value here". It enters the same per-value contest as any other write ([Layers](/peios/using-peios/registry-layers/layers.md)); if it wins, a read of that value returns "not found" rather than falling through to some other layer's write. Like any write, it belongs to a layer — so when that layer is removed, the tombstone goes with it and whatever it was suppressing comes back.

A **blanket tombstone** is the same idea applied to a whole key at once: a marker that enters the contest as a "no value" candidate for *every* value name on the key. A layer can set a blanket tombstone and then write the specific values it does want — the effect is "clear everything here, then set these". It is how a layer declares the complete contents of a key instead of merging into whatever was already there.

The key-level equivalent is **hiding**: a layer can claim that no key exists at a path, masking one that an older or lower-precedence write provides. Remove the layer and the key reappears.

In every case the marker is just another write — owned by a layer, removed with the layer.

## Automatic revert

This is the property the whole design exists to deliver. Because layers are removable and nothing is ever destroyed in place, **deleting a layer cleanly undoes everything it did** — with no undo script, no bookkeeping, no leftover state:

- Its writes vanish from every contest they were in.
- Its tombstones and hidden-key markers lift.
- For each affected value or key, the registry simply re-runs the contest without the deleted layer's writes, and the next-best survivor becomes effective.

There is no separate "revert" operation. Revert is what *naturally happens* when a layer's writes stop existing, because the effective value was always just the winner of a live contest.

```mermaid
flowchart LR
    A["role-jellyfin present: MaxNestingDepth = role's value"] -->|delete the layer| B["role's write gone: MaxNestingDepth reverts to the base value underneath"]
```

There is, in particular, no **tattooing** — the failure mode where removing configuration leaves its changes burned in. In a system that overwrites in place, uninstalling something means trusting that its installer recorded the previous values and restores them correctly. Here there is nothing to restore: the previous value never left.

## Roles

A **role** is a bundle of configuration deployed as a layer. Installing a role creates a layer and writes the role's keys and values into it (atomically, as one transaction, so the role never appears half-installed). Uninstalling a role deletes the layer — and by automatic revert, every value it set and every key it added simply falls away, and whatever the system looked like before resurfaces on its own. Clean uninstall is not something a role's authors have to implement carefully; it is a property of the layer.

## Group Policy

Domain **Group Policy** is configuration delivered from outside the machine, and it rides on layers too — but at a *higher precedence* than local layers. That choice is deliberate, and [Layers](/peios/using-peios/registry-layers/layers.md) explained why: a higher-precedence write wins regardless of recency, so a domain setting beats local configuration even if a local administrator writes the value again afterwards. Applying a policy adds the layer; lifting it deletes the layer, and the local configuration it had been overriding resurfaces automatically — the same revert, one tier up. (High-precedence layers are privileged to create, so policy cannot be forged by an unprivileged process; see [Access control](/peios/using-peios/registry-security/access-control.md).)

## What layers are not

- **Not a transaction.** Layers decide *which* write wins and let you remove a group of writes together; atomicity — making several writes commit all-or-nothing — is a separate mechanism. A role install uses both: a transaction to apply the writes atomically, a layer to make them removable.
- **Not access control.** A layer does not decide who may read or change a value; the [security descriptor](/peios/using-peios/registry-security/access-control.md) on each key does. And the two do not mix: a security change made while a layer existed is **not** reverted when the layer is deleted. Security is operational state, not configuration overlay — [Access control on keys](/peios/using-peios/registry-security/access-control.md) is where that distinction lives.
- **Not a browsable history.** Layers are not a version-control timeline you can scroll through. You see the effective view — the current winners — not a log of every write that ever competed.

## Where to go next

If you want what "deleting a key" really does once names are layered — and why there is no recursive delete — read [Deleting keys and values](/peios/using-peios/registry-layers/deleting-keys-and-values.md).

If you want the security model and its sharp interaction with layers — why deleting a layer reverts its values but *not* a security change made under it — read [Access control on keys](/peios/using-peios/registry-security/access-control.md).

If you want to see how a watcher experiences a layer being added or removed — it sees the effective values change, with the layer machinery invisible — read [Watching for changes](/peios/using-peios/registry-concepts/watches.md).

For the sandboxing case — per-thread *private* layers that only one caller sees — read [Private hives and layers](/peios/using-peios/registry-advanced/private-hives-and-layers.md).

---

# Deleting keys and values

_Peios / Using Peios / Layers_

> Deleting a key withdraws one layer's claim to a name — the key stays if another layer still claims it, and there is no recursive delete.

Deletion is one more thing the [layered model](/peios/using-peios/registry-layers/layers.md) quietly reshapes. "Delete this key" sounds absolute, but in a store where a name is the winner of a per-layer contest, removing a key really means withdrawing *one layer's claim* to that name. Several behaviours follow that are worth knowing before you delete anything.

## Deletion, in one sentence

**Deleting a key withdraws one layer's claim to a name; the key disappears only if no layer still claims it — and even then, anything holding it open keeps working until it lets go.**

## Deleting withdraws a claim

When you delete a key, you remove its name in a particular layer — the [base layer](/peios/using-peios/registry-layers/what-layers-are-for.md) unless you say otherwise. Nothing about the key is special-cased; its claim in that layer is simply withdrawn, and it drops out of the [contest](/peios/using-peios/registry-layers/layers.md) for that name.

If another layer still names the key, it stays visible through that layer. So deleting a key that a role also provides removes only *your* claim — the role's key remains until the role does. To remove a key everywhere, every layer's claim has to go. For anything delivered by a role or a policy, that means removing the *layer* (which [reverts cleanly](/peios/using-peios/registry-layers/what-layers-are-for.md)) rather than deleting the key — deleting it in the base layer would not touch the role's claim anyway.

(The hive roots themselves — `Machine\`, `Users\<SID>\` — cannot be deleted or hidden. They are the anchors the namespace hangs from.)

## There is no recursive delete

You cannot delete a key that still has visible child keys; the deletion is refused. There is no "delete this whole subtree" primitive. Removing a populated subtree is a deliberate walk from the leaves upward, performed by whatever tool you are using — not a single sweep in the kernel.

This is a safety property, not a limitation to work around. A mistaken delete cannot take a populated subtree down with it; you have to mean it, key by key.

## Deleting out from under an open handle

A key can be deleted while a process still has it open, and the registry handles that the same way Linux handles deleting an open file (the *unlink* model). The open handle keeps working — reads, writes, and watches all continue against the now-unnamed key — and the key is only truly discarded once the last handle closes. Meanwhile, new attempts to open it *by path* fail at once: the name is gone, even though the object lingers for whoever still holds it.

So "deleted" means "no longer reachable by name", not "destroyed this instant". A service that had the key open does not break mid-operation; it holds the last reference until it closes, and only then does the key go away.

## Deleting values

A value works the same way one level down. Deleting a value withdraws a layer's write for that value name. If a lower-precedence or older layer also wrote that value, its write resurfaces as the new effective value — the ordinary [revert](/peios/using-peios/registry-layers/what-layers-are-for.md). Remove a value's only write and the value simply becomes absent.

## Permission

Deleting (or hiding) a key requires delete permission on it — see [Access control on keys](/peios/using-peios/registry-security/access-control.md). As everywhere in the registry, the check is against the key you are deleting, decided by its security descriptor, with no check on the keys above it.

## Where to go next

For the contest that "withdrawing a claim" feeds back into, read [Layers](/peios/using-peios/registry-layers/layers.md).

For removing configuration in bulk — deleting a layer, which is what you do instead of deleting a role's or policy's keys — read [What layers are for](/peios/using-peios/registry-layers/what-layers-are-for.md).
