> ## Documentation Index
> Fetch the complete documentation index at: https://docs.reasonos.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Infrastructure overview

> Declare cloud infrastructure in .rbs files, right next to the code it serves, and manage its whole lifecycle with rbs infra.

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.

```python theme={null}
load("@rbs//infra/prelude.rbs", "database")

database(
    name = "maindb",
    engine = "postgres",
    instance_type = "small",
    providers = ["aws"],
)
```

```bash theme={null}
rbs infra plan     # see what would change
rbs infra apply    # make it so
```

## 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:

```python theme={null}
load("@rbs//infra/prelude.rbs", "networking", "database", "storage")
```

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:

| Provider            | Name      | Covers                                                               |
| ------------------- | --------- | -------------------------------------------------------------------- |
| Amazon Web Services | `aws`     | Curated abstractions + full generated catalog of every resource type |
| Google Cloud        | `google`  | Curated abstractions + full generated catalog                        |
| Microsoft Azure     | `azurerm` | Curated abstractions + full generated catalog                        |

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.

<Note>
  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.
</Note>

## 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:

```python theme={null}
infra.environment(name = "dev", variables = {"node_count": 2, "instance_type": "medium"})
infra.environment(name = "prod", variables = {"node_count": 5, "instance_type": "large"})

cluster = kubernetes(
    name = "app",
    node_pools = [{"name": "default", "instance_type": infra.var("instance_type"), "min": 1, "max": 10}],
    providers = ["aws"],
)
```

```bash theme={null}
rbs infra plan -e dev
rbs infra apply -e prod
```

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:

```python theme={null}
load("@rbs//infra/service.rbs", "service")

service(
    name = "api-svc",
    binary = ":api",
    uses = ["maindb"],
    needs = {"DB_PASSWORD": "maindb.password"},
)
```

`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

<CardGroup cols={2}>
  <Card title="Defining infrastructure" icon="blocks" href="/infra/defining-infrastructure">
    Resource declarations, environments, provider-specific types, service bindings, and policies.
  </Card>

  <Card title="Infra commands" icon="terminal" href="/infra/commands">
    Every rbs infra subcommand: what it does, key flags, and what touches your cloud.
  </Card>
</CardGroup>
