Configuration
orun configuration is split across five user-facing surfaces:
intent.yaml- discovered
component.yamlmanifests - composition sources declared by
intent.compositions - local CLI config in
~/.orun/config.yamlfor backend defaults, the selected workspace, and repo links - the login session in the OS credential store, with
~/.orun/credentials.jsonas the fallback
Intent file
Minimal example:
apiVersion: sourceplane.io/v1
kind: Intent
metadata:
name: demo
env:
OWNER: sourceplane
discovery:
roots:
- services/
environments:
development:
parameterDefaults:
"*":
region: us-east-1
env:
AWS_REGION: us-east-1
The intent file is where you define environments, discovery roots, groups, selectors, defaults, and optional inline components. Root-level env provides global environment variables shared across all environments.
A work: section (orun v2.64 to v2.81) once named where the repository
kept its epics and task contracts and whether orun plan synced them into
Orunbase. Work is tracked in Orunbase or a connected tracker now
(saas-work-gitops-teardown): task contracts live in tasks/ at the
repository root, epic designs under specs/epics/, and a leftover work:
section is ignored with one notice from orun plan.
It also declares where compositions come from:
compositions:
sources:
- name: example-platform
kind: dir
path: ./compositions
Component manifest
Minimal example:
apiVersion: sourceplane.io/v1
kind: Component
metadata:
name: network-foundation
spec:
type: terraform
domain: platform-foundation
env:
REPO: network-foundation
subscribe:
environments: [development, staging, production]
parameters:
stackName: network-foundation
terraformDir: .
Components carry type-specific inputs, root-level environment variables, labels, overrides, and dependency declarations. Root-level env applies across all subscribed environments and can be overridden per subscription.
Conditional profile selection
Use profileRules to select different execution profiles depending on which trigger fires:
spec:
subscribe:
environments:
- name: dev-preview
profile: plan-only
profileRules:
- profile: apply
when:
triggerRef: github-push-main
The profile field is the default fallback. Rules are evaluated in order (first-match-wins). See profile rules for full details.
Change watches
Declare which global intent sections should mark this component as changed during --changed planning:
spec:
change:
watches:
- environments
- groups
- env
Valid values: environments, groups, env, automation, compositions, discovery, execution.
Without change.watches, global intent changes do not affect the component. See change watches for full details.
Change inputs
Declare files outside the component's own directory that should mark this component as changed during --changed planning — a root lockfile, turbo.json, a shared tooling tree:
spec:
change:
inputs:
- pnpm-lock.yaml
- turbo.json
- tooling/eslint/**
Patterns are relative to the repository root and use doublestar syntax (* within a segment, ** across directories, ?, [...], {a,b}). Absolute paths, . or .. segments, backslashes, and malformed globs are rejected. Path ownership is unchanged; a matching file is an additional direct change that propagates over dependsOn input: true edges. See change detection → Input globs.
Live preview
A component can say how it runs itself as a dev server, and the intent names
the one a preview shows. orun preview runs it, and
a coding session's orun agent serve keeps it running:
# component.yaml
spec:
preview:
run: ["pnpm", "--filter", "web", "dev", "--host", "0.0.0.0", "--port", "4173"]
port: 4173
needs: [api-edge] # components whose own previews start first
path: / # the page the preview opens; default /
env: {} # optional: plain, non-secret env for the server
# intent.yaml
preview:
component: web
run is a non-empty argv list run from the component's directory, port an
integer from 1024 to 65535 the server answers on at 127.0.0.1, and path
starts with /. Each of needs must name a component that declares a
preview, with no cycle, and preview.component must name one that does.
orun validate checks all of it. The plan engine
ignores the block.
Composition sources
Declare composition sources in the intent and plan directly against that intent:
orun plan --intent intent.yaml
Each source can be a local package directory, a packaged archive, or an OCI reference. Orun resolves those sources into a cache and writes a lock file under <intent-dir>/.orun/compositions.lock.yaml.
--config-dir still works as a compatibility fallback for legacy folder-shaped compositions.
Environment-specific control
Use CLI flags when you want to scope the effective configuration at compile time:
--envto filter a single environment--changedand related flags for change-aware planning--viewfor DAG-focused render views
Read environment variables if you want to control configuration through the shell.
Environment promotion
Declare promotion dependencies between environments to establish deployment ordering:
environments:
staging:
activation:
triggerRefs: [github-push-main]
promotion:
dependsOn:
- environment: preview
strategy: same-component # default
condition: success # default
satisfy: same-plan-or-previous-success # default
match:
revision: source # default
parameterDefaults:
"*":
lane: verify
| Field | Values | Default | Meaning |
|---|---|---|---|
environment | string (required) | — | Environment that must succeed first |
strategy | same-component, environment-barrier | same-component | How to link jobs across environments |
condition | success | success | Required outcome in the dependency environment |
satisfy | same-plan, previous-success, same-plan-or-previous-success | same-plan-or-previous-success | Whether to use DAG edges, gates, or both |
match.revision | source | source | How to match prior success evidence |
See environment promotion for detailed behavior.
User config: ~/.orun/config.yaml
~/.orun/config.yaml is the non-secret, per-user CLI config (cliauth.Config in internal/cliauth/types.go; read and written by internal/cliauth/storage.go, mode 0600). It never holds tokens.
cloud:
url: https://orun-backend.example.com # Orunbase, or a self-hosted backend URL
catalog:
autopush: false # off by default; publishing the catalog is team-visible
workspace: # written by `orun workspace use`
id: ws_01hxyz
slug: acme
setAt: "2026-09-01T10:00:00Z"
repos: # written by `orun cloud link`
- backendUrl: https://orun-backend.example.com
gitRemote: git@github.com:sourceplane/orun.git
repoFullName: sourceplane/orun
orgId: "org_123"
orgSlug: acme
projectId: "proj_456"
projectSlug: platform
linkedAt: "2026-09-01T10:05:00Z"
| Key | Meaning |
|---|---|
cloud.url | The backend URL: Orunbase or a self-hosted backend. When unset, the CLI uses its built-in default (remotestate.DefaultCloudURL), which points at the hosted Orunbase API |
cloud.catalog.autopush | Push the catalog snapshot automatically after a successful orun plan; equivalent to the intent-level autopushCatalog |
backend.url | Deprecated alias of cloud.url, honoured for one release. orun prints a one-line warning when only the alias is set and prefers cloud.url when both are |
backendBootstrap | Non-secret metadata written by orun backend init for a self-hosted backend: managedBy (always orun-backend-init), accountId, workerName, d1DatabaseName, d1DatabaseUUID, r2BucketName, the catalog queue and DLQ names and IDs, catalogCron, backendCommit, initAt. API tokens and secrets are never stored here |
workspace | The working workspace chosen with orun workspace use: id (the ws_… Workspace ID when known, else the org_… ID), slug, and setAt (RFC 3339). It is a default, not an override — --workspace, ORUN_WORKSPACE, a repo's intent.yaml, and a repo link all win over it |
repos[] | One RepoLink per linked repository: backendUrl, gitRemote, repoFullName, the org/project spine (orgId, orgSlug, projectId, projectSlug), the legacy single-tenant fields (namespaceId, namespaceKind, repoId), and linkedAt |
orun auth login also writes cloud.url from the session's backend URL, keeping the deprecated backend.url in sync only when it was already set.
Credential store
The login session (access token, refresh token, their expiries, the user, and the workspaces the user belongs to) is a secret and lives outside config.yaml:
- On macOS, the default store is the OS keychain — service
io.sourceplane.orun, accountcli-session— when a usability probe succeeds. - Otherwise the session is written to
~/.orun/credentials.jsonwith mode0600. ORUN_CREDENTIAL_STORE=fileforces the file;ORUN_CREDENTIAL_STORE=keychainforces the keychain (macOS) and skips the probe. Test binaries never reach the developer's keychain.
The session is refreshed in place when the access token nears expiry, under a lock so concurrent commands do not race the refresh. orun auth logout clears it. Headless automation uses ORUN_TOKEN or ORUN_TOKEN_FILE instead of a stored session — see environment variables.
Remote state configuration
Add an execution.state block to intent.yaml to enable remote state coordination through the Orunbase backend (or a self-hosted one):
execution:
state:
mode: remote
backendUrl: https://orun-backend.example.com
| Field | Values | Meaning |
|---|---|---|
mode | local (default) or remote | Where execution state is stored |
backendUrl | URI | URL of the backend (Orunbase or self-hosted); when omitted the CLI falls back to ORUN_BACKEND_URL, then ~/.orun/config.yaml, then the built-in default |
autopushCatalog | bool (default false) | When true, a successful orun plan best-effort publishes the resolved catalog and advances the head — but only on the clean default branch, debounced so an unchanged catalog is a no-op, and never failing the plan (silent unless ORUN_VERBOSE). Equivalent to the user-level cloud.catalog.autopush; either being set enables it. For explicit, fail-loud publishing use orun plan --push-catalog or orun catalog refresh --push. |
The backendUrl can also be supplied via --backend-url or ORUN_BACKEND_URL; those take priority over the intent file.
When neither the flag, environment variable, nor intent file sets a backend URL, orun falls back to the cloud.url key in ~/.orun/config.yaml, and finally to the built-in default. orun cloud link writes the repos entries used for local session-authenticated remote-state runs.
Workspace/project scope
State calls are scoped to a workspace/project (path /v1/organizations/{orgId}/projects/{projectId}/state/…). The scope is resolved with the precedence --workspace/--org and --project flags > ORUN_WORKSPACE/ORUN_ORG and ORUN_PROJECT env > the cached RepoLink. The OSS single-tenant backend uses a fixed _local/_local scope, so a workspace with no explicit scope keeps working.
Declare the workspace in intent.yaml under execution.state:
| Field | Values | Meaning |
|---|---|---|
workspace | ws_… id, slug, or org_… id | The leading, committed tenancy claim. A Workspace ID (ws_…) is short and immutable — safe to commit and durable across renames. The declared value is always a Workspace, never the parent Account. |
org | slug or org_… id | Retained alias of workspace (read either, prefer workspace) — existing org configs keep working unchanged. |
requireOrg | bool | Strict mode — a non-interactive remote op that resolves no workspace fails fast. Implied whenever workspace/org is set. |
project | slug or proj_… id | Advanced override; the default is project = repo, derived from the git remote server-side. |
The ws_… / slug / org_… value is passed opaquely; the platform resolves it server-side.
When mode: remote is set, all three commands that read execution state (run, status, logs) automatically use the backend without requiring --remote-state on the command line.
Execution defaults
execution.defaults sets run-time bounds every job inherits unless it sets its own:
execution:
defaults:
timeout: 45m # bounds every step whose job and step set none (runner default: 60m)
lanes:
upstreamWait: 90m # how long a lane job waits for an upstream in another lane (default 60m)
| Field | Values | Meaning |
|---|---|---|
defaults.timeout | Go duration | The bound on every step whose job template and step declare no timeout; carried on the plan as execution.timeout. Without it the runner applies 60 minutes, so no step holds a runner for the CI limit. See Timeouts |
lanes.upstreamWait | Go duration | How long a lane job waits for an upstream in another lane through remote state (default 60 minutes); carried as metadata.lanes.upstreamWait. See Lanes |
Related
- Environment variables — the shell-level overrides for everything above.
- Intent model — the full
intent.yamlreference. - Workspaces and tenancy — how the workspace scope is resolved.
orun login,orun auth,orun cloud,orun workspace— the commands that write the user config and credential store.