Skip to main content

Blueprints

A recipe tells you what to do. A blueprint does it.

A blueprint is a section of a site the instance can apply: a content model, a folder tree, sample pages, the parameters that make it yours, and a promise about what exists afterwards. One call produces a working, verifiable starting point, which is the difference between "here are eleven steps" and "here is a blog".

shio blueprints # what this instance carries
shio blueprints blog # one package's parameters
shio apply --blueprint blog --site mysite --param title="My Blog"

Over MCP that is shio_blueprint; over REST:

GET /api/v2/agent/blueprints
GET /api/v2/agent/blueprints/{name}
POST /api/v2/agent/blueprints/{name}/apply

Reading the catalogue is deliberately cheap: a name, a version, what applying it gives you, and the parameter names. The full schema of the one you pick is a second call, so choosing between six packages does not cost six schemas.


The first-party set

BlueprintGives you
blogAn Article type, a /blog section, published samples
docsA documentation section with a navigable tree
product-catalogA catalogue type and a listing
marketing-siteA section-composed site, plus the Next.js front end
landingA single section-composed page, plus the front end
nextjs-starterThe Next.js starter project itself, as a package

Three of them carry front-end files: nextjs-starter gets the starter tree, and marketing-site and landing get the same tree plus a small section-aware overlay. The other three deliberately do not: they compose no sections, so the overlay would ship a dependency they never import.


Where more packages come from

Those six ship with the instance. A marketplace catalogue adds more, and it is off until an operator turns it on:

shio.marketplace.enabled=true
shio.marketplace.url=https://shio.viglet.org/marketplace/marketplace.json
shio.marketplace.timeout-millis=5000

With it off, listing the marketplace still answers — with the six built-ins and a source of builtin-only. That is deliberate: "the feature is off" and "this build has no packages" are different facts, and the listing says which. When a catalogue is configured but cannot be reached, the source reads unreachable and carries the reason, so a list that quietly shrank cannot be mistaken for a catalogue that shrank.

From a terminal

shio marketplace list # what is available, and where the list came from
shio marketplace show shio-blueprint-blog # one entry, with its README
shio marketplace install shio-blueprint-blog
shio apply --blueprint shio-blueprint-blog --site mysite

Installing is not applying. install downloads the package and puts it where the registry can see it, and stops there — applying it to a site stays shio apply --blueprint. The separation is the point: you can read what you installed before anything writes to your content. install --dry-run goes further and does everything except the move into place, so an archive that is malformed, or that disagrees with the catalogue about its own name, is caught before it lands.

From the console and from an agent

The console has the same catalogue under Admin → Marketplace. An agent reads it with the shio_marketplace MCP tool, which lists and describes — it does not install. That is a deliberate boundary, not an omission: installing a stranger's package is the operator's act, so it stays with a credential the operator holds.

Who may install

Browsing is open to anyone signed in. Installing and uninstalling require an administrator, over every surface — the console, the CLI and the REST API alike. The reasoning is that merely re-reading the package directory already needs an administrator, because it changes what every later apply can do; installing is that act plus a download, so it cannot be gated more weakly than what it contains.

Uninstall only removes a package that came from a catalogue. The six built-ins are part of the instance, so "uninstalled" would be a claim the next restart contradicts.


Applying one

apply is a two-step by default, because looking before writing is the whole safety story:

shio apply --blueprint marketing-site --site mysite --dry-run

A plan writes nothing and needs no write transaction: it can run on a read-only credential or a replica. It declares what it would create: the site, each post type with the fields it would carry, every folder and every page. Then:

shio apply --blueprint marketing-site --site mysite --create-site \
--param title="Northwind" --param tagline="Tools that last"

Everything lands as drafts. Publishing is yours.

FlagEffect
--param k=vRepeatable
--params <file>The same values as JSON
--site <site>Which site to apply into, always the caller's choice
--create-siteMake the site if it is not there
--merge-post-typesAdd fields an existing type is missing
--forceOverwrite carried files that already exist on disk

The rules that protect you

A blueprint is code you did not write, applied to content you care about. Seven rules follow from that, and each is a refusal rather than a convention:

  • A package may not name its own site, and may not prune. The site is always the caller's; a site key inside a package is refused at load, not ignored: an ignored key looks like it worked. Prune's blast radius depends on what the author forgot to mention, and that author is not you.
  • The model is provisioned, never overwritten. A post type that already exists is left exactly as it is (including a field a curator added) and the response names the fields it lacks so you can decide. --merge-post-types adds them; nothing silently replaces your model.
  • Substitution is {{name}} and nothing else. No conditionals, no expressions: the same parameters always produce the same site. site is reserved and always available. An unknown parameter is a teaching error, never quietly dropped — except inside a template field (HTML, CSS, JAVASCRIPT), where an unknown name belongs to the page renderer and passes through untouched. A package that ships a layout writes {{post.title}} there and means the renderer's, not yours; the trade is that a mistyped parameter inside a template is not caught, because in that field an undeclared name is the normal case.
  • A malformed package is the package's fault, and says so. Everything is validated when the package loads: a missing referenced file, a path that escapes the package, an unknown manifest key, a placeholder with no declared parameter, a promise the content never fulfils. That is a 409, not a 400: nothing you sent caused it.
  • One package's failure is not the catalogue's. A package you added to your own blueprint directory that will not load is refused on its own and reported in GET /api/v2/agent/diagnostics, with the reason. The packages that shipped with the instance keep working, so a package somebody else wrote cannot take your catalogue down. It is also how you validate one before publishing it: point an instance at the directory, reload, and read the refusal.
  • A package cannot come from a URL. The only sources are the instance's own classpath and one directory an operator names. Whoever set that path is vouching for what is in it, and there is no fetch-by-URL — a blueprint writes pages under your credentials, so moving that decision from a person to a network is the shape of every supply-chain incident. The shio-blueprint- name prefix is reserved for packages that did not ship with the instance, so the name tells you where something came from.
  • The server never writes a project file. Carried files come back on the response and the CLI writes them, never over an existing file without --force. A client that cannot write files is told what it has not got, and each row names the placeholders standing in that file, read from the bytes, with the resolved value beside it.

What a package looks like

A directory with a blueprint.json and the files it references:

blog/
├── blueprint.json
├── post-types/Article.json # the same bytes `shio pull` writes
├── content/blog.json # a desired-state document
└── files/… # optional front-end sources

Nothing in that format is a new serialization: the post types are what the CLI already writes, the content is a desired-state document, and verify is the promise the package keeps. A blueprint is therefore something you can read, diff and author with the tools you already have.

blueprint.json declares the name, the version, the parameters with their defaults, the files, and a verify block naming what must exist when the apply is done, which is what makes "it worked" checkable instead of asserted.


PageDescription
The shio CLIblueprints, apply --blueprint, init --blueprint
The Agent SurfaceThe desired-state document a package compiles into
MCP Servershio_blueprint and the shio://blueprints resources
Content ModelingThe post types a package provisions