13. Layout
Context and decision
XDG geography tells LychD where an object belongs; typed creation and binding receipts decide what it may later change or remove.
The three domains
src/lychd/system/constants.py is the executable path contract. ~ is the effective LychD
user's home. Shared XDG parents are never recursively owned by LychD.
| Domain | XDG root and default | LychD root | Purpose |
|---|---|---|---|
| Codex | XDG_CONFIG_HOME or ~/.config/ |
~/.config/lychd/ |
lychd.toml, lifecycle receipt, Runes |
| Crypt | XDG_DATA_HOME or ~/.local/share/ |
~/.local/share/lychd/ |
durable data, triggers, PostgreSQL, Lab, Core, Extensions |
| Forge | XDG_CACHE_HOME or ~/.cache/ |
~/.cache/lychd/ |
disposable assembly staging |
The Codex contains lychd.toml, .lychd-lifecycle.json, and runes/ including the Animator,
Soulstone, and Portal anchors. The Crypt contains the Vessel's runtime-guard, triggers/inbox and triggers/journal,
postgres/init_db.sh, postgres/pg_hba_v1.conf, postgres/data, snapshots, lab, core, and extensions.
postgres/data may be an ordinary directory, external mount, or LychD-created Btrfs subvolume.
The Forge contains assembly. Configuration (12) owns what these paths
mean; Security owns credentials and network policy.
Binding sites are shared host namespaces: ~/.config/containers/,
~/.config/containers/systemd/, ~/.config/systemd/, and ~/.config/systemd/user/ (with XDG
overrides applied). The lifecycle and Scribe ownership receipts are regular, non-symlink,
invoking-UID-owned files with mode 0600. They grant authority only over recorded identities and
names, never their shared parents.
Creation, binding, and deletion authority
init --dry-run describes a plan without mutation; init journals only successful creation
identities. At the shared binding sites, init creates missing components as 0700 and preserves
safe existing modes. Planning and execution refuse symlinks, non-directories, foreign ownership,
unsafe effective access or group/other writability, and unsafe ancestry (except an appropriate
sticky root/invoker-owned directory or a foreign read-only mount). bind applies the same
binding-site law while planning and committing, but never creates a missing site. The
per-UID/per-Codex lifecycle lock is under fixed host /tmp, not caller-selected TMPDIR.
The exact existing PostgreSQL data/ leaf is a preservation-only exception to host-UID
ownership: rootless PostgreSQL may own it through a subordinate UID. Init verifies its real,
host-owned parent through no-follow descriptors and observes the leaf without entering it;
neither may be group/other-writable. It neither changes that ownership or mode nor adopts the
leaf into ordinary directory deletion authority. This observation proves safe preservation,
not container access, database readiness, or permission to repair an arbitrary foreign directory.
Publish an owned object
Every traversal is descriptor-relative and no-follow. A missing component is made under a private
same-directory staging name, opened and device/inode-attested, then atomically published without
replacement. The receipt records the opened identity and parent descriptor authority rather than a
replaceable pathname. File publication follows the same rule: write and fsync a candidate,
publish no-clobber, re-attest through the pinned parent, fsync the directory, then journal. A
race winner, replacement, ambiguous result, or failed journal is preserved or quarantined as typed
recovery; it is never broadened into deletion authority.
The Designed Reliquary profile binds an explicitly supplied isolated private root to its exact owned identity; it introduces no implicit Crypt path or container mount. Artifact identifiers and producer labels never supply unchecked path components. Reads and publication re-attestation use nonblocking, no-follow opens and verify the opened inode before reading. Payload retirement follows a durable tombstone and exact identity attestation, then namespace sync. Unknown names, extra hardlinks, replacements and interrupted outcomes remain recovery evidence rather than deletion authority.
Retire only recorded identities
After a complete transaction, the receipt may adopt the exact device/inode identities of the
dedicated Codex, Crypt, and Forge roots. Shared XDG parents, mounts, source checkouts, and external
model shelves remain outside that grant. del is a separately confirmed destructive lifecycle:
it joins receipt authority to a live inventory, stops managed installation state, and refuses
unknown mounts, identity mismatches, invalid receipts, foreign objects, or ambiguous ancestry. Recursive
walks reject mount crossings and possible Btrfs root/stub signatures. Deletion moves a
re-attested leaf to a collision-resistant private sibling before type-specific removal; mismatch or
failure restores it when possible, otherwise leaves a typed recovery marker. Published init
creations are never pathname-deleted as rollback.
PostgreSQL substrate
Only an absent PostgreSQL data/ target on trusted Btrfs may receive a new subvolume and +C
attempt. Creation addresses the leaf through its inherited parent descriptor. Identity observation
uses a bounded, fixed read-only helper with only the opened leaf descriptor: it requires a real
Btrfs root directory and a supported kernel ABI before reading its own UUID and ID through
BTRFS_IOC_GET_SUBVOL_INFO. No privileged path or snapshot lookup is required. No-COW mutation
and before/after confirmation remain descriptor-bound. A successful subvolume records canonical
UUID and non-reserved ID as well as device/inode in the version-2 lifecycle receipt. Existing
storage is never retrofitted; +C is only an inheritance policy and a false result is a warning.
LychD never adopts an observed existing subvolume or backfills a missing identity. A mounted
Phylactery requires complete live mount and Btrfs agreement; an unmounted one requires the exact
version-2 creation receipt plus matching live identity and a safe top-level mapping. On success,
LychD emits an attested btrfs subvolume delete --subvolid ID TOP_LEVEL operator handoff and waits
for proven absence before generic retirement. It never invokes sudo.
Host and container geography
The Vessel uses identical host/container targets: Codex is read-only; lab and runtime-guard are read-write; core
and extensions are read-only; and when selected, Reactor inbox is read-write and journal is
read-only. PostgreSQL data belongs to its PostgreSQL unit; there is no blanket Crypt mount.
~/work/ is an explicit read-write Outland mount and never gains Three-Domain lifecycle authority.
Tomb's reserved task, workspace, artifact, and cache targets sit beneath
~/.local/share/lychd/tomb/, but it receives no Codex and no trigger/signalling path. Its envelope
is secret-forbidden. This is a designed trust delta, not an executor: Security (09)
owns its credentials/network/isolation and Workers (14) any executor.
Consequences
XDG overrides move roots but not relative topology or ownership law. A known location is useful geography, never permission to adopt, overwrite, or delete somebody else's object. The implemented CLI path is Partial and real-host lifecycle proof remains separate; State of Work owns that maturity boundary.