rbs resolves third-party packages itself — it talks to the npm registry and PyPI
directly, resolves version ranges and transitive dependencies, and downloads
everything into a shared store. No npm install, yarn, or pip install in your
workflow, and no per-project node_modules bloat: a package version is downloaded
once per user and shared by every workspace that needs it.
Declaring dependencies
Dependencies are declared in WORKSPACE.rbs, then referenced from build targets like
any other dependency.
Then depend on them from BUILD.rbs:Declaring many packages at once: Then depend on them from BUILD.rbs:py_repositories(name, packages = ["django:5.0.1", ...]) declares a batch in one
call. Go dependencies don’t need workspace declarations — they come from the go.mod and
go.sum you already commit:rbs fetches modules from the Go module proxy in a single, explicit staging step,
verifying every download against your committed go.sum — then compiles fully
offline, with module fetching disabled so nothing can be pulled mid-build. If
go.sum is missing entries, the build fails and tells you to run go mod tidy and
commit the result, rather than silently trusting whatever was fetched.A committed vendor/ tree is also supported: when vendor/ is present, rbs builds
from it offline without touching the network at all.Downloaded modules land in a module cache shared by every target in the workspace,
so each module version is fetched once per workspace, not once per target.
The lockfile: rbs.lock
For npm and pip dependencies, rbs records every resolution — the exact version chosen
for each declared and transitive package, plus integrity checksums — in rbs.lock at
the workspace root.
- Commit it. With a lockfile present, resolution is instant (no registry
round-trips for known packages) and reproducible: everyone on the branch gets the
same dependency tree.
- rbs updates it automatically when you change declared versions; there’s no separate
“lock” command to run.
Go modules are locked by go.mod/go.sum as usual — rbs.lock covers the
registry-resolved ecosystems.
Where packages live
Resolved packages are materialized per-workspace under
.rbs/external-deps/<platform>/<ecosystem>/<package>/, which is where build rules
and language servers find them. Behind that path sits a user-global store
(under ~/.cache/rbs, following RBS_CACHE_DIR): each package version is stored
once per user, and workspace paths link into it. A second workspace declaring
express@4.18.2 links the existing copy instead of re-downloading it.
The store is managed with the rest of the shared cache:
rbs cache stats reports it under “External deps”, and rbs cache gc ages out
packages no workspace has used recently — anything swept re-resolves automatically on
the next build or sync.
Syncing for your editor: rbs sync
Language servers need dependencies on disk before they can offer autocomplete and
go-to-definition — but on a fresh clone, packages only appear as targets get built.
rbs sync resolves everything up front:
Sync walks the targets’ dependency graphs, resolves every external dependency
through the same resolvers the build uses, and reports what was downloaded versus
already cached. After it finishes, your editor’s LSP has the full dependency set for
accurate autocomplete and type checking.
Run rbs sync //... right after cloning a branch — it warms the workspace for both
your editor and your first build.