Peios Learn
Products
PePeios pkpekit PvProvium UDUniversal Directory TrTrail PrProject WiWispist
Using Peios Security Basics Technical Documentation Source
Using Peios Security Basics Technical Documentation Source
Peios

Concepts

Single-page view · as markdown

The registry

Peios / Using Peios / Concepts

The registry is the structured configuration store for a Peios system. It is a hierarchy of named keys, each holding values — typed pieces of data. Subsystems read their operational settings from it, services keep their definitions in it, and policy is delivered through it. Where a file holds an opaque stream of bytes, a registry value holds a small, named, typed datum that some part of the system knows how to act on.

It is also kernel-mediated. There is no file under /etc you edit and no daemon socket you talk to directly. Every registry operation — opening a key, reading a value, writing configuration, watching for change — goes through a dedicated set of system calls. Userspace never touches the store underneath; the kernel is always in the path, which is what lets the registry carry the same access control, the same identity model, and the same auditing as every other protected object in Peios.

This page sketches the shape, names the handful of ideas that make the registry its own thing, and points at the pages that cover each one.

The registry in one sentence #

The registry is a tree of keys and typed values, reached only through kernel system calls, where every key is an access-controlled object — and every value you read is the effective result of resolving a stack of layered writes.

Ignore that last clause for now. For almost everything in this section you can picture the registry as a plain hierarchical store: one key per path, one value per name, the value you wrote is the value you read. That simpler picture is correct as far as it goes, and it is the right way to learn the model. The layering underneath — what "effective" really means — is the deepest idea here, and it gets its own page once the rest is solid.

The shape #

Three entities make up the namespace.

EntityWhat it is
HiveA top-level namespace — the first component of every path. Machine\ holds system-wide configuration; Users\<SID>\ holds per-user configuration. In practice a running system exposes just a few.
KeyA node in the tree. Keys are containers: they hold child keys (forming the hierarchy) and values (holding data). Every key is a secured object with its own security descriptor. Keys are roughly the registry's directories.
ValueA named, typed datum living inside a key — a REG_DWORD number, a REG_SZ string, and so on. A key can hold many values, each with a distinct name. Values are roughly the registry's files.
flowchart TD
    M["Machine\ (hive)"] --> S["System"]
    S --> K["KMES (key)"]
    K --> V1["BufferCapacity = 4194304  (REG_QWORD)"]
    K --> V2["MaxEventSize = 65536  (REG_DWORD)"]
    M --> R["Registry"]
    U["Users\&lt;SID&gt;\ (hive)"] --> US["per-user keys..."]

Paths are written with backslashes — Machine\System\KMES — and compared case-insensitively while preserving the case you wrote. A handful of well-known roots anchor everything: Machine\ for the machine, Users\<SID>\ per principal, and CurrentUser\ as a convenience alias the kernel rewrites to the caller's own Users\<SID>\.

We will return to one subtree throughout this section as a running example: the KMES event subsystem reads its tuning parameters from values under Machine\System\KMES. It is small, real, and exercises every idea — types, meaning, security, and change notification.

What the registry is not #

The registry borrows a familiar shape, and the familiarity is a trap. Three wrong mental models attach themselves immediately; clearing them is most of understanding what the registry actually is.

It is not a filesystem. It looks like one — hierarchical paths, separators, containers and leaves — but it behaves differently in ways that matter. You reach it through dedicated registry system calls, not the file API. Values are typed data, not byte streams. Access is checked once, against the single key you open, with no traversal check on the keys above it — a process can read Machine\System\Services\Jellyfin without any access to Machine\System\Services. And security is attached per key; there is no such thing as a per-value permission.

It is not a dumping ground of opaque settings. Every key is a first-class secured object and every value is typed, access-controlled, and watchable. Nothing in the registry is unmanaged or hidden — there is no "miscellaneous junk" tier. It is a deliberate, governed surface, not a place things accumulate.

It is not self-describing. The registry stores a value's type tag but never interprets its bytes. It does not know that BufferCapacity must be a power of two, what its default is, or which subsystem reads it. That knowledge — the meaning of a value — lives entirely outside the registry: in the subsystem that owns the value, and, for a human looking it up, in regman, the registry's manual. regman is the man of the registry — give it a path and it tells you a value's type, default, valid range, when a change takes effect, and what the setting is for. The registry cannot describe itself, so its manual is shipped beside it. This separation of storage from meaning is the first idea worth slowing down for, and it has consequences — a write the registry accepts can still be refused by the subsystem that reads it — so it gets its own page.

It is not flat. Underneath the single value you read, the registry keeps a stack of writes, each tagged with a layer, and resolves the winner on every read. That is what makes configuration revert cleanly, role install and uninstall work, and domain Group Policy apply and lift without residue. It is powerful and it is the one idea worth deferring — the Layers page pulls the curtain back once the rest of the model is clear.

One registry, built from two parts #

When precision matters, "the registry" is really two cooperating components: a kernel subsystem that owns the data model, path resolution, access control, change notification, and layer resolution; and a userspace store that persists the data on disk. They talk over a private protocol, and the kernel is the only authority — the store never sees who is asking and never makes a security decision.

For the conceptual model you do not need that split, and these pages mostly treat the registry as one thing. The division becomes useful only when you care about how the registry is backed, swapped, or recovered — so it waits for LCS and sources, under Administration.

Where to start #

If you want the data model — hives, keys, the value types, case rules, and why a value is "typed but opaque" — read Keys, values, and types.

If you want the idea that the registry stores values it does not understand — what reject-or-keep means, and why the value the registry shows and the value a subsystem is actually using can legitimately differ — read Configuration, not storage.

If you are ready for the layered truth under the effective view — precedence, the base layer, how deleting a layer automatically reverts its changes, and how roles and Group Policy ride on it — read Layers.

If you want the security model — why every key carries a security descriptor, the registry-specific access rights, and the rule that security changes are not undone by layer removal — read Access control on keys.

If you want to know how a service reacts to a configuration change instead of polling for it, read Watching for changes.

For the two command-line tools, read regman — the registry's manual, which tells you what a key means — and reg — the scripting interface that reads and writes what a key is. You consult the first to decide what to set, and use the second to set it.

Keys, values, and types

Peios / Using Peios / Concepts

The registry's data model has exactly two kinds of thing in it: keys and values. A key is a node in the tree — a container that holds child keys and values. A value is a leaf — a named, typed piece of data living inside a key. There is no third kind of thing, and the nesting only ever happens through keys. That smallness is deliberate, and it is worth getting precise about before anything else, because every later idea is built on this shape.

Two levels, and only two #

Keys nest; values do not. A key can contain other keys (its subkeys) and it can contain values, but a value cannot contain anything — it is always a leaf. You cannot put a value "inside" another value, and there is no record type between a key and a value.

HoldsHeld byRole
KeySubkeys and valuesIts parent keyThe container — the registry's directories
ValueNothing (a leaf)Exactly one keyThe data — the registry's files

This two-level rule is a hard constraint, not a convention. When you want structure, you make subkeys; when you want data, you make values. There is no other axis.

Keys #

A key is identified by its path — Machine\System\KMES names a key three levels down from the Machine\ hive root. Each key holds its children and its values, and each key is a first-class secured object with its own security descriptor; that is what makes "who can read or change this configuration" a per-key decision.

A few naming rules apply to each component of a path (each segment between separators):

  • Any UTF-8 is allowed in a key name except backslash, forward slash, and the null byte. Backslash is the separator; forward slash is accepted on input and normalised to a backslash; null is never allowed.
  • Components cannot be empty. Machine\\System (a doubled separator) and a trailing separator are both invalid.

Every key also has one default value — the single value whose name is the empty string. It is the key's "main" value, the one you get when you read the key without naming a value. Most keys also carry additional named values alongside it.

Values #

A value is a (name, type, data) triple stored in a key. A key can hold many values, each with a distinct name, plus the one unnamed default value.

Value names follow almost the same rules as key names, with one difference: backslash and forward slash are allowed in a value name. Value names are not paths — they are not hierarchical — so a separator inside one has no special meaning. Only the null byte is forbidden, and the empty string is reserved for the default value.

The value types #

A value's type is a small tag stored alongside its data. The full set:

TypeHolds
REG_DWORD / REG_QWORDA 32-bit / 64-bit integer.
REG_DWORD_BIG_ENDIANA 32-bit integer in big-endian byte order.
REG_SZA string.
REG_EXPAND_SZA string containing references to be expanded (e.g. environment variables) by whatever reads it.
REG_MULTI_SZAn array of strings.
REG_BINARYRaw bytes with no further structure.
REG_LINKA symbolic-link target. The one type the registry acts on itself — see Registry links.
REG_NONENo type / no meaningful data.

There are also three hardware-resource types carried over for format fidelity. Peios assigns them no meaning — they behave exactly like REG_BINARY and the registry never produces them itself. You will essentially never author one.

Typed, but opaque #

Here is the property the rest of the topic leans on. The registry stores a value's type tag and its raw bytes, and that is all it does with them. It does not check that the bytes match the type. It does not parse a REG_DWORD into a number. It does not know that Machine\System\KMES\BufferCapacity is supposed to be a power of two, what its default is, or which subsystem reads it. The single exception is REG_LINK on a link key, which the kernel follows during path resolution.

So "typed" here means tagged, not validated. The type travels with the value so that a reader knows how to interpret the bytes — but the interpreting, the validating, and the deciding-what-to-do are all somebody else's job.

Two consequences follow immediately, and both get their own treatment:

  • The registry can hold a value for a subsystem that is not even running, or a setting nobody has read yet. Storage does not require a reader.
  • Whether a value is valid is never the registry's verdict. That belongs to whatever reads it — which is the whole subject of Configuration, not storage.

Names, case, and paths #

Paths use the backslash as their canonical separator. A forward slash is accepted on input and normalised to a backslash, so Machine/System/KMES and Machine\System\KMES name the same key; the stored, canonical form always uses backslashes.

Comparison is case-insensitive but case-preserving. KMES and kmes resolve to the same key, but the registry keeps whatever case you wrote for display. The matching uses a fixed Unicode case-folding rule, so it does not depend on locale.

One sharp edge: there is no Unicode normalisation. Two different byte sequences that render as the same character (a precomposed é versus e plus a combining accent) are two different keys. Case is folded; representation is not.

Volatile keys #

A key can be created volatile, meaning it is stored only in memory and disappears on reboot or when its store unloads. It is the registry's home for runtime-only state that should never survive a restart. Volatility is fixed at creation, and there is one structural rule: the children of a volatile key must themselves be volatile (you cannot place a persistent key under a non-persistent one).

Watch the word, because it collides with a different idea. "Volatile" describes storage persistence — does this key survive a reboot. It says nothing about how quickly a configuration change takes effect — whether editing a setting applies live, on service restart, or only on reboot. That second property belongs to each individual setting and is recorded in regman as its applies field. A value can live in a perfectly persistent (non-volatile) key and still only take effect on reboot. Keep the two apart.

Where to go next #

If you want the idea that the registry stores values it does not understand — what regman documents, what "reject-or-keep" means, and why the value the registry shows can differ from the value a subsystem is using — read Configuration, not storage.

If you want the layered truth beneath the single value you read — precedence, the base layer, and automatic revert — read Layers.

If you want the security model — why every key carries a security descriptor and what the registry-specific access rights are — read Access control on keys.

Configuration, not storage

Peios / Using Peios / Concepts

A registry value is a type tag and a pile of bytes. The registry stores it, returns it on request, and protects it with a security descriptor — but it never asks what the value means. As Keys, values, and types put it, the registry is typed but opaque: it knows the tag, not the meaning. This page is about what follows from that, because it is the single most counterintuitive thing about the registry and the thing that most often trips people up.

Configuration and meaning, in one sentence #

The registry holds configuration; it does not understand it. Meaning lives in the subsystem that reads the value and in regman, the manual that documents it — never in the registry itself.

Every config system you have used before bundles storage and meaning together: the file holds the setting and the program that parses the file knows what the setting means, in one place. The registry splits them on purpose. Storage is the registry's job. Meaning lives in two other places.

Where meaning lives #

In the subsystem that owns the value. The code that reads Machine\System\KMES\BufferCapacity is the thing that knows it must be a power of two, knows the compiled-in default, and knows what to do when it changes. That knowledge is in KMES, not in the store. Ask the registry "is this a sensible buffer capacity?" and it has no answer — it was never told what the value is for.

In regman, for a human. regman is the registry's manual — the man of the registry. Give it a path and it tells you what a key or value actually does:

$ regman Machine\System\KMES BufferCapacity

  Type     REG_QWORD
  Default  4194304  (4 MB)
  Valid    65536–268435456 bytes (64 KB–256 MB), power of two
  Applies  live — ring-buffer swap

Per-CPU ring buffer capacity, in bytes. Must be a power of two; values
that are not are treated as invalid and ignored.

regman is shipped documentation that lives beside the registry, not inside it — because the registry cannot describe itself, the manual has to sit next to it. This is the backbone of configuring a Peios system: before you touch a knob, regman is how you find out what it is, what values are legal, and what changing it will cost you (the Applies line — live, on restart, or on reboot). regman reads its own shipped docs; it does not read the live registry, so it tells you what a setting is, not what it is currently set to.

Reject-or-keep #

Because the store never validates, validation happens where the value is read. This produces the rule that surprises people:

A write the registry accepts is just stored bytes. The subsystem that owns the value validates it on read — and a value it judges invalid is ignored, not applied. The subsystem keeps its last known-good value. It never clamps the bad value to the nearest legal one, and never silently corrects it.

flowchart LR
    A["Admin writes a value"] --> B["Registry stores the bytes (always succeeds)"]
    B --> C["Owning subsystem reads it"]
    C --> D{"Valid?"}
    D -->|yes| E["Apply the new value"]
    D -->|no| F["Keep last known-good value + log the rejection"]

KMES is the worked example. Write a BufferCapacity that is not a power of two and the write succeeds — the registry has no opinion about powers of two. When KMES reads it, it rejects the value, keeps the capacity it was already using, and emits an event naming the key, the value it rejected, and the value it is still running on.

Written versus used #

That leaves two sources of truth that can legitimately disagree:

  • The registry shows what was written. Read the key back and you see the value the admin set — including a rejected one, sitting there looking authoritative.
  • The log shows what is actually in use. When a subsystem rejects a value and keeps its previous one, it says so in the event log. That record, not the registry read, is the truth about what the system is running on.

So "what is this subsystem actually configured to right now?" is not always answered by reading the registry. If a change does not seem to have taken effect, the registry will happily show you the value you wrote; the audit and event log is where you find out it was refused and why. This is why observability matters here: the registry is the intent, the log is the reality, and they are allowed to differ.

The registry does this to itself #

The cleanest demonstration is the registry subsystem configuring itself. It reads its own tuning parameters from Machine\System\Registry\ and applies exactly this discipline to them: a valid value is hot-swapped into effect; an invalid one (out of range, wrong type) is ignored, the previous known-good value is retained, and an audit event is emitted naming the key, the rejected value, and the value still in force. Values are never clamped. The subsystem that owns the entire registry treats its own configuration as "stored bytes I must validate before I trust" — see How the registry boots and configures itself.

There is no invalid registry #

Put plainly: a value is never "invalid" at the registry level, because the store has no standard to judge it against. Validity is a verdict, and the reader makes it. The same bytes could be valid to one subsystem and meaningless to another; the registry holds them either way.

So "is this configuration valid?" is not a question you ask the registry. You ask the subsystem that owns the value — or you read the log to see what verdict it already reached.

Why it is built this way #

Briefly, because it is worth knowing the trade rather than dwelling on it: keeping the store meaning-free keeps it small and uniform, lets it hold a value for a subsystem that is not running yet or a key nobody has read, and lets configuration be delivered from outside — a domain Group Policy, say — without the kernel needing a built-in schema for every subsystem on the machine. The price is that the store cannot tell you whether a value is sensible. That price is paid by regman (which documents what should be there) and the logs (which record what the system actually did).

Where to go next #

If you want to see how a subsystem notices a configuration change so it can re-validate and re-apply, read Watching for changes.

If you want the registry's own bootstrap and self-configuration story — the purest example of reject-or-keep — read How the registry boots and configures itself.

If you are ready for the layered model beneath the single value you read, read Layers.

Watching for changes

Peios / Using Peios / Concepts

Configuration changes while the system is running — an administrator edits a value, a role is installed, a policy is applied. A subsystem could poll the registry to notice, but Peios does not make it: the registry can tell a process when something it cares about changes. This is what lets services react to configuration instead of repeatedly re-reading it, and it is how the registry behaves as a live configuration backbone rather than a passive store.

Watching, in one sentence #

A process arms a persistent watch on a key — optionally covering its whole subtree — and is notified whenever the effective state there changes, so it can react to configuration instead of polling for it.

Persistent, not single-shot #

A watch is armed on an open key handle. Once armed, it stays armed until the handle is closed: events keep flowing, with no need to re-arm after each one. This matters because re-arming would open a race — a change slipping through in the gap between one notification and the next registration. There is no such gap here. The handle is also pollable: a process waits on it the same way it waits on any other file descriptor, and reads structured change records off it when they arrive.

A watch can cover just the one key, or the key and its descendants (a subtree watch), so a service can watch an entire configuration area with a single registration.

What a watch reports #

Events describe what changed, by kind:

EventMeaning
Value setThe effective value at a name changed or appeared.
Value deletedThe effective value at a name went away.
Subkey created / deletedA child key became visible / became invisible.
SD changedThe watched key's security descriptor was modified.
Key deletedThe watched key itself is no longer reachable by path.
OverflowEvents were dropped — re-read to recover (below).

Watchers see effective state, not layers #

Here is the important tie-back to Layers. A watcher is told about changes to the effective value — the winner of the contest — and nothing about the machinery underneath. Delete a layer and cause a value to revert, and the watcher sees a plain "value set" event for its new effective value. Apply a higher-precedence policy that overrides a local setting, and the watcher sees the value change. It never sees "a layer was added" or "a write lost a contest" — only that the answer it would get from a read is now different.

This is exactly right: the layering is an implementation of how the effective value is chosen, and a watcher only cares that it changed. The curtain stays down; the watcher watches the front of it.

Committed state only #

Watch events fire when a change commits, never mid-flight. A transaction that writes many values produces its events as one batch at commit time; a watcher never observes a half-applied transaction, and an aborted one produces no events at all. What a watcher sees is always a state the system actually reached.

Best-effort: be ready to re-read #

A watch is a change notification, not a guaranteed-complete journal. Events are queued for the watcher, and the queue is bounded. If a watcher falls behind — events arriving faster than it reads them — the queue overflows: the oldest events are dropped and a single overflow marker is delivered in their place. The contract on overflow is simple and firm: re-read the watched key (and subtree) to recover the current state, then carry on with subsequent events.

So a correct watcher is written to do two things — apply individual events when it is keeping up, and fall back to a full re-read when it is told it fell behind. It never assumes the event stream is a complete edit history; it treats it as "something changed, here is a hint, and if the hint says you missed some, go look." The same overflow-and-re-read recovery covers the bigger disruptions too — a large layer operation, or the backing store restarting — where computing an exact per-change list is not worthwhile.

The reaction loop #

Put watches together with reject-or-keep and you get the pattern that runs throughout Peios. A subsystem watches its own configuration subtree; when a value changes, the watch fires; the subsystem re-reads, re-validates, and either applies the new value or keeps its last known-good one and logs the rejection. KMES does this for its tuning; peinit does it for service definitions; the registry itself does it for its own parameters (see How the registry boots and configures itself). Configuration is something you react to, and watches are the mechanism.

One subtlety: a watch follows the object #

A watch is bound to the specific key object you opened, not to the path string. If layers later cause a different key to appear at the same path, your watch stays with the original object — it does not jump to the newcomer. If the original key is removed, you get a "key deleted" event; to watch whatever now lives at that path, you reopen the path and arm a fresh watch. This follows from keys having an identity of their own, distinct from the name that currently points at them.

Where to go next #

If you want the registry's own use of watches — how it picks up changes to its own configuration without restarting — read How the registry boots and configures itself.

If you want the validation half of the reaction loop — why a changed value might be read and then refused — read Configuration, not storage.

If you want what it takes to be allowed to watch a key, read Access control on keys.

Peios Learn — documentation for the Peios project.

Built with Trail.