Skip to main content

Workspace, package, target, label

  • A workspace is a directory tree whose root contains a WORKSPACE.rbs file. All rbs commands operate within one workspace; the root is discovered by walking up from your current directory (override with --workspace-root or RBS_WORKSPACE_ROOT).
  • A package is any directory inside the workspace that contains a BUILD.rbs file.
  • A target is a named buildable thing declared in a BUILD.rbs file by a rule — a binary, a library, a test, a task, an image.
  • A label identifies a target: //path/to/package:target_name. Inside a package, :target_name suffices. //... means every target in the workspace, and :all means every target in one package.

Build files

Build files are written in the RBS language, a small, deterministic, Python-like language. Two kinds matter:
  • WORKSPACE.rbs — workspace-wide setup: toolchains, downloaded tools, external repositories.
  • BUILD.rbs — per-package target declarations.
Rules come from three places: built-ins available everywhere (like native.task, native.genrule, native.filegroup), rule modules shipped inside the binary and imported with load("@rbs//..."), and your own rules defined inline with native.define_rule. Shared rule packages from other repositories are declared in ext.rbs and loaded under a namespace you choose (see rbs ext).

The dependency graph

Every deps edge you declare joins one workspace-wide graph. rbs uses it to build prerequisites first, run independent targets in parallel, and rebuild only what a change affects. The graph is also directly inspectable:
rbs graph covers more than build targets — infrastructure resources, CI workflows, and external dependencies appear in the same document, because they are all declared in the same language.
rbs atlas goes a level deeper: it builds a symbol-level knowledge graph of your code (functions, types, calls, imports) that you can query with commands like rbs atlas explain, rbs atlas path, and rbs atlas affected.

Hermetic toolchains

Compilers and runtimes are declared, not assumed. A toolchain declaration in WORKSPACE.rbs pins an exact version; rbs downloads it on first use and stores it in a user-global toolchain store shared by every workspace on the machine. Builds never depend on whatever happens to be on PATH, so results are reproducible across laptops and CI.
External packages follow the same philosophy: rbs resolves npm, PyPI, and Maven dependencies itself — no npm install or pip install — verifies downloads, and records resolutions in a committed rbs.lock file so later builds resolve instantly and identically.

The shared cache

There is one cache: a user-global, content-addressed store (~/.cache/rbs by default, or $RBS_CACHE_DIR) shared by every workspace on your machine. Action results are keyed by the content of their inputs, so identical work done in any checkout is done once. The per-workspace .rbs/ directory holds only materialized state — outputs, toolchain links, resolved dependencies — and is rebuilt on demand. It is safe to delete and should be git-ignored.
The store is shared across all of your workspaces — cleaning it affects every project on the machine, which is why rbs cache clean requires an explicit scope such as --max-age or --all. Prefer rbs cache gc for routine maintenance.
Teams can additionally share a remote cache: rbs build //... --remote host:8980 (or set RBS_REMOTE) reuses results across machines.

Environments

Configuration is layered .env files validated by a schema written in the build language. Your workspace’s .env.schema declares which variables exist, which are secret, and which named environments (dev, staging, prod — whatever you declare with env.environment()) are available. A base .env provides defaults, per-environment files override it, and a personal .env.local overrides both for you alone. Select an environment per invocation with -e or the RBS_ENV variable — it works on every command:
Values are resolved when build files load and validated before anything runs, so a missing or malformed variable fails fast — not halfway through a deploy.

Branch nodes

In the ReasonOS model, every active branch gets its own rbs server — a branch node. The node owns one branch’s checkout; the ReasonOS editor and agents connect to it, and several developers can join the same branch session and collaborate there. Switching branches means connecting to a different node, never mutating a shared server’s checkout underneath other people. That model shapes how state is stored: Anything worth keeping lives in .reasonos/ and travels with the branch through git: merge the branch and its history merges with it; check the branch out anywhere and its context comes along. Nodes themselves are ephemeral and hold nothing durable. Running your own node is one command in a branch checkout:
The server provides file APIs, ripgrep-powered search, WebSocket terminals, language servers configured from the build graph, and the streaming AI agent — everything the editor needs. One server runs per workspace at a time.
Fully managed branch nodes — where creating a branch automatically provisions its server — are part of the hosted ReasonOS service. Self-hosting today means running rbs server in each branch’s checkout; the storage model above applies either way.