Guide Platform 14 min read August 2026

Building Blocks, Not Bespoke Scripts

Everything that lands on your box — a Postgres, a coordination daemon, an AI employee, a scheduled task worker — arrives the same way: a short, declarative workflow made of named step types, executed on the box by a small on-box engine. Not a thousand-line install script per product. A vocabulary of maybe seventeen building blocks, one honest bash step for the long tail, and a strict rule about what earns a name.

The engine, in one section (answer first)

The workflow step engine is how anything gets set up on your box. An App Store listing declares typed workflows — install, status, uninstall, update — each a list of steps. The platform forwards those steps, unchanged, to a lightweight manager running on your box, which executes them in order: install a package, clone a repo, start a container, poll a health endpoint. The same engine that installs an app also runs your scheduled agent workers and stands up your AI staff. One delivery mechanism, every product.

A step is a small typed record — a type, a name, and a config block — and that is the whole authoring surface:

{ "type": "git_clone", "name": "fetch_app",
  "git_clone_config": { "repo": "https://github.com/acme/app.git", "ref": "v2.1.0", "dest": "/opt/app" } }

The step-type philosophy: what earns a name

The design rule that keeps the engine small: a step type earns its place only when the operation is both common and config-bearing. Common — it recurs across many listings and workflows, not one. Config-bearing — authors genuinely inject variables into it, so a named, typed block is more useful than an ad-hoc shell line. Everything else is a script step: plain bash, the workhorse that covers the long tail.

OperationStep type?Why
Clone a repo with a ref, a destination, and an auth tokenYes: git_cloneRecurs everywhere; the token demands safe handling a bash line gets wrong
Install a working Docker on any distroYes: docker_installCommon, and painfully distro-specific — exactly what a curated block should absorb
Wait until a service is actually readyYes: health_checkEveryone needs it; typed config (http status, tcp port, log regex) beats sleep 30
Bootstrap one specific tool's runtime imageNo: scriptOne tool's one CLI call; wrapping it adds a name, not value
Run a coding agent headlessNo: script (today)A bash step calling the harness CLI — a first-class step is a candidate, not a given
Why this restraint matters to you as an operator: a step type is a promise. It is documented, portable across boxes, idempotent, and secret-safe — forever. A platform that mints a step per vendor or per command accumulates hundreds of half-maintained promises. A platform that holds the line has a vocabulary you can learn in an afternoon and trust in a year. The step type is a name: it tells an author "you can do this thing here, safely" — a curated building block, not a wrapper for every possible command.

The vocabulary

The current building blocks, grouped by what they are for:

StepWhat it does
scriptRun bash. The workhorse — anything without its own block.
package_installInstall packages; detects apt / apk / yum / dnf itself.
git_cloneClone a repo (ref, depth, dest); token via the secret env channel, never the command line.
file_download / file_uploadFetch a URL to a path; copy files into place.
docker_installA usable Docker (engine + compose v2) on any supported distro, with a pinned fallback.
docker_run / build / compose / network / volume / loginThe container family; registry passwords ride stdin, never argv.
object_storageGet / put / sync against any s3-compatible store, credentials off-band.
health_checkPoll until genuinely ready: an HTTP status, a TCP port, or a log-line regex.
environment_setup / wait / notificationSet variables for later steps; pause; POST a webhook (Slack and friends).

Notice what is not here: no "install claws" step, no "set up Postgres" step, no per-product anything. Products compose the vocabulary; the vocabulary does not grow a word per product.

The blank-canvas rule

The box assumes nothing, and every workflow provisions its own prerequisites. A fresh box is deliberately minimal — effectively an OS and curl. A workflow that needs git runs a package_install for git first. A workflow that needs Docker declares docker_install. Nothing is pre-baked into the box image on the theory that some workflow might want it someday.

This sounds like a small discipline; it is actually what makes the fleet manageable. Because no listing depends on box-baked state, any workflow runs on any box — new, old, big, small — and two apps never fight over a shared, half-owned dependency. Your box stays yours: what is on it is exactly the sum of what you installed, each piece owned by the workflow that put it there and removed by the uninstall workflow that shreds it.

A worked install: an AI employee, step by step

The claw listing is the best showcase because it mixes both kinds of step. The named blocks do the common, config-bearing work; bash script steps call the product's own CLI for the product-specific tail:

# The claw install workflow (abridged) — building blocks + bash tail
script          curl .../install.sh | bash        # the tool's own installer
docker_install  engine + compose v2               # named block: distro-agnostic
script          claws image bootstrap --yes       # one tool's CLI = bash, not a step type
script          claws apply --secrets-dir=/etc/claws/secrets
script          claws start default/
health_check    claws agent ping, retry 5 x 10s   # named block: typed readiness

The is a typed listing input, substituted at install time. The same pattern delivers the coordination daemon (docker_installdocker_runhealth_check on its health endpoint) and every infra app in the store. Once you can read one workflow, you can read them all — that is the point of a vocabulary.

Secrets and safety, engine-enforced

The engine, not the author, carries the security invariants. Secret inputs are delivered off-band to root-owned files created under a restrictive umask; steps reference them as files or environment variables. A database password, a git token, a model API key: none of them appear in a step's command line, the workflow record, or a log — and an uninstall shreds them. Steps that take credentials (git_clone, docker_login, object_storage) are built to accept them only through that channel, which is precisely the kind of correctness a named building block can guarantee and an ad-hoc bash line reliably fumbles.

What earns a name next

The vocabulary is allowed to grow — through the same gate. The candidate on the table now is an agent step: "run a coding agent with this task," with typed config for the task, the harness, a budget, and autonomy limits. Today that is a script step (cogs run their harness exactly that way, with the task passed through the environment so it cannot be injected), and it stays a script step until running agents is so common, and its config surface so real, that the promotion pays for its permanent upkeep. If it crosses that bar — and as agent workers multiply, it plausibly will — it becomes the vocabulary's first agent-native word. If not, bash was already enough. That is the philosophy working as intended, in both directions: for the harness side of that question, see why your worker should not be locked to one brain.

Install something on your box → Deploy from the App Store →