Concepts
This section explains the Soul Stack mental model and its vocabulary of names. Understanding these five or six entities is enough to read the rest of the documentation.
The mental model
Section titled “The mental model”Soul Stack is a central server that holds a description of the desired state of hosts, and agents that bring their hosts to that state.
The flow in one sentence: an operator describes what should be on a host (Destiny), the server renders that description against the host’s facts (Soulprint) and parameters (Essence), sends it to the agent, and the agent applies it — installs packages, edits files, manages services — and reports the run’s result back.
flowchart TB
Op["Operator (Archon)"] -->|describes the desired state| D["Destiny"]
D --> K["Keeper (central server)"]
K -->|"renders against Soulprint + Essence<br/>delivery over gRPC on mTLS"| S["Soul (agent on the host)"]
S -->|applies to the host| H["host"]
S -.->|run report back to Keeper| K
The key difference from “run a script over SSH”: Soul Stack is declarative and idempotent. You don’t describe “run the package-install command”; you describe “the htop package must be installed”. On a repeat run, if the state is already reached, nothing changes — the module reports changed=false.
Vocabulary of names
Section titled “Vocabulary of names”Soul Stack uses a soulful metaphor. The roles behind the names are the familiar ones from master/agent systems; the rest of the documentation uses only our terms.
| Name | What it is |
|---|---|
| Keeper | The central server. Holds the registries and the service catalog, renders and delivers Destiny, keeps the audit log. Scales horizontally (several instances over shared PostgreSQL and Redis). |
| Souls (Soul) | Agents on managed hosts. A single Go binary that applies Destiny. |
| Destiny | A host’s desired state after a run — a set of steps like “package installed”, “file present”, “service running”. |
| Soulprint (Prints) | Facts about the host system: OS, distribution, kernel, CPU, memory, network. Collected by the agent. |
| Essence | Parameters and secrets bound to a host or group. |
| Coven | A stable logical tag for a group of agents (cluster, project, environment, data center). Used in targeting, RBAC, and routing. |
| Trait | Operator-set key-value labels on an incarnation (owner=alice, product=billing). An axis parallel to Coven: owner/product/namespace attributes you can filter targets on precisely and narrow RBAC visibility by. |
| Archon | A Soul Stack operator — a person or a machine identity. The identifier (AID) is any string matching the pattern ^[a-z0-9][a-z0-9._@-]{1,127}$ (2–128 characters); for example archon-alice, alice@corp.com, uid-4815, ops-team. |
Keeper
Section titled “Keeper”The central server. Not a monolith: a horizontally scalable stateless cluster over shared PostgreSQL (cold storage: registries, service catalog, logs) and Redis (heartbeat cache, leases, coordination between instances). Any instance in the cluster can serve any request.
Keeper is responsible for:
- the registry of agents and their identities;
- the service catalog (what can be applied) and runtime instances (incarnations);
- rendering Destiny (resolving secrets, evaluating CEL expressions, rendering templates) — rendering happens on the Keeper side; the agent carries no template engine;
- delivering rendered Destiny to agents over gRPC on mTLS;
- RBAC, the audit log, Vault integration.
The agent on a managed host — a single static Go binary, soul. It works in two modes with the same set of modules:
- pull — a daemon that initiates a long-lived connection to Keeper itself and waits for jobs;
- push — one-shot application, where Keeper delivers Destiny over SSH to a host with no agent installed (via the
keeper.pushmodule).
An agent’s identity is its SID (equal to the host’s FQDN) and its SoulSeed (an mTLS pair of certificate + private key, issued by Keeper and rotated regularly). The private key is generated on the host and never leaves it.
Destiny — what gets applied to the host
Section titled “Destiny — what gets applied to the host”Destiny is a declarative description of a host’s desired state. It consists of steps; each step addresses a module and its state in the form core.<module>.<state>:
- module: core.pkg.installed # the package must be installed params: name: nginx
- module: core.file.present # the file must be present params: path: /etc/nginx/nginx.conf content: "..."
- module: core.service.running # the service must be running params: name: nginxEach step is idempotent: the module first checks the current state and changes the host only if it differs from the desired one. A step’s result is changed=true (the state changed) or changed=false (it was already so).
Destiny is not written in a “programming language” — it is YAML with a typed schema. Expressions inside it (conditions, interpolation) are CEL; file rendering is Go text/template. See the DSL section.
Soulprint — facts about the host
Section titled “Soulprint — facts about the host”Soulprint is a typed set of facts the agent collects about its host: OS family and distribution, version, architecture, package manager and init system, kernel, CPU, memory, network interfaces. These facts are available when rendering Destiny — for example, to pick a package name by distribution or to target a step by a host attribute.
In expressions, facts are addressed as soulprint.self.<path> — for example soulprint.self.os.family.
Essence — parameters and secrets
Section titled “Essence — parameters and secrets”Essence is parameters and values bound to an agent or group: application settings, passwords, keys. Secrets are resolved through Vault on the Keeper side during rendering and masked in the output (in logs, traces, reports) — they never reach the logs in cleartext.
Coven — grouping and targeting
Section titled “Coven — grouping and targeting”Coven is a stable logical tag for a group of agents: cluster, project, environment, data center, hardware type. Coven labels are the basis for:
- targeting — which hosts take part in a scenario run;
- RBAC — which operators see and manage which groups;
- routing — potentially, which Keeper instances a group gravitates to.
Important: a Coven captures only stable attributes. A host’s volatile role (for example, primary/replica in a cluster) is not a Coven — it is determined by a live check during the run.
Trait — incarnation attributes
Section titled “Trait — incarnation attributes”Trait — operator-set key-value labels on an incarnation: owner=alice, product=billing, namespace=dba-ns. This is an axis parallel to Coven — where Coven answers “which group a host belongs to” with a flat label, Trait answers “whose instance this is / which product / which namespace” with a key-value pair.
Differences from Coven:
- Key-value, not a flat label. A Trait’s value is a scalar (
product: billing) or a list (owners: [alice, bob]); you can filter precisely by key (owner == 'alice'), which a flat Coven does not allow. - Belongs to the incarnation, not the host. A Trait is set by the operator in
incarnation.spec.traitswhen the incarnation is created and is projected onto all its hosts automatically — it is an organizational label for the owner/product of the whole instance. - Sourced from the operator, not the agent. Like Coven, a Trait is a stable registry fact (who owns the instance), not something the agent reports about the system.
Trait labels are projected into soulprint.self.traits.<key> and available in targeting (where:) and in RBAC scope (trait=key:value). Managed via Trait on the incarnation (REST/MCP/UI); targeting via Scenario → where:.
Service and Incarnation
Section titled “Service and Incarnation”Two related concepts from the runtime model:
- Service — the type: a git repository with scenarios, default parameters, and a state schema. Versioned by a git ref (a tag or a branch).
- Incarnation — a runtime instance of a service: a concrete application of the type to a set of hosts, with its own state in PostgreSQL. An incarnation has a
spec(what was ordered), astate(what was actually reached), and astatus(where the run is now).
For example, the Service redis-cluster is a description of “how to bring up a Redis cluster”; the Incarnation redis-prod-eu is a concrete cluster in EU production, with its own set of hosts and state.
What’s next
Section titled “What’s next”- Architecture and diagrams — how the components connect technically.
- DSL — the grammar of Destiny, scenarios, and Essence.
- Modules — what the built-in modules can do.