These docs are under active development and cover the v0.20 Kobicha security model.
On this page
§3.1

Database Schema

Each hive has its own SQLite database. The schema is identical across all hive databases. loregd creates these tables on first boot and migrates them on schema version changes.

§3.1.1 Schema version tracking

CREATE TABLE schema_version (
    version INTEGER NOT NULL
);

A single row. The current schema version is 1 (the initial release defined by this specification). loregd checks this at startup. If the version is older, loregd runs migrations. If newer, loregd refuses to start (prevents downgrade corruption). On first boot (empty database), loregd creates the schema and inserts version 1.

§3.1.2 Keys table

CREATE TABLE keys (
    guid           BLOB NOT NULL PRIMARY KEY,
    name           TEXT NOT NULL,
    name_folded    TEXT NOT NULL,
    parent_guid    BLOB,
    sd             BLOB NOT NULL,
    volatile       INTEGER NOT NULL DEFAULT 0,
    symlink        INTEGER NOT NULL DEFAULT 0,
    last_write_time INTEGER NOT NULL
);
  • guid: 16-byte GUID assigned by LCS. Primary key.
  • name: Case-preserving key name component.
  • name_folded: Unicode Simple Case Folded form of name. Used for case-insensitive lookups.
  • parent_guid: GUID of the parent key. NULL for hive root.
  • sd: Security Descriptor in KACS binary format.
  • volatile: 1 if volatile, 0 if persistent. Volatile keys are stored in the attached in-memory database, not this table. This column exists only for persistent keys and is always 0 in the on-disk database. See the Volatile Keys section.
  • symlink: 1 if this key is a symbolic link, 0 otherwise.
  • last_write_time: Unix nanoseconds.

§3.1.3 Path entries table

CREATE TABLE path_entries (
    parent_guid    BLOB NOT NULL,
    child_name     TEXT NOT NULL,
    child_name_folded TEXT NOT NULL,
    layer          TEXT NOT NULL,
    target_type    INTEGER NOT NULL,
    target_guid    BLOB,
    sequence       INTEGER NOT NULL,
    PRIMARY KEY (parent_guid, child_name_folded, layer)
);

CREATE INDEX idx_path_entries_target
    ON path_entries (target_guid)
    WHERE target_type = 0;
  • parent_guid: GUID of the parent key.
  • child_name: Case-preserving child name.
  • child_name_folded: Folded form for case-insensitive primary key.
  • layer: Layer name (case-sensitive, binary comparison).
  • target_type: 0 = GUID (key exists in this layer), 1 = HIDDEN (tombstone masking lower layers).
  • target_guid: The key GUID if target_type=0. NULL if HIDDEN.
  • sequence: Monotonic sequence number assigned by LCS.

The index on target_guid (for non-HIDDEN entries) enables efficient orphan detection: keys whose GUID does not appear in any path entry.

§3.1.4 Values table

CREATE TABLE values (
    key_guid       BLOB NOT NULL,
    name           TEXT NOT NULL,
    name_folded    TEXT NOT NULL,
    layer          TEXT NOT NULL,
    type           INTEGER NOT NULL,
    data           BLOB,
    sequence       INTEGER NOT NULL,
    PRIMARY KEY (key_guid, name_folded, layer)
);
  • key_guid: GUID of the key this value belongs to.
  • name: Case-preserving value name. Empty string for the default value.
  • name_folded: Folded form. Empty string for the default value.
  • layer: Layer name.
  • type: Registry value type (REG_SZ=1, REG_DWORD=4, etc.). REG_TOMBSTONE (0xFFFF) for tombstones.
  • data: Value payload. NULL for tombstones.
  • sequence: Monotonic sequence number.

§3.1.5 Blanket tombstones table

CREATE TABLE blanket_tombstones (
    key_guid       BLOB NOT NULL,
    layer          TEXT NOT NULL,
    sequence       INTEGER NOT NULL,
    PRIMARY KEY (key_guid, layer)
);
  • key_guid: GUID of the key this blanket tombstone belongs to.
  • layer: Layer name.
  • sequence: Monotonic sequence number.

§3.1.6 Volatile key schema

Volatile keys are stored in the ATTACHed shared in-memory database (volatile). The volatile store uses SQLite's shared-cache URI (file:<hivename>_volatile?mode=memory&cache=shared) so all connections (read and write) for the same hive share the same volatile data. The volatile database has identical table structures:

-- In the 'volatile' attached database:
CREATE TABLE volatile.keys          (...same columns as main.keys...);
CREATE TABLE volatile.path_entries  (...same columns as main.path_entries...);
CREATE TABLE volatile.values        (...same columns as main.values...);
CREATE TABLE volatile.blanket_tombstones (...same columns...);

CREATE INDEX volatile.idx_path_entries_target
    ON path_entries (target_guid)
    WHERE target_type = 0;

When processing RSI operations, loregd checks the volatile flag on the target key to determine which database to query/write:

  • volatile=1 → query/write volatile.* tables
  • volatile=0 → query/write main.* tables

For RSI_LOOKUP and RSI_ENUM_CHILDREN, loregd MUST query both the main and volatile tables and merge the results, since a parent key may be persistent while a child is volatile (or vice versa, though a non-volatile child under a volatile parent is forbidden by the LCS spec).

For RSI_CREATE_KEY, loregd stores the key in the volatile database if the volatile flag is set in the request, otherwise in the main database.

§3.1.7 Case folding

The name_folded and child_name_folded columns store the Unicode Simple Case Folded form (CaseFolding.txt, status S and C entries, Unicode 16.0 per the LCS v0.21 specification) of the corresponding name. loregd computes the folded form at write time and uses the folded column in all WHERE clauses and primary key lookups.

This avoids the need for a custom SQLite collation function. Queries use exact binary comparison on the folded column:

-- Lookup by name (case-insensitive):
SELECT * FROM path_entries
WHERE parent_guid = ? AND child_name_folded = ?

The canonical (case-preserving) name is stored separately and returned in RSI responses. Callers see the original case; storage comparisons use the folded form.