Codex
"The Hexanomicon is the prophecy. The Codex is the law."
The Codex is the immutable configuration from which the Sepulcher is summoned. It defines the fundamental laws of existence for the Lich.
It is physically located at ~/.config/lychd/ (respecting XDG_CONFIG_HOME).
This page explains the Codex as an operator-facing structure and ritual surface.
- For the technical configuration contract (rune ownership, loader rules, validation order), see Configuration (ADR 12).
- For filesystem geography and Host/Container mount symmetry, see Layout (ADR 13).
🏛️ The Anatomy of the Book
The Codex is strictly organized. The Librarian (Loader) reads by anchor and ignores scrolls placed in the wrong section.
graph TD
Codex[~/.config/lychd/]
Prime[lychd.toml]
RuneDir[runes/]
AnimatorDir[animator/]
SoulDir[soulstones/]
PortalDir[portals/]
XDir[other extension anchors...]
Codex --> Prime
Codex --> RuneDir
RuneDir --> AnimatorDir
RuneDir --> XDir
AnimatorDir --> SoulDir
AnimatorDir --> PortalDir
SoulDir --> S1[hermes.toml]
SoulDir --> S2[vision.toml]
PortalDir --> P1[openai.toml]
PortalDir --> P2[anthropic.toml]
style Codex fill:#2a2a2a,stroke:#7c4dff,stroke-width:2px
style Prime fill:#1a1a1a,stroke:#fff
style SoulDir fill:#1a1a1a,stroke:#ff5252
style PortalDir fill:#1a1a1a,stroke:#40c4ff
I. The Prime Scroll (lychd.toml)
This contains the fundamental settings for the Daemon itself: server behavior, logging, persistence, queue defaults, and global policy.
It governs the Sepulcher at the daemon level and provides defaults used by rune families.
Typical examples:
- runtime and service settings
- persistence and queue settings
- global policy thresholds (including privacy/egress policy)
- defaults shared across extensions
The Prime Scroll carries global law. Instance declarations live in runes/.
II. The Rune Archive (runes/)
This is the archive of instance scrolls.
In Codex terms, a rune is one validated TOML config document under the active rune root, defaulting to ~/.config/lychd/runes/ through XDG-aware constants. It is intent, not the running service and not the generated Quadlet artifact. RuneConfig defines fields and its Codex-root-relative anchor path; Codex validates those anchors and owns root resolution. Runic marks runtime objects that keep .rune provenance and are therefore servants of the Codex.
Live source: relative anchor validation
def __init_subclass__(cls, **kwargs: Any) -> None:
"""Validate rune class metadata when a rune subclass is declared.
Args:
**kwargs: Extra subclass initialization keywords forwarded to
``BaseModel``.
Raises:
ValueError: If the subclass declares an invalid ``path_fragment``.
TypeError: If ``path_fragment`` is not a ``Path`` or rune ancestry
is ambiguous.
"""
super().__init_subclass__(**kwargs)
# ``ClassVar`` values are inherited. ``getattr(cls, "path_fragment")``
# would therefore let a child silently reuse its parent suffix, producing
# the wrong anchor. Require a class-local declaration instead.
if "path_fragment" not in cls.__dict__:
msg = f"Rune '{cls.__name__}' declares no path_fragment."
raise ValueError(msg)
path_fragment: Any = cls.__dict__["path_fragment"]
# The fragment is a relative ``Path`` suffix, not a filesystem root.
# Loader/writer code owns the absolute root via ``PATH_RUNES_DIR``.
if not isinstance(path_fragment, Path):
msg = f"Rune '{cls.__name__}' declares non-Path path_fragment {path_fragment!r}."
raise TypeError(msg)
if path_fragment.is_absolute() or path_fragment == Path():
msg = f"Rune '{cls.__name__}' declares invalid path_fragment '{path_fragment}'. Expected relative path fragment."
raise ValueError(msg)
# Validate every fragment part because a fragment may contain more than
# one suffix component. The pattern rejects traversal, uppercase drift,
# spaces, and filesystem-looking surprises.
for part in path_fragment.parts:
if not RUNE_PATH_PART_PATTERN.fullmatch(part):
pattern = RUNE_PATH_PART_PATTERN.pattern
msg = f"Rune '{cls.__name__}' declares invalid path_fragment part '{part}'. Expected pattern: {pattern}"
raise ValueError(msg)
# The final path is a single parent chain plus this suffix. Multiple
# RuneConfig parents would make that path ambiguous. Mixins that only
# share fields should inherit BaseModel or object, not RuneConfig.
rune_parents = [
base for base in cls.__bases__ if issubclass(base, RuneConfig) and base is not RuneConfig
]
if len(rune_parents) > 1:
names = ", ".join(parent.__name__ for parent in rune_parents)
msg = f"Rune '{cls.__name__}' declares multiple rune parents: {names}."
raise TypeError(msg)
parent = rune_parents[0] if rune_parents else None
# Store the final path under PATH_RUNES_DIR, still relative. RuneConfig
# computes schema-local placement; loaders/writers choose the root.
cls.relative_path = path_fragment if parent is None else parent.relative_path / path_fragment
Live source: RuneConfig
RUNE_PATH_PART_PATTERN = re.compile(r"^[a-z0-9](?:[a-z0-9_-]{0,48}[a-z0-9])?$")
class RuneConfig(BaseModel, ABC):
"""Base for TOML-backed Codex runes.
A rune is one validated TOML config document under
``lychd.system.constants.PATH_RUNES_DIR``. Subclasses define TOML fields;
this base validates class-level placement metadata.
"""
model_config = ConfigDict(extra="forbid")
def __init_subclass__(cls, **kwargs: Any) -> None:
"""Validate rune class metadata when a rune subclass is declared.
Args:
**kwargs: Extra subclass initialization keywords forwarded to
``BaseModel``.
Raises:
ValueError: If the subclass declares an invalid ``path_fragment``.
TypeError: If ``path_fragment`` is not a ``Path`` or rune ancestry
is ambiguous.
"""
super().__init_subclass__(**kwargs)
# ``ClassVar`` values are inherited. ``getattr(cls, "path_fragment")``
# would therefore let a child silently reuse its parent suffix, producing
# the wrong anchor. Require a class-local declaration instead.
if "path_fragment" not in cls.__dict__:
msg = f"Rune '{cls.__name__}' declares no path_fragment."
raise ValueError(msg)
path_fragment: Any = cls.__dict__["path_fragment"]
# The fragment is a relative ``Path`` suffix, not a filesystem root.
# Loader/writer code owns the absolute root via ``PATH_RUNES_DIR``.
if not isinstance(path_fragment, Path):
msg = f"Rune '{cls.__name__}' declares non-Path path_fragment {path_fragment!r}."
raise TypeError(msg)
if path_fragment.is_absolute() or path_fragment == Path():
msg = f"Rune '{cls.__name__}' declares invalid path_fragment '{path_fragment}'. Expected relative path fragment."
raise ValueError(msg)
# Validate every fragment part because a fragment may contain more than
# one suffix component. The pattern rejects traversal, uppercase drift,
# spaces, and filesystem-looking surprises.
for part in path_fragment.parts:
if not RUNE_PATH_PART_PATTERN.fullmatch(part):
pattern = RUNE_PATH_PART_PATTERN.pattern
msg = f"Rune '{cls.__name__}' declares invalid path_fragment part '{part}'. Expected pattern: {pattern}"
raise ValueError(msg)
# The final path is a single parent chain plus this suffix. Multiple
# RuneConfig parents would make that path ambiguous. Mixins that only
# share fields should inherit BaseModel or object, not RuneConfig.
rune_parents = [
base for base in cls.__bases__ if issubclass(base, RuneConfig) and base is not RuneConfig
]
if len(rune_parents) > 1:
names = ", ".join(parent.__name__ for parent in rune_parents)
msg = f"Rune '{cls.__name__}' declares multiple rune parents: {names}."
raise TypeError(msg)
parent = rune_parents[0] if rune_parents else None
# Store the final path under PATH_RUNES_DIR, still relative. RuneConfig
# computes schema-local placement; loaders/writers choose the root.
cls.relative_path = path_fragment if parent is None else parent.relative_path / path_fragment
path_fragment: ClassVar[Path]
"""Relative suffix appended after the parent rune class's ``relative_path``."""
relative_path: ClassVar[Path]
"""Computed path under ``PATH_RUNES_DIR`` where this class's TOMLs live."""
source_file: Path | None = Field(default=None, exclude=True, repr=False)
"""Absolute source TOML file that produced this validated instance."""
Live source: Runic
"""Typing protocols for objects that retain rune provenance."""
from __future__ import annotations
from typing import Protocol, runtime_checkable
from lychd.config.runes.base import RuneConfig
@runtime_checkable
class Runic[T: RuneConfig](Protocol):
"""Protocol for objects configured by a Codex Rune.
Any runtime object with ``rune: T`` satisfies this protocol structurally.
Such objects are servants of the Codex: they retain the validated config
that shaped them, but they are not themselves rune schemas.
"""
@property
def rune(self) -> T:
"""Return the validated source Rune.
Returns:
The ``RuneConfig`` instance used as this object's configuration
provenance.
"""
For rune discovery and extension assembly, see Configuration (ADR 12).
Each subdirectory (anchor) belongs to a rune family. Core modules and installed extensions may declare additional anchors. The Librarian reads scrolls from their anchor territory and validates them before any binding occurs.
In practice:
- one TOML file = one instance
- the folder path determines which rune family owns the file
- misplaced scrolls are ignored or rejected during validation
- valid runes become intent for the binding ritual
The rune archive is extensible. Installing an extension can add new anchors without changing how the Codex is read.
III. Common Built-In Rune Families
The Codex ships with animator-related rune families by default:
runes/animator/: branch anchor for animator-owned rune familiesrunes/animator/soulstones/: branch anchor for local Quadlet-backed runtime familiesrunes/animator/soulstones/generic/: custom/passive local runtimes without a dedicated adapter schemarunes/animator/portals/: remote service/provider connections
For details on each family:
- Animator for the overall service animation model
- Soulstone for local runtimes and containerized services
- Portal for remote providers, peer services, and cloud connections
- Extensions for extension-owned capabilities and added rune families
The Prime Scroll Sets the Ground
Keep daemon-wide settings and shared defaults in lychd.toml.
Put instance-specific declarations in rune files under runes/.
This keeps the Codex readable and keeps the binding ritual deterministic.
🔮 The Rite of Binding
The Codex is merely a book of Potential until it is spoken. The lychd bind command is the bridge between the Configuration (Codex) and the Operating System (Reality).
# 1. Edit the Scrolls
vim ~/.config/lychd/runes/animator/soulstones/generic/my-model.toml
# 2. Perform the Rite
lychd bind
The Transmutation Process
- Reading: The Librarian reads
lychd.tomland the rune archive by anchor. - Validation: The Codex is checked for structural violations before manifestation (ownership, identity, required named instances, and policy constraints).
- Calculation: The Scribe resolves runtime relationships and orchestration consequences.
- Inscription: The Scribe writes generated Quadlet manifests into the System's Binding Site (
~/.config/containers/systemd/). - Reanimation: Systemd reloads, and the new services manifest.
For the technical rules behind this sequence:
The Ephemeral Quadlets
Do not edit the files in ~/.config/containers/systemd/ manually.
These files are Quadlet manifests, projected by the Scribe from Codex Runes. They are ephemeral artifacts. The next time lychd bind runs, the Scribe wipes that directory clean and rewrites it from scratch.
To change reality, edit the Codex, not the projection.