CellBlueprint manifest¶
apiVersion: v1beta1
kind: CellBlueprint
metadata:
name: claude-code
realm: kuke-system # scope coordinates; deepest non-empty wins
# space: team-a # optional — space-scoped
# stack: agents # optional — stack-scoped (requires space)
spec:
prefix: cc # cell-name prefix; defaults to metadata.name
parameters: # scalar ${KEY} substitutions
- name: MODEL
default: claude-sonnet-4-6
cell:
containers:
- id: agent
root: true
image: ghcr.io/example/agent:latest
# ... full container template ...
A CellBlueprint is a daemon-stored, parametrized cell template. It is written to daemon storage by kuke apply and run with kuke run --from-blueprint, so it can be applied, scoped, listed, and referenced by a CellConfig. A blueprint declares the cell template plus two fill channels — scalar ${KEY} parameters resolved at run time and structural repo/secret slots a Config fills.
See kuke run --from-blueprint for the run-side verb and CellConfig for the binding that additionally fills a blueprint's structural repo/secret slots. Both stamp a fresh <prefix>-<6hex> cell per invocation — a blueprint with required slots can only run through a Config.
metadata¶
A Blueprint is scopable at realm, space, or stack — never cell. A template scoped to a single cell is nonsensical: the blueprint exists to materialize cells, not to live inside one. The scope is the deepest non-empty coordinate, and a deeper coordinate may only be set when every shallower one is.
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | The blueprint's name, unique within its scope. |
realm |
string | yes | The always-required top-level scope coordinate. |
space |
string | no | When set, scopes the blueprint to a space within realm. |
stack |
string | no | When set, scopes the blueprint to a stack within space (requires space). |
labels |
map[string]string | no | Copied onto every cell materialized from this blueprint, in addition to the kukeon.io/blueprint lineage label. |
spec¶
spec.prefix (string, optional)¶
Cell-name prefix used when generating the <prefix>-<6hex> name on each kuke run --from-blueprint. Defaults to metadata.name when unset.
spec.parameters[] (list, optional)¶
Declared ${KEY} substitution variables the cell body references. kuke run --from-blueprint resolves each parameter against --param K=V, the parameter's default, and (when permitted) the caller's environment, in that order. An undeclared --param errors at call time so typos surface immediately. A required: true parameter that resolves to no value errors.
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | The substitution variable's name (referenced as ${name}). |
description |
string | no | Free-form documentation. |
default |
string | no | Default value when --param is not supplied. Empty string is a valid default and short-circuits the env-fallback. |
required |
bool | no | When true, the blueprint fails to run if the parameter resolves to no value. |
spec.cell (object, required)¶
The cell template body. It mirrors a Cell manifest's user-authorable surface, but each container additionally carries structural slot declarations — repo slots with no inline url, and secret slots — that a CellConfig fills.
| Field | Type | Required | Description |
|---|---|---|---|
tty |
object | no | Cell-level TTY configuration; see Cell. |
containers[] |
list | yes | The cell's containers; see below. |
autoDelete |
bool | no | When true, the materialized cell auto-deletes on exit. |
nestedCgroupRuntime |
bool | no | Opts the cell into full controller delegation for a nested runtime (e.g. an inner kukeond). See Cell. |
spec.cell.containers[] (list, required)¶
Each container carries the user-authorable subset of ContainerSpec — id, image, command, args, env, volumes, networks, ports, resources, git, tty, the host-namespace toggles, capability sets, and restartPolicy. Daemon-stamped identity fields (containerdId, realmId, spaceId, stackId, cellId) are intentionally absent: materialization fills them from the blueprint's metadata and the generated cell name.
In addition to the regular container fields, a blueprint container declares two slot channels:
spec.cell.containers[].repos[] (list, optional)¶
Same shape as ContainerRepo. Unlike a hand-written Cell / Container, url is not required at apply time. A repo with no url is a structural slot a CellConfig fills; a repo whose url is supplied inline (directly or via a ${KEY} parameter) runs as-is under kuke run --from-blueprint. The slot's name is the identity a CellConfig matches on.
spec.cell.containers[].secrets[] (list, optional)¶
Slot-only channel: the blueprint declares the consumption side (where the resolved bytes land inside the container), and a CellConfig supplies the source side (which kind: Secret provides the bytes). Because a blueprint never carries the source, a blueprint that declares secret slots cannot run inline with --from-blueprint — it requires kuke run --from-config <cfg> with a CellConfig that fills the slots.
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | The slot's identity, unique within the container; a CellConfig matches by this name. |
mode |
string | no (default env) |
env injects an environment variable named envName; file stages a read-only mount at mountPath. |
envName |
string | when mode: env |
Environment variable name. Independent of the slot name, so the consumption side can change without renaming the slot. |
mountPath |
string | when mode: file |
Absolute in-container path for the read-only file mount. |
required |
bool | no | When true, a CellConfig must fill the slot. Required slots also block inline --from-blueprint. |
Storage layout¶
The daemon writes the blueprint document to a root-owned, world-readable file under the scope's metadata tree:
<runPath>/data/<realm>/blueprints/<name> # realm-scoped
<runPath>/data/<realm>/<space>/blueprints/<name> # space-scoped
<runPath>/data/<realm>/<space>/<stack>/blueprints/<name> # stack-scoped
The blueprints/ directory is 0755 and each blueprint document is 0644, both owned by root. A blueprint carries only template references — no credential bytes — so the directory is world-readable, unlike secrets/. Because blueprints/ nests inside the scope's metadata directory, the same teardown that reclaims a scope (kuke purge / kuke delete) reclaims its blueprints.
Invariants¶
- Always fresh. Every
kuke run --from-blueprintmaterializes a cell named<prefix>-<6hex>, where the suffix is 3 bytes of entropy. A blueprint binds only scalar parameters; pin a name per invocation with--name. ACellConfigis also a 1:N binding (a fresh cell per stamp) — its added value over a bare blueprint is filling the blueprint's structural repo/secret slots, not pinning a singleton. - Lineage label. Every materialized cell carries the
kukeon.io/blueprint=<name>label, so an operator can list all cells stamped from a blueprint withkuke get cells -l kukeon.io/blueprint=<name>. - Scalar parameters declared. A
${KEY}in the body must appear inspec.parameters[]; otherwise the blueprint fails to load. Typos surface at apply time, not as a runtime mystery. - Structural slots block inline. A required repo slot (no inline
url) or any required secret slot makes the blueprint un-runnable with--from-blueprint; the run path refuses with a message naming the offenders and recommendingkuke run --from-config <cfg>with aCellConfigthat fills them. Optional unfilled slots are dropped silently from the materialized container.
Minimal¶
apiVersion: v1beta1
kind: CellBlueprint
metadata:
name: claude-code
realm: kuke-system
spec:
parameters:
- name: MODEL
default: claude-sonnet-4-6
cell:
containers:
- id: agent
root: true
image: ghcr.io/example/claude-code:latest
env:
- MODEL=${MODEL}
A realm-scoped blueprint named claude-code with one scalar parameter and one container. Run a fresh instance with kuke run --from-blueprint claude-code --realm kuke-system (each invocation produces a new claude-code-<6hex> cell). To additionally fill structural repo/secret slots, bind it via a CellConfig and run with kuke run --from-config <cfg>.