Soulstone Rune
A Soulstone Rune is compiler input: one validated TOML document describing a local service. It is neither live state nor a generated Quadlet. Concrete instances live beneath a registered leaf runtime schema:
~/.config/lychd/runes/animator/soulstones/<runtime>/<instance>.toml
The abstract Animator and Soulstone branches cannot own TOML files. Folder ancestry selects the
Rune type; no type= field may switch it after discovery.
In code, the Rune combines Animator-owned identity and capability intent with an embedded
quadlet: QuadletConfig value. That nested value owns the common image invariant only. Soulstone
keeps its own runtime, endpoint, resource, secret, and lifecycle policy, and the generated
QuadletContainer remains a separate Bind/Scribe artifact.
The Runtime Shape
| Field | Default | Office |
|---|---|---|
name |
required | Animator identity; first segment of every capability key. |
description |
"" |
Operator note. |
quadlet.image |
runtime default | OCI image inside the embedded QuadletConfig; required by the generic runtime. |
runtime |
"generic" |
Selects the local runtime adapter. |
model_path |
null |
Single-model artifact path or runtime-specific identity input. |
served_model_id |
null |
Exact provider-facing id returned by live model inventory; required when it differs from the path basename or Soulstone name. |
base_url |
derived | Connector endpoint root; credentials, query, fragment, and invalid ports are refused. |
port |
assigned | Unique host port; derives http://localhost:{port}/v1. |
groups |
[] |
Compatible Coven target membership. |
devices |
[] |
Explicit device passthrough. |
volumes |
[] |
Explicit host-to-container mounts. |
env_vars |
{} |
Non-secret environment values. |
secret_env_files |
{} |
Environment names mapped to Podman secret names. |
exec |
[] |
Complete command override; bypasses adapter synthesis. |
models |
[] |
Declared model catalogue; a non-empty table is the exact admitted id allowlist. |
generation |
null |
Service-wide generation overlay. |
Runtime leaves may add typed fields such as extra_args or llama.cpp startup_mode. Adapter
defaults are followed by typed overrides; explicit exec replaces synthesized arguments rather
than extending them.
A concrete built-in runtime leaf pins its own runtime literal. A llama.cpp, vLLM, SGLang, or
ExLlamaV3 file cannot claim another adapter merely by changing that field; use the matching Rune
directory and schema. base_url is a composable endpoint root, so a path prefix such as /v1 is
preserved, while embedded credentials, query or fragment state, port zero, and out-of-range ports
fail during Rune validation. Hydration additionally requires a Soulstone URL to use an approved
loopback host and an explicit port; when port is also declared, the two values must agree. A
non-loopback endpoint belongs at the Portal boundary.
Loaded Rune values are immutable through their nested models, sequences, and string maps. Change the TOML and construct a new process generation; do not mutate an admitted object in place. Rune writing uses the same exact schema generation admitted by loading, rather than every imported Python subclass, so an unregistered branch cannot leak into generated configuration.
Capability declarations
The accepted general-service shape adds first-class [[capabilities]] entries. A declaration
references one registered interface_id and immutable profile_ref (stable id plus revision or digest), selects its permitted
operations, and pins the driver/dialect, evidence, resource envelope, and containment profile for
this instance. A Rune may supply endpoint, secret reference, lifecycle, and explicit admitted
overlays; it cannot rewrite the referenced profile's request/result schemas, licenses, formats,
languages, or proved limits.
The following model catalogue is the currently delivered v1 compatibility path, not the universal service schema.
Declared models become v1 capabilities
Each [[models]] block names a stable id, an explicit container-side path, an optional
description and format, capability hints, and a per-model generation overlay. It yields capability
identity {animator}:{family}:{model_id}. Duplicate ids fail validation. When at least one block is
present, those ids are the complete admitted catalogue in declaration order: live discovery for an
undeclared id is ignored, while a declared id absent from discovery keeps its declaration but may
be downgraded by readiness evidence.
[models.capabilities] may declare:
families:chat,vision,embedding,stt,tts,tool_execution, orrerank;modalities_inandmodalities_out:text,image, oraudio; andsupports_toolsandsupports_streaming.
These hints are authoritative for routing. A live probe may downgrade availability or fill an
explicitly open runtime fact; it may not invent an undeclared model. An explicit families list is
closed. When that field is omitted, v1 applies only its bounded inference: chat from the admitted
surface/text input and probed embedding or rerank; it never infers vision, stt, tts, or
tool_execution. Image input enriches a chat capability and does not create the dedicated
vision family. The full two-axis law lives in Capabilities.
An empty models list is not the Portal zero-capability rule. A Soulstone adapter may instead use
its runtime/discovery model catalogue or derive one model id from served_model_id, model_path,
or the Soulstone name. A non-empty list remains the exact allowlist.
Every admitted Soulstone must synthesize at least one capability through its adapter. An
unrecognised generic runtime remains passive at the adapter boundary, but registry generation
refuses that Soulstone until a registered adapter or explicit OpenAI-compatible alias with defined
binding semantics yields a capability. In the general-service shape, non-model services use
[[capabilities]]; they do not fake a model declaration. Compatibility is claimed separately for
each named Connector dialect.
Generation Overlays
Generation fields are optional: max_context, max_tokens, temperature, top_p, top_k,
repetition_penalty, and reasoning_format. Effective values overlay in this order:
runtime defaults → Soulstone [generation] → [models.generation]
The accepted ranges are max_context/max_tokens ≥ 1, finite temperature 0–2, finite top_p
0–1, top_k ≥ 0, and finite repetition_penalty ≥ 0; NaN and infinities are invalid even when a
comparison alone would appear to admit them.
Concurrency Intent
The [concurrency] table separates lifecycle authority from coexistence:
| Field | Meaning |
|---|---|
dedicated |
LychD owns lifecycle and may start, stop, or evict the runtime. |
persistent_resident |
Keep the runtime resident and outside every eviction set. |
conflict_domains |
Finite hardware domains this managed runtime cannot share. |
Omitting conflict_domains on a dedicated non-resident requests the compiler-owned
default-exclusive wildcard. Explicit [] alone asserts coexistence. A shared
(dedicated = false) or persistent-resident Soulstone may omit the field or declare []; a
non-empty set fails binding because LychD may neither evict the shared runtime nor compile an edge
that could evict a resident. Labels are lowercase identifiers of at most 50 characters; operators
must not spell default-exclusive directly.
groups requests compatible aggregation, and one Rune may name several groups. That means the
same service participates in several operator formations; it does not duplicate the instance or
promise that the complete union may coexist. alliances is accepted shape without enforcement
authority. Neither changes the conflict graph, reserves hardware, or asks for semantic dispatch.
Refusal and Handoff
The complete Rune generation validates before any unit is written. Duplicate identity, invalid ports, missing named secrets, unsafe mounts, an internally conflicting Coven, or an unauthorized conflict declaration fails closed. Configuration owns Rune discovery and validation; Containers owns projection; Orchestrator owns transitions.