Commands

Lifecycle commands for build, release, run, and post-start stages. Each value is a string (shorthand) or an object with command, optional timeout, and optional capture.

Platforms execute well-known commands in this order: build → release → start → (bootstrap on user request). The seed, test, and bootstrap commands are invoked on demand, not as part of the standard deploy lifecycle.

Stage Purpose When
build Install dependencies, compile — artifact prepare Every deploy
install Prepare from source (deps, codegen) — the source-mode pair of build Source-mode launches, on demand. See Source-mode commands below.
release Migrations, cache clear, asset compilation Every deploy, after build
start Start the application — artifact run Every deploy, after release
dev Start the application from source — the source-mode pair of start Source-mode launches; preferred over start when running from source, ignored by artifact providers. See Source-mode commands below.
bootstrap Post-start setup that must run against a running component: create the first admin user, generate an initial invite link, write runtime config that depends on the deploy URL On demand after start (user-invoked, re-runnable, non-deploy-failing). See Bootstrap stage below.
seed Seed the database with initial data On demand (first deploy or explicit trigger)
test Run the test suite On demand (CI or explicit trigger)

Additional named commands are allowed and invoked on demand.

commands:
  build: "npm install"
  release: "npx prisma migrate deploy"
  start: "node server.js"
  test: "npm test"

With timeout:

commands:
  release:
    command: "npx prisma migrate deploy"
    timeout: "5m"

Source-mode commands

Lifecycle commands run in the context of the built artifactstart is what the production image runs, build produces that artifact. Running the app from source on a developer machine is a different execution context: the artifact's entrypoint may be a compiled binary absent from the source tree, and the source tree has dev affordances (hot reload, unbundled assets) the artifact doesn't. Execution mode — source vs. artifact — is a distinct axis from deployment environment (DESIGN.md D-37 / L-3): "dev" is a mode, not an environment.

Two well-known keys name the source-mode commands, and one optional component field names where they run:

Key Pairs with Role Example
install build source-mode prepare "bun install"
dev start source-mode run "bun run dev"
source (component field) working directory for install / dev (defaults to build.context, then repo root) "./apps/api"

Only prepare and run are mode-aware. release, bootstrap, seed, and test are mode-invariant — the same command runs whether you launch from source or from the artifact. A path or binary that genuinely differs by mode (a cache dir, a repo-local CLI) belongs in storage: / env / the provider's PATH, not a separate command.

Resolution (per component). A provider selects one mode for the launch (launchfile dev → source, launchfile up → artifact) and resolves each component:

  • Run, by precedence dev > image > start: a dev command runs the component from source; an image keeps it in artifact mode unless dev overrides it; a bare start runs from source only when there is no image — so a prebuilt image is never replaced by a start that assumes the image's internals (fallbacks must be detectably safe).
  • Prepare: source mode runs install ?? build, on demand (first launch or a detected dependency/lockfile change), not on every run; artifact mode runs build (or pulls image).

Providers that execute the built artifact (Docker, Kubernetes, cloud platforms) ignore install and dev. The values are ordinary command values — string shorthand or the expanded form with timeout and capture.

A Launchfile that declares both is launchable in either mode from the same file:

components:
  api:
    source: ./apps/api                  # cwd for install/dev
    image: ghcr.io/acme/api:1.4         # artifact run (ENTRYPOINT = compiled binary)
    commands:
      install: "bun install"            # source prepare
      dev: "bun src/index.ts --port $PORT"   # source run
      bootstrap: "api-cli create-admin --url $app.url"  # mode-invariant

launchfile up pulls and runs the image; launchfile dev runs bun install (on demand) then bun src/index.ts from ./apps/api, ignoring the image. bootstrap is identical in both modes. Source mode is a deliberately narrow carve-out: it expresses that the same lifecycle intent needs a different command line from source than from the artifact — it is not an environment-override mechanism (staging vs. production config remains an orchestrator concern, DESIGN.md L-3).

Bootstrap stage

The bootstrap stage is for imperative post-start setup that can only run against a running component. Typical uses:

  • Creating the first admin user via a CLI that's only available inside the container
  • Generating a one-time invite link or password reset token the user needs to see
  • Writing runtime configuration whose values depend on the deployment's public URL (via $app.url) and must be picked up by an already-running process

Unlike release, which runs before start in an ephemeral container and fails the whole deploy on error, bootstrap runs after start against the running component, is invoked on user request (not automatically after deploy), is re-runnable, and failures are reported rather than deploy-failing. It is the place to encode operational knowledge that today lives in READMEs ("after deploying, run docker exec … <app-cli> create-admin").

Bootstrap commands should be written to be idempotent — running them a second time should either no-op or produce a new one-time credential, not corrupt state. The spec recommends idempotency; it does not enforce it.

commands:
  start: "node server.js"
  bootstrap:
    command: "my-app-cli create-admin --url $app.url"
    capture:
      invite_link:
        pattern: "https?://\\S+"
        description: "One-time invite link"
        sensitive: true
### Command Capture

Any command that uses the expanded form can declare **captures** — named values extracted from the command's stdout via regex patterns. The platform matches each capture's regex against the command's stdout and stores the first capture group's value (or, if the pattern has no capture group, the full match).

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `pattern` | `string` | **yes** | -- | Regex matched line-by-line against the command's stdout |
| `description` | `string` | no | -- | Human-readable description of the captured value |
| `sensitive` | `boolean` | no | `false` | If `true`, value is masked in API/UI unless explicitly revealed |

Captured values are surfaced by the platform under a `$outputs.*` namespace and made available through the platform's API or UI. If a pattern does not match any line, the capture is absent (not an error).

Capture is most commonly used with `release` (for migration-time generated values) and `bootstrap` (for post-start admin creation and invite flows), but is allowed on any command that uses the expanded form:

```yaml
commands:
  release:
    command: "./setup.sh"
    capture:
      admin_password:
        pattern: "Admin password: (.+)"
        description: "Generated admin password from initial setup"
        sensitive: true
      admin_url:
        pattern: "Dashboard: (https?://\\S+)"
        description: "URL to the admin dashboard"
  bootstrap:
    command: "my-app-cli create-invite --url $app.url"
    capture:
      invite_link:
        pattern: "https?://\\S+"
        description: "One-time invite link — open in a browser to register"
        sensitive: true

Note on D-23 placement supersede. Earlier versions of this spec documented capture via a top-level outputs: field at component level. That placement has been superseded by the nested capture: form documented above. The capture mechanism (pattern / description / sensitive) is preserved verbatim; only the location of the capture block in the schema has changed. See DESIGN.md D-34 for the migration rationale.

esc
Type to search the docs