initree capability registry — v1¶
Status: the public contract. This is the vocabulary every layer reads from and writes to the shared context. It is versioned and governed by the stability policy in §11.
Relationship to the other documents:
- the layer manifest schema (apiVersion: initree.dev/v1) defines the shape of a layer.yaml.
- the slice walkthroughs (python/fastapi/docker/gh-actions/vps and go/gin/gitlab-ci/k8s/slack)
are worked examples.
- this registry defines the meaning of the keys those layers exchange. The manifest schema and
this registry version independently.
1. Core concepts¶
Capability key. A namespaced namespace.key value on the shared context. A consumer binds to the
key, never to the layer that produced it — so swapping the producing layer (docker → podman,
gh-actions → gitlab-ci) requires no change to any consumer.
Namespace ownership. Each namespace has exactly one authoritative provider slot (§3). Only that slot's layer may provide that namespace's keys. This is what makes capability-binding enforceable.
Conformance. Each key carries a level for its owner slot: - MUST — a conformant layer of that slot is required to provide it. Consumers may rely on it. - SHOULD — recommended; consumers must tolerate absence with a documented fallback. - MAY — optional; provided only when meaningful (e.g. compiled-language build keys).
Two-tier interpolation.
- ${namespace.key} — a context value, resolved by the engine at compute (scaffold time).
Always concrete and backend-agnostic.
- {{TOKEN}} — a deferred runtime token, resolved only by the ci slot at its render (§7),
because only it knows the runtime's native syntax. Used for secrets and the commit SHA.
Shared vs private. Keys in this registry are the shared contract. A layer's own inputs and state
live under namespace.<backend>.* (e.g. deploy.k8s.namespace) and are never consumed by other
layers (§9).
2. Type vocabulary¶
| type | meaning |
|---|---|
string |
UTF-8 string |
int |
integer |
bool |
true/false |
list<string> |
ordered list of strings |
map<string,string> |
string→string mapping |
recipe |
list<string> of shell commands that may contain {{TOKEN}}s. Backend-agnostic; rendered into a CI job by the ci slot. |
3. Namespace ownership map¶
| namespace | provider | purpose |
|---|---|---|
project.* |
engine (seeded) | project identity |
git.* |
engine (seeded) | repo facts |
runtime.* |
language slot | language toolchain + build recipe inputs |
app.* |
framework slot | how the application starts and is checked |
container.* |
container slot | the built image |
registry.* |
container slot | where the image is published |
ci.* |
ci slot | the pipeline system |
deploy.* |
deploy slot | the deployment target (common facts only) |
notify.* |
notify slot (optional) | outbound notifications |
4. Engine-seeded keys (always present)¶
project.*¶
| key | type | conformance | description | example |
|---|---|---|---|---|
project.name |
string | MUST | human-readable project name | myapp |
project.slug |
string | MUST | filesystem/DNS-safe name | myapp |
project.dir |
string | MUST | absolute output directory | /home/me/myapp |
git.*¶
| key | type | conformance | description | example |
|---|---|---|---|---|
git.is_repo |
bool | MUST | whether project.dir is already a git repo |
false |
There is deliberately no
git.shaat scaffold time. The commit SHA is a CI-runtime concept; recipes obtain it via the{{SHA}}token or implicitly inside{{IMAGE}}(§7).
5. runtime.* — provided by the language slot¶
| key | type | conformance | description | example |
|---|---|---|---|---|
runtime.language |
string | MUST | language id | python / go |
runtime.version |
string | MUST | language version | 3.12 / 1.22 |
runtime.base_image |
string | MUST | OCI image for build (and run, if single-stage) | python:3.12-slim / golang:1.22 |
runtime.install_cmd |
string | MUST | command that fetches dependencies | uv sync --frozen / go mod download |
runtime.test_cmd |
string | SHOULD | command that runs the test suite; the ci layer renders the test job from it | uv run pytest / go test ./... |
runtime.run_base_image |
string | MAY | final-stage image for compiled languages | gcr.io/distroless/static-debian12 |
runtime.build_cmd |
string | MAY | compile step (compiled languages only) | CGO_ENABLED=0 go build -o /out/server ... |
runtime.artifact |
string | MAY | built artifact path (compiled languages only) | /out/server |
runtime.image_prep |
string | MAY | image-prep lines a single-stage build splices before the dependency install (the language's own setup, e.g. fetching its package manager) | COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv |
Presence of runtime.build_cmd + runtime.artifact is the signal a container layer uses to choose a
multi-stage build. Interpreted languages omit them. runtime.image_prep is how a single-stage
language keeps its own toolchain setup out of the container layer — the container splices it verbatim,
so swapping languages never edits the container. A language that needs no prep omits it.
6. app.* — provided by the framework slot¶
| key | type | conformance | description | example |
|---|---|---|---|---|
app.port |
int | MUST | port the application process listens on | 8000 / 8080 |
app.start_command |
string | MUST | command that starts the app inside the image | uvicorn app.main:app --host 0.0.0.0 --port 8000 / /server |
app.healthcheck_path |
string | SHOULD | HTTP health endpoint path | /health / /healthz |
Framework-internal details (ASGI module path, Go entrypoint package, etc.) are private inputs that derive
app.start_command. They are not shared keys —app.start_commandcarries everything a downstream layer needs.
7. container.* and registry.* — provided by the container slot¶
container.*¶
| key | type | conformance | description | example |
|---|---|---|---|---|
container.runtime |
string | MUST | container tool id | docker / podman |
container.image_name |
string | MUST | unqualified image name (used for resource/labels) | myapp |
container.exposed_port |
int | MUST | port the container exposes (usually = app.port) |
8000 / 8080 |
container.build_recipe |
recipe | MUST | backend-agnostic build/push commands (tokens allowed) | see below |
registry.*¶
| key | type | conformance | description | example |
|---|---|---|---|---|
registry.host |
string | SHOULD | registry host/path prefix | registry.gitlab.com/myapp |
registry.image_name_base |
string | SHOULD | untagged reference = host/image_name |
registry.gitlab.com/myapp |
registry.* is SHOULD, not MUST: a build-and-run-locally container layer may not push. If
registry.image_name_base is absent, the ci slot must not assume a registry.
Example container.build_recipe:
- 'docker login -u {{SECRET:registry_user}} -p {{SECRET:registry}} ${registry.host}'
- 'docker build -t {{IMAGE}} .'
- 'docker push {{IMAGE}}'
8. ci.*, deploy.*, notify.*¶
ci.* — provided by the ci slot¶
| key | type | conformance | description | example |
|---|---|---|---|---|
ci.provider |
string | MUST | ci system id | gh-actions / gitlab-ci |
The ci slot is the terminal assembler (runs last; §10). It provides little because it consumes
recipes from the other slots and is the sole resolver of {{...}} tokens.
deploy.* — provided by the deploy slot¶
| key | type | conformance | description | example |
|---|---|---|---|---|
deploy.target |
string | MUST | deploy backend id | vps / kubernetes |
deploy.summary |
string | MUST | one-line human description; the only channel through which backend internals surface to others | kubernetes · ns/prod · 2 replicas |
deploy.apply_recipe |
recipe | MUST | backend-agnostic deploy commands (tokens allowed) | [ 'kubectl apply -k k8s/ -n prod', ... ] |
deploy.url |
string | SHOULD | live URL if known (may be empty) | https://app.example.com |
deploy.runtime_image |
string | MAY | OCI image the ci deploy job must run in (e.g. one carrying the deploy CLI); omitted when the ci runner already has the tools | bitnami/kubectl:latest |
notify.* — provided by the notify slot (optional slot)¶
| key | type | conformance | description | example |
|---|---|---|---|---|
notify.send_recipe |
recipe | MUST-if-present | backend-agnostic notification command (tokens allowed) | [ 'curl -sf -X POST ... {{SECRET:slack_webhook}}' ] |
9. Recipe tokens (deferred {{...}})¶
Resolved by the ci slot at its render. A non-ci layer that needs a runtime value emits one of these tokens; it must never emit CI-native syntax directly.
| token | the ci slot resolves it to | meaning |
|---|---|---|
{{IMAGE}} |
${registry.image_name_base} + : + native short SHA |
fully-qualified image ref for this run |
{{SHA}} |
native short commit SHA reference | e.g. $CI_COMMIT_SHORT_SHA, ${{ github.sha }} |
{{SECRET:purpose}} |
native masked-variable reference | e.g. $CI_REGISTRY_PASSWORD |
{{SECRET_FILE:purpose}} |
native file-type-variable path | e.g. $KUBE_CONFIG (a path) |
10. Secret purposes¶
Secret values never enter the context. The only cross-layer secret vocabulary is the set of
logical purposes below, referenced through the {{SECRET:...}} / {{SECRET_FILE:...}} tokens.
The engine compiles every purpose it observes into a provisioning checklist (INITREE_SECRETS.md).
| purpose | token form | meaning | typical native (GitLab / GitHub / cluster) |
|---|---|---|---|
registry |
{{SECRET:registry}} |
registry password/token (push) | $CI_REGISTRY_PASSWORD / ${{ secrets.REGISTRY_TOKEN }} |
registry_user |
{{SECRET:registry_user}} |
registry username (push) | $CI_REGISTRY_USER |
kubeconfig |
{{SECRET_FILE:kubeconfig}} |
cluster credentials (file) | $KUBE_CONFIG (path) |
slack_webhook |
{{SECRET:slack_webhook}} |
Slack incoming webhook URL | $SLACK_WEBHOOK |
A single logical purpose can map to multiple physical stores.
registryis a CI masked variable on the push side and a clusterimagePullSecret(a deploy-private input, e.g.deploy.k8s.pull_secret) on the pull side. One handle, two manifestations, rendered by two layers.
New purposes must be added to this table and follow the naming rule (§12).
11. Canonical injection points¶
An injects.into target must match either one of these canonical ids or an id a layer declares in its
own injection_points. The id is shared vocabulary; the format is owner-specific.
| id | declared by | typical file | formats seen | semantics |
|---|---|---|---|---|
runtime.dependencies |
language | pyproject.toml / go.mod / package.json |
toml-array / text-block / json-array |
additive dependency entries; owner runs a tidy/lock in finalize |
runtime.ignore |
language | .gitignore |
line |
additive ignore patterns |
Injection is the right tool only when the contributed content's format is backend-stable (a
dependency is a dependency). Content whose structure is backend-specific (CI job blocks) is passed
as a recipe capability instead and rendered by the ci slot — never injected.
12. Private (non-shared) keys¶
A layer's own inputs and internal state live under namespace.<backend>.* and are out of the shared
contract. They MUST NOT be consumed by other layers.
| example | owner | note |
|---|---|---|
deploy.k8s.namespace |
k8s | namespace; surfaces to others only via deploy.summary |
deploy.k8s.replicas |
k8s | replica count |
deploy.k8s.pull_secret |
k8s | cluster image-pull secret name |
deploy.vps.host |
vps-ssh | target host |
13. Per-slot conformance summary (the author checklist)¶
language — MUST provide runtime.language, runtime.version, runtime.base_image,
runtime.install_cmd. SHOULD provide runtime.test_cmd. MAY provide runtime.run_base_image,
runtime.build_cmd, runtime.artifact, runtime.image_prep. SHOULD own its dependency manifest and
declare the runtime.dependencies injection point.
framework — MUST provide app.port, app.start_command. SHOULD provide app.healthcheck_path.
MUST require a language slot (with one_of if it only supports some). Typically injects into
runtime.dependencies.
container — MUST provide container.runtime, container.image_name, container.exposed_port,
container.build_recipe. SHOULD provide registry.host, registry.image_name_base. MUST consume
runtime.base_image, runtime.install_cmd, app.start_command, app.port. MAY consume
runtime.build_cmd, runtime.artifact, runtime.run_base_image, runtime.image_prep.
ci — MUST provide ci.provider. MUST own its pipeline file and be the sole resolver of {{...}}
tokens. MUST consume container.build_recipe, deploy.apply_recipe, registry.image_name_base,
runtime.language, runtime.install_cmd, runtime.test_cmd. SHOULD consume notify.send_recipe
(optional). MAY consume deploy.runtime_image to set the deploy job's image. Renders the test job
from runtime.test_cmd and the language's native toolchain setup — never hardcoding either. Runs
last in topological order.
deploy — MUST provide deploy.target, deploy.summary, deploy.apply_recipe. SHOULD provide
deploy.url. MAY provide deploy.runtime_image. MUST consume container.exposed_port,
container.image_name, registry.image_name_base. Keeps backend specifics under deploy.<backend>.*.
notify (optional slot) — MUST provide notify.send_recipe if present. MAY consume
deploy.summary, deploy.url, project.name.
14. Naming rules¶
- Keys are
namespace.key, lowercasesnake_case. - A namespace names a capability domain, never a tool:
container.*, notdocker.*. - Backend-private keys:
namespace.<backend>.key— outside shared scope. - Recipe tokens are
{{UPPER}}or{{UPPER:arg}}. - Secret purposes are lowercase
snake_caseand listed in §10.
15. Reserved namespaces¶
Claimed for future shared capabilities — do not use these for private keys or unrelated purposes:
db.*, cache.*, queue.*, observability.*, test.*, env.*, docs.*, and secrets.*
(reserved as a non-bus namespace — secrets flow only as tokens, never as context values).
16. Versioning & stability policy¶
This registry is versioned (v1). A layer declares the registry version it targets; the engine warns on a mismatch.
Non-breaking (minor bump): - add a new MAY/SHOULD key, or a new namespace; - add a secret purpose, a recipe token, or a canonical injection point.
Breaking (major bump): - add a MUST key to an existing slot, or raise a key from SHOULD/MAY to MUST; - rename, remove, or retype any key; - change a token's or purpose's meaning.
Deprecation: mark a key deprecated: <version>, keep it functional for one major version, and
document its replacement.