Skip to main content
rbs ships infrastructure-as-code as part of the build system. You declare resources in the same .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: The infra 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:
The prelude exports: 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 sensitive attributes) are encrypted per value inside the state file. Set the RBS_INFRA_PASSPHRASE environment 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 show and rbs infra output mask sensitive values by default.
Transient artifacts — lock files, saved plan files — stay under the per-server .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:
Each environment keeps its own state file. Once environments are declared, selecting a name none of your files declare is an error listing the declared ones — a typo can never silently plan against empty variables. (-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 exports DATABASE_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.