Baselines
A baseline is a whole product's structure and standards, packaged as code. Where a Stack carries golden paths and presets carry platform rules, a baseline carries everything a working product needs: its repository layout, its components and their golden paths, its CI, its environments. orun rebuilds it for a new owner, and because the build records its provenance, the product can take later releases of the baseline by three-way merge instead of becoming a fork.
A baseline is a production-shaped product repository that can be rebuilt
for a new owner: a new GitHub organisation, a new cloud account, a new product
name. Where a template gives you files, a baseline gives you a live product on
stage and prod with its own CI, its own migrations, and its own catalog,
in about an hour.
Baselines sit on top of two things orun already has: the
scaffold engine that places a kind: Blueprint into a
directory, and Orunbase's workspaces, integrations, and secrets. The
guide walks the flow
end to end; this page explains the pieces.
Three documents, one build
registry row blueprint card build document
baselines.yaml ──▶ blueprint.yaml ──▶ repo-blueprint.yaml
(identity, tag, (inputs, requires, (modules, phases,
tier, manifestPath) secrets, bootstrap) hooks, gates)
│ │ │
└────────── what can be built ┴ what the operator supplies ┴ what orun places
The registry row answers what can be built and by whom. It lives in the
platform's baseline registry and carries the id the console routes on, the
sourceRepo, the pinned tag, the tier (free or paid), visibility
(public, unlisted, private), the measured expectedMinutes, the providers it
requires, and manifestPath, the path of the card inside the source
repository. The Orunbase-maintained rows are a file in Orunbase's own
orun-cloud repository, infra/baselines-registry/baselines.yaml, reconciled
into the registry on every merge. Rows an account registers itself are written
by orun baseline register.
The blueprint card answers what the operator supplies. It is fetched
from sourceRepo at the registry's tag, never from a branch, and it declares:
spec.requires.integrations, the providers that must be connected, and the API key role a build needs (admin, because it writes secrets);spec.inputs, each with apatternits value is checked against before anything starts, and either asked, derived from another input (derive: "https://api.{productdomain}"), or filled from the chosen repository (from: repo.name) or the workspace (from: workspace);spec.secrets, a preview of the keys the build will mint from the workspace's provider connections, so the console can show them read-only;spec.bootstrap.blueprint, the path of the build document, andspec.verify.urls, what a finished build must answer.
The card carries no tag. It is fetched at the tag the registry pins, so a tag inside it could only ever be a second, rotting copy.
The build document answers what orun places. It is an ordinary
kind: Blueprint: sources, modules with template, copy, or consume
placement, declared dependsOn edges, and phases with per-phase hooks. The
engine places modules in dependency order, phase by phase, and gates the
output: every generated component.yaml must parse, and the tree must pass
orun validate and a full orun plan compile (no plan file written) before the build reports success.
Blueprint-driven and shell-layer baselines
A baseline is blueprint-driven when its build document declares every
phase and hook, and orun runs it with no model attached. cirrus, lumen,
and multi-tenant-saas are blueprint-driven: their registry rows declare
manifestPath and nothing else, and the platform sandbox runs
orun agent serve --driver bootstrap, which in turn runs
orun baseline new <id>@<tag> --local --run-hooks --resume, adding --until
or --phase for one stage of a two-stage build.
A baseline with a shell layer additionally declares a briefPath (an
agent brief) and an umbrellaPath (the workflow the brief runs). A row
declares both or neither; one of the two is a row someone edited halfway.
stratus is the current example.
Phases, hooks, and derived state
A phase is a placement barrier with hooks attached. All of phase N is placed before phase N+1 begins, and no dependency edge may point forward across a phase boundary. A phase can state what must be true before it runs (preconditions) and can wait without failing when a condition it depends on is not yet met, such as a pull request's CI.
Hooks are declared argv, never shell, run after placement, outside
the render sandbox, and only with --run-hooks. A hook either runs a command
or calls a typed action from a closed registry: opening a task, ensuring an
epic and milestone, minting a brokered secret, reconciling a workspace
connection, opening and landing a pull request, and the like. A bad
parameter is a parse error before the build starts.
Phase state is derived, not recorded: a fresh container can look at the
tree and the platform and answer which phases are done. That is what makes
--resume safe, and what lets the console retry a stopped build from the
setup it kept. The one thing the tree cannot tell is a phase that landed and
then failed to deploy, so a retry names it: --redo <phase> places it again.
Two-stage builds
orun.work builds an app in two stages. The scaffold stage needs GitHub
only: the code placed and branded, the repository created, the first pull
request landed. The live stage needs the card's providers: credentials
reconciled, deployed, probed. An account with no Cloudflare connection gets a
code-only app by stopping after scaffold, and a later build runs the rest. The
card's spec.stages names the phase each stage ends at; the bootstrap door
resolves a stage to that phase and the runner receives it as
ORUN_BASELINE_UNTIL (stop after it) or ORUN_BASELINE_PHASE (run only it).
Both compose with --resume, so a runner restarted mid-stage resumes like a
full build.
A value only the live stage can have, such as the workers.dev subdomain of
the Cloudflare account, is declared lateBound: true in the build document.
The scaffold stage writes its default as a placeholder. The live stage must
supply the value: a resumed run takes it over the recorded placeholder, and any
run that goes past the stop without it is refused before anything is placed.
See late-bound inputs.
Where a build runs
--via-platform | --local | |
|---|---|---|
| Runs on | A sandbox the platform provisions (4 vCPU, 8 GiB, open egress) | Your machine |
| Writes into | A repository you linked in the console | --out <dir> |
| Credentials | A time-boxed admin grant and brokered provider tokens | Your session or ORUN_TOKEN, your gh login |
| Watch it | The console: Overview and Agents | Your terminal, --progress json for machines |
| Hooks | Always on | Only with --run-hooks |
| Concurrency | One build per repository, enforced by a lease | Yours to manage |
Both paths gate on readiness: every provider the card requires must be
connected in the workspace, or nothing starts. A --local build that --until
stops skips the gate: the stage it stops after may need none of them, and the
platform's door has already checked the phases that will run. Both resolve a stale @tag
forward to what the registry publishes now, and say so.
Provenance and upgrade
Every build writes .orun/provenance.lock into the product: the blueprint
digest, each source digest, a secret-free hash of the inputs, and the mode
and target of every module. When the registry publishes a newer tag,
orun new upgrade re-renders it against the lock and three-way merges the
result. Files the baseline owns and you did not touch are updated; a file you
edited is a conflict and is never overwritten. A product built from a
baseline is upgradable, not a permanent fork.
Publishing
Publishing a baseline release is two steps in a fixed order:
- Push the git tag in the baseline's own repository.
- Move the registry's pin:
orun baseline publish <id> <tag>for a baseline your account registered, or a pull request onbaselines.yamlfor an Orunbase-maintained one.
The platform proves the tag before the registry moves: it must be a tag and not a branch, the files a build reads first must be present in it, and the card must parse. A tag that fails leaves the registry untouched and prints the reason. Getting the order wrong costs a refused publish, never a broken product.
Related
- Create a workspace and build it from a baseline
orun baseline— command referenceorun new— the scaffold engine and the Blueprint grammar- Scaffolding design notes