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": "[email protected]", "role": "owner" }],
"secrets": {/* credential refs, see below */},
"scenes": [/* child scenes, recursive */]
}
Two things are worth understanding deeply.
Identity sits at the top, settings sit in settings. 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[], where 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 a reference in the secrets block, never a plaintext field:
"assets": [
{ "id": "brave1", "type": "brave/account", "name": "Brave", "fields": {} }
],
"secrets": {
"brave/account:brave1": { "from": "kern:BRAVE_ACCT" }
}
At apply time the reference resolves, with kern:NAME reading from an age-encrypted vault and 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 and 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 while 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 at /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 and 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-08-03