# 5.2.8 Names and Case

_Peios / Advanced Peios / PKM / LCS / The Data Model_

> Every string in the LCS interface is UTF-8 — length as byte count, separators, and how case folding works.

Every string in the LCS interface — key names, value names, hive names,
layer names, paths — is UTF-8. Invalid UTF-8 is rejected with `EINVAL`
before parsing, routing, folding, layer resolution or source dispatch.
Null bytes are rejected in all of them.

The one thing that is not a string is value data, which is opaque
bytes.

## 5.2.8.1 Lengths are byte counts

Every configured length limit is measured in UTF-8 bytes, not Unicode
scalar values and not display characters. `MaxPathComponentLength`
(default 255) bounds one component or one value or layer name;
`MaxTotalPathLength` (default 16383) bounds a whole path; `MaxKeyDepth`
(default 512) bounds nesting.

A syscall path arrives as a null-terminated C string, is copied under a
hard bound, and has its terminator stripped before anything is
measured — so the terminator is not part of the length. Ioctl and RSI
strings are length-delimited and need no terminator; a terminator byte
included in the length is a null byte and is therefore invalid.

## 5.2.8.2 Separators

Backslash is canonical. Forward slash is accepted on input and treated
as a separator wherever a separator is recognised, so a component can
never contain either. There is no string-rewriting step: normalisation
is a property of how paths are split rather than a transformation
applied to them.

## 5.2.8.3 Case folding

Comparison is case-insensitive and storage is case-preserving. The
algorithm is **Unicode Simple Case Folding** — the `C` and `S` status
entries of `CaseFolding.txt`, with the full (`F`) and Turkic (`T`)
entries excluded. It is a fixed one-to-one codepoint mapping with no
locale input, applied after decoding from UTF-8, never to raw bytes.

The Unicode version is **pinned at 16.0**. The table is generated by
`pkm/tools/lcs/generate_casefold_table.py` and checked in with the
digest of the source data, so adopting a newer Unicode version means
regenerating the table deliberately. It is not something that happens
by updating a dependency.

This gives practical compatibility with Windows'
`RtlCompareUnicodeString` without claiming byte-identical behaviour in
every edge case.

**Unicode normalisation is not performed.** NFC and NFD forms of the
same visual character are different names, matching Windows.

Case folding is what "identity" means throughout: a layer's identity is
its folded name, a hive route's identity includes its folded name, and
a duplicate is a folded-equal duplicate. `RoleA` and `rolea` are one
layer, not two.

Two comparisons in the kernel are ASCII-only rather than folded: the
check for whether a layer name is `base` on two of its call sites, and
KACS's duplicate check when parsing private layer names into a token.
For the literal string `base` the two agree; for arbitrary names they
do not.
