.rbs files that declare your build targets, and rbs plans, applies, and tracks them — no separate tool, no separate language, no separate state backend to operate.
Two surfaces
Infrastructure has two distinct surfaces — keep them straight: Theinfra object is predeclared in every .rbs file. Like env and glob, you never load() it. It carries the SDK functions: infra.environment, infra.var, infra.resource, infra.register_provider, infra.output, infra.secret, infra.resolve, infra.module, infra.define_resource_type, infra.define_policy, and more.
The abstract resource rules are loaded from the prelude. These are the cloud-agnostic building blocks — one call declares a database or a Kubernetes cluster on any supported cloud:
compute, networking, security_group, load_balancer, kubernetes, container_registry, storage, block_storage, database, cache, serverless_function, api_gateway, message_queue, dns_zone, dns_record, certificate, iam_role, iam_policy, secret, cdn, kms_key, nosql_table, topic, log_group, metric_alert, container_service, nat_gateway, and scheduler_job.
Supported clouds and providers
Three providers ship inside the rbs binary, ready to use with no downloads to configure:
Every abstract resource rule in the prelude has adapters for all three, so switching clouds — or targeting several — is a change to the
providers list, not a rewrite. The embedded catalogs are version-pinned: using a generated resource type locks the provider to the version its schema was generated from, so plans stay reproducible.
Beyond the built-in three, any provider from the Terraform registry works: register it with infra.register_provider(name = ..., source = "namespace/name", version = ...) and rbs downloads the provider plugin and validates your resources against its real schema. You can also write providers in the pure build language (infra.register_native_provider) for internal APIs.
Pulumi provider sources (
source = "pulumi/...") are not supported. rbs rejects them with a pointer to the equivalent Terraform source, which almost always exists — most Pulumi providers wrap the same upstream providers.State travels with the branch
Applied infrastructure state lives in the committed.reasonos/ tree: .reasonos/infra/state/<environment>.state.json. It moves through git with your branch — every branch node, every collaborator, and CI all see the same state without a remote backend to configure.
Because state is committed, secrets in it are taken seriously:
- Sensitive values (marked by provider schemas and by
sensitiveattributes) are encrypted per value inside the state file. Set theRBS_INFRA_PASSPHRASEenvironment variable before applying; rbs derives the key from it. - With no passphrase set, rbs saves sensitive values in plaintext and warns loudly — the state file is committed to git, so set the passphrase.
- Loading encrypted state without the passphrase (or with the wrong one) is a hard error, never silent garbage.
rbs infra showandrbs infra outputmask sensitive values by default.
.rbs/ directory and never enter git. Provider plugins are cached once per user, shared across all workspaces.
Environments
Infrastructure is environment-aware. Declare environments and their variables in your.rbs files, then select one with -e on any rbs infra command:
-e is an alias for -w/--workspace; infra.workspace is the same function as infra.environment.)
These are the same environment names your .env overlays use with -e/RBS_ENV at build time — one vocabulary for “which flavor of the system am I working on.”
From infrastructure to running services
Declaring resources is half the story; the other half is apps consuming them without hardcoded endpoints. Resource types declare conventional environment exports (a database exportsDATABASE_HOST/DATABASE_PORT, a container registry exports REGISTRY_URL), and the service rule binds a binary to the resources it uses:
rbs run //backend:api-svc builds the binary, resolves the environment from applied state, and launches the process with it injected. Resolution happens at run time on purpose: infra changes take effect on the next run without a rebuild, and secrets are never baked into files on disk. The oci_push rule participates the same way — uses = ["api-registry"] pushes to the registry rbs provisioned, URL from state.
Where to next
Defining infrastructure
Resource declarations, environments, provider-specific types, service bindings, and policies.
Infra commands
Every rbs infra subcommand: what it does, key flags, and what touches your cloud.