Custom Blueprints¶
Synthkit loads blueprints from two sources at startup:
- Bundled blueprints — the
blueprints/directory baked into the binary (always present). - Custom blueprints — staged to the
BLUEPRINT_DATA_DIRvolume by upload or git fetch, then applied on restart.
Custom blueprints use the same YAML format as the bundled set. They are managed via the control-plane API and optionally via git sources that poll for updates.
Control-plane upload¶
The simplest way to add a blueprint is POST /control/blueprints/custom with a JSON body carrying the namespace, name, and blueprint YAML. The server validates the YAML and the name, writes it to the staging area, and returns {"status":"staged"} (an invalid blueprint or bad name returns 400).
POST /control/blueprints/custom
Content-Type: application/json
{
"namespace": "mine",
"name": "my-blueprint",
"yaml": "name: my-custom-blueprint\n..."
}
To check a blueprint without staging it, POST /control/blueprints/validate with body {"yaml": "..."} — it validates one blueprint in isolation and returns the validation result. (It cannot detect cross-blueprint substrate-identity collisions; those are surfaced by GET /control/diagnostics and enforced at restart.)
The blueprint is staged immediately but does not take effect until restart. The control-plane UI shows a "restart to apply" banner when staged blueprints differ from what is running. See Control Plane for the full /control/blueprints API.
To remove a staged upload: DELETE /control/blueprints/custom?name=<namespace>/<name>.
Git sources¶
A git source is a repository (HTTPS only) that synthkit fetches YAML blueprints from on demand. Configure one via POST /control/blueprints/sources or through the control-plane UI:
{
"id": "my-bp-repo",
"name": "My blueprint repo",
"namespace": "mine",
"url": "https://github.com/example/synthkit-blueprints",
"ref": "refs/heads/main",
"subpath": "blueprints",
"token_env_var": "MY_REPO_TOKEN"
}
| Field | Description |
|---|---|
id |
Stable slug, also the on-disk directory name under git/. |
name |
Human-readable label shown in the UI. |
namespace |
Prefix applied to every blueprint name from this source (namespace/blueprint-name). |
url |
Repository URL (HTTPS). SSH is not supported. |
ref |
Git ref to fetch from, e.g. refs/heads/main or refs/tags/v1.0. |
subpath |
Directory within the repo holding *.yaml files ("" = repo root). |
token_env_var |
Name of an environment variable holding the HTTPS PAT for private repos. Empty = public repo. The token itself never leaves the server; only the variable name is persisted. |
Trigger a fetch: POST /control/blueprints/sources/fetch?id=<id>. Synthkit fetches the remote HEAD SHA, compares it to the last applied SHA, and downloads updated YAML blobs only when the SHA has changed.
Polling for "update available"¶
Set GIT_POLL_INTERVAL (in seconds) to enable background polling. The poller checks HEAD for each configured source on the interval and marks sources as "update available" when a new SHA is found. Polling updates only the cached SHA — it does not re-fetch blobs automatically. Use POST /control/blueprints/sources/fetch?id=<id> to apply the update, then restart.
A default fallback PAT for all sources can be set via GIT_TOKEN; individual sources override with token_env_var. See Configuration.
Staging layout¶
All custom blueprints are staged under BLUEPRINT_DATA_DIR (default ./data/blueprints):
data/blueprints/
├── custom/ # uploaded blueprints
│ └── mine__my-blueprint.yaml
├── git/
│ └── my-bp-repo/ # fetched from the git source with id="my-bp-repo"
│ └── production.yaml
└── .boot-manifest.json # records what was loaded at last startup
custom/— uploads, named<namespace>__<name>.yaml.git/<id>/— one directory per configured source, containing the fetched*.yamlblobs..boot-manifest.json— written by the runner at startup; records which blueprints (and git source SHAs) were applied. The control plane diffs this against the current staged state to drive the "restart to apply" banner.
Namespacing¶
Every custom blueprint is prefixed with its namespace: {namespace}/{name}. This prevents collision between blueprints from different sources. The namespace:
- Is a URL-safe slug (alphanumeric + hyphens).
- Cannot contain
/or__(the upload separator). - Is applied at load time, not inside the YAML file — the file's
name:field is the bare blueprint name; the namespace wraps it.
Blueprint identity (the determinism seed root) includes the namespaced name, so blueprints from different namespaces with the same bare name produce distinct identities and series.
Collision handling¶
At restart, the loader resolves all staged blueprints. If two blueprints from different sources (or a custom and a bundled blueprint) claim the same substrate identity — the same cluster name, AWS account ID + DB name, or network-topology instance — the collision is logged as a diagnostic and the colliding blueprint is skipped. The diagnostic appears in GET /control/diagnostics. Fix the collision by renaming the conflicting resource in one of the blueprints, then re-stage and restart.
Restart to apply¶
Custom and git blueprints take effect only on restart. The runner loads all staged blueprints at startup, writes the boot manifest, and runs with that fixed set for its lifetime. There is no hot-reload.
The control-plane endpoint GET /control/blueprints/pending returns the diff between the boot manifest and the current staged state:
changed lists git sources whose latest fetched SHA differs from the applied SHA. restart: true means a restart is needed to apply pending changes.
skcapture and skforge¶
For blueprints derived from a real Kubernetes environment, the skcapture + skforge tools capture live cluster state and generate a blueprint skeleton from it. See Tools for the full capture→forge workflow.
In brief: skcapture runs as a kubectl ephemeral container and exports an age-encrypted inventory snapshot; skforge decrypts it, maps the captured workloads to synthkit construct declarations, and emits an LLM prompt that produces a draft blueprint. The output is a starting point — review and adjust the generated YAML before staging it.