Skip to content

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 ContainerSpecid, 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-blueprint materializes 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. A CellConfig is 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 with kuke get cells -l kukeon.io/blueprint=<name>.
  • Scalar parameters declared. A ${KEY} in the body must appear in spec.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 recommending kuke run --from-config <cfg> with a CellConfig that 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>.