Blueprints
A blueprint is a scene authored as data. One JSON file (or a directory with sidecar HTML and markdown) describes everything that makes a scene a scene: identity, assets, recurring jobs, evaluable tasks, who has access. Apply it and the scene appears. Export a live scene and you get the same shape back, ready to fork.
Blueprints are the substrate for provider scenes, the catalog at /blueprints, and any scene you want to version, share, or evolve outside the iOS app. A blueprint can even carry credentials — as references into a vault, never as values — so applying one can provision connected accounts with no OAuth flow and no human in the loop.
What's in a blueprint
{
"version": 1,
"id": "scn_my_world",
"slug": "my-world",
"name": "My World",
"icon": "globe",
"tint": "#4C8BF5",
"settings": {
"description": "Built from a blueprint."
},
"assets": [/* scene-owned content */],
"linkedAssets": [/* foreign rows pinned in place */],
"schedules": [/* recurring agent tasks */],
"tasks": [/* evaluable scenarios */],
"members": [{ "email": "you@example.com", "role": "owner" }],
"secrets": {/* credential refs — see below */},
"scenes": [/* child scenes, recursive */]
}
Two things are worth understanding deeply.
Identity sits at the top, settings sit insettings. Identity (id, slug, name, icon, tint) is load-bearing for cross-references and the visual header. Configurable state (description, feature flags, welcome suggestions, notification preferences) lives in settings — same as the iOS settings sheet. The applier only writes columns the blueprint declares, so anything you set in iOS that the blueprint doesn't carry stays intact.
assets[] versus linkedAssets[] is the load-bearing split. Scene-owned content goes in assets[] — the blueprint creates these rows and owns their contents. Foreign rows go in linkedAssets[] — account chips, GitHub repos, Slack channels, SAP entities. Their DB ids stay stable across applies; the blueprint pins them, can optionally edit their content in place, but never forks them into duplicate rows.
Apply, export, merge
# Apply a blueprint
daslab blueprint apply ./my-world.blueprint.json
# Export a live scene as a blueprint
daslab blueprint export <sceneId> --children --extract-html --out=./my-world.blueprint.json
# Apply with conflict-detection
daslab blueprint apply ./my-world.blueprint.json --strict
Apply is idempotent — same blueprint, same input, same output. Deterministic IDs from (sceneId, type, externalId) mean re-running upserts the same rows.
Export captures the scene's current head commit and tree hash in meta.exported_from. The next apply reads those, compares to the scene's current state, and runs a real three-way merge per asset: clean fast-forward, behind (live moved ahead, your apply would clobber), diverged (history rewritten), or conflict (both sides changed the same row differently). --strict refuses on conflict; default warns and proceeds.
This is git semantics for scenes. Branch a scene, hack on it in your editor, merge back safely.
Credentials are references, never values
A blueprint can provision credentialed accounts headlessly. The credential is never a plaintext field — it's a reference in the secrets block:
"assets": [
{ "id": "brave1", "type": "brave/account", "name": "Brave", "fields": {} }
],
"secrets": {
"brave/account:brave1": { "from": "kern:BRAVE_ACCT" }
}
At apply time the reference resolves — kern:NAME from an age-encrypted vault, env:NAME from the server environment — and the credential fields merge into the account asset. Export runs the same split in reverse: secret fields (api_key, access_token, client_secret, …) are sealed out of the JSON, leaving only refs. A blueprint with credentials is safe to commit, share, and fork by construction.
Providers with a single shared key can skip the blueprint entirely: an envFallback declaration on the provider exposes a virtual default account straight from the server environment. Use a secrets ref when you want a real, pinned account — multiple accounts, or credentials that aren't one env var.
Push a blueprint without touching git
daslab blueprint push scene.blueprint.json
# supply a credential that lives only on your machine:
daslab blueprint push scene.blueprint.json \
--secret "brave/account:brave1:api_key=sk-…"
# print the request, send nothing:
daslab blueprint push scene.blueprint.json --dry-run
The CLI sends the blueprint — plus any --secret overlays — over TLS to the apply endpoint under your token; the server applies it and adds you as owner of each root scene. A credential reaches the server one of two ways: already sealed in the server's vault (refs resolve server-side), or inline via --secret — encrypted in transit, never in git.
Edit-in-place loop
Most scenes mix assets you author (notes, dashboards, view components) with rows that live outside the blueprint (account chips, the underlying SAP/Slack/GitHub entities). The format handles both cleanly.
# 1. Branch
daslab blueprint export <sceneId> --extract-html --out=./mine.blueprint.json
# 2. Edit
$EDITOR ./mine.blueprint.json
$EDITOR ./mine/<asset>.html
# 3. Merge back, refusing to clobber drifted edits
daslab blueprint apply ./mine.blueprint.json --strict
The big content fields — cockpit HTML, markdown notes, code snippets — extract into adjacent files via $file references on --extract-html. The blueprint stays a small JSON file; the HTML becomes a real HTML file you can edit with syntax highlighting. Re-apply inlines them transparently.
For provider-managed rows (a SAP product, a GitHub repo, a Slack channel), the export puts them in linkedAssets[] with the row's current name and fields. Edit those fields in the blueprint, re-apply, and the existing row's content is updated — the DB id stays the same. No fork, no duplicate.
One file or a directory — both apply the same
A blueprint is either a single file or a directory. Both work identically with the applier.
- Single file — everything inline. Fits in a gist. Easy to share via paste.
- Directory —
<name>.blueprint.jsonplus a sidecar folder containing referenced HTML, markdown, code. Package as a tarball or a git submodule. Helm-chart shape.
Use either as your content size dictates — the applier doesn't care.
What a blueprint is not
- Not a configuration management tool. It mutates the database, but only for the scene tree it describes. It doesn't manage server state, user accounts outside the scene, or anything cross-scene.
- Not a build artifact. Source of truth is the JSON file; the applier writes to the DB, but the JSON is the spec.
- Not a backup format. It captures the structure of a scene plus its scene-owned content. Job histories, conversation traces, scene annotations, and runtime state are not included — those live in their own systems with their own retention rules.
- Not a code substitute. Blueprints declare; they don't compute. Schedules and tasks reference prompts the agent reads at runtime, but the format itself has no functions, loops, or conditionals.
Where blueprints come from
We ship a small set of high-quality starting points — see /blueprints. Provider scenes are blueprints too: a provider's identity, asset types, and tool definitions packaged as a scene that the registry loads at boot.
Anything you build in the iOS app is exportable as a blueprint. Anything someone else built is forkable as a blueprint.
Reference
The full v1 spec, every field, every semantic — scenecast/docs/scene-blueprint.md.
What's next
- Get started: your first blueprint — a 5-minute walkthrough.
- Provider scenes — providers authored as blueprints.
- Browse blueprints — what's shipping today.
- Scenes — what a deployed blueprint becomes.
Updated 2026-07-18