Layers
Single-page view · as markdown
Layers
Peios / Using Peios / Layers
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.
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.)
"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: 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. 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.
What layers are for
Peios / Using Peios / Layers
Layers 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); 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.
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 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.)
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 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 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.
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.
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.
For the sandboxing case — per-thread private layers that only one caller sees — read Private hives and layers.
Deleting keys and values
Peios / Using Peios / Layers
Deletion is one more thing the layered model 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 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 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) 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. 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. 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.
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.