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

# Go

> Build, test, and cross-compile Go services with hermetic toolchains and offline module resolution

`rbs` builds Go with a **hermetic, downloaded toolchain** — no Go installation on the
host is required. Compilation always runs offline: third-party modules are fetched once
into a shared workspace cache (verified against your committed `go.sum`) or read from a
committed `vendor/` tree, and the compiler itself can never reach the network.

The Go rules live in two modules:

```python theme={null}
load("@rbs//go/rules.rbs", "go_binary", "go_library", "go_test", "go_deps_consistency")
load("@rbs//go/proto.rbs", "go_proto_library")
```

## Declaring the Go toolchain

Declare the toolchain once in your workspace's `WORKSPACE.rbs`. `rbs` downloads the
matching Go SDK for your platform on first build and reuses it from a shared store
afterwards.

<Steps>
  <Step title="Declare the toolchain in WORKSPACE.rbs">
    ```python theme={null}
    load("@rbs//go/toolchain.rbs", "go_toolchain")

    go_toolchain(
        name = "go",
        version = "1.25.6",
    )
    ```
  </Step>

  <Step title="Build a Go target">
    ```bash theme={null}
    rbs build //services/myservice:myservice
    ```

    The first build downloads the Go SDK; subsequent builds are fully offline.
  </Step>
</Steps>

`go_toolchain` accepts:

| Parameter           | Type   | Default    | Required | Meaning                                                                                                                                  |
| ------------------- | ------ | ---------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `name`              | string | `"go"`     | no       | Toolchain name. Keep the default unless you have a reason not to — the Go rules look up the toolchain named `go`.                        |
| `version`           | string | `"1.24.3"` | no       | Go version to download. Currently supported: `1.24.3`, `1.25.6`. An unsupported version fails at load time and lists the available ones. |
| `register`          | bool   | `True`     | no       | Register the toolchain for the workspace (leave on).                                                                                     |
| `platform_override` | string | `None`     | no       | Pin the toolchain to a specific platform instead of the current target platform.                                                         |
| `download_host`     | bool   | `True`     | no       | Also download the host platform's SDK when it differs from the target platform.                                                          |

<Warning>
  The Go rules never auto-download a different Go version mid-build. If a `go.mod`
  declares a `go` directive newer than the workspace toolchain, the build fails with
  the Go tool's own version error — bump `version` in `WORKSPACE.rbs` instead.
</Warning>

<Note>
  If no Go toolchain is declared, the rules fall back to whatever `go` is on your
  `PATH`. That works for quick experiments but is not hermetic — declare the
  toolchain for anything you share or ship.
</Note>

## Rules

### go\_binary

Builds a Go main package into a runnable binary. Sources (plus `go.mod`/`go.sum` for
module builds) are staged into an isolated build workspace; the build runs with the
network disabled.

| Attribute | Type        | Default | Required | Meaning                                                                                                                                                                                   |
| --------- | ----------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`    | string      | —       | yes      | Target name.                                                                                                                                                                              |
| `srcs`    | label list  | `[]`    | no       | Go sources, plus `go.mod`/`go.sum` for module builds. A committed `vendor/` tree is optional — without one, dependencies are fetched from the module proxy and verified against `go.sum`. |
| `deps`    | label list  | `[]`    | no       | Local `go_library` dependencies (staged as sources).                                                                                                                                      |
| `goflags` | string list | `[]`    | no       | Extra flags passed to `go build`.                                                                                                                                                         |
| `pkg`     | string      | `"."`   | no       | Main package path within the module, e.g. `"./cmd/myservice"`.                                                                                                                            |
| `cgo`     | bool        | `False` | no       | Opt in to CGO. Off by default so builds never depend on a host C toolchain. Cannot be combined with `goos`/`goarch`.                                                                      |
| `goos`    | string      | `""`    | no       | Target OS for cross-compilation (e.g. `"linux"`). Empty means the host platform.                                                                                                          |
| `goarch`  | string      | `""`    | no       | Target architecture for cross-compilation (e.g. `"amd64"`, `"arm64"`). Empty means the host platform.                                                                                     |

A typical service target uses a standard Go module layout — a thin `main` under
`cmd/`, everything else under `internal/`:

```python theme={null}
load("@rbs//go/rules.rbs", "go_binary")

_module_srcs = glob([
    "cmd/**/*.go",
    "internal/**/*.go",
]) + ["go.mod", "go.sum"]

go_binary(
    name = "myservice",
    srcs = _module_srcs,
    pkg = "./cmd/myservice",
)
```

```bash theme={null}
rbs build //services/myservice:myservice
rbs run //services/myservice:myservice
```

### go\_library

Packages Go sources under a name so other targets can depend on them. It does not
compile a separate archive — the consuming `go_binary` or `go_test` compiles all
sources together.

| Attribute | Type       | Default | Required | Meaning                                                                                   |
| --------- | ---------- | ------- | -------- | ----------------------------------------------------------------------------------------- |
| `name`    | string     | —       | yes      | Target name.                                                                              |
| `srcs`    | label list | `[]`    | no       | Go source files (and optionally `go.mod`/vendor files).                                   |
| `deps`    | label list | `[]`    | no       | Local `go_library` dependencies.                                                          |
| `cgo`     | bool       | `False` | no       | Accepted for API parity with `go_binary`; compilation is handled by the consuming target. |

```python theme={null}
load("@rbs//go/rules.rbs", "go_binary", "go_library")

go_library(
    name = "greeter",
    srcs = ["greeter.go"],
)

go_binary(
    name = "hello",
    srcs = ["main.go", "greeter.go"],
    deps = [":greeter"],
)
```

<Note>
  Within a single Go module, the simplest pattern is to `glob` the module's sources
  directly into each `go_binary`/`go_test` target (as in the `go_binary` example
  above) — the Go compiler resolves package imports through the module path.
</Note>

### go\_test

Runs `go test` hermetically over your module's packages, with the same offline
module resolution as `go_binary`. It is also the coverage entry point for
`rbs coverage`.

| Attribute           | Type        | Default     | Required | Meaning                                                                                                                                                          |
| ------------------- | ----------- | ----------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`              | string      | —           | yes      | Target name.                                                                                                                                                     |
| `srcs`              | label list  | —           | yes      | Go sources **including** `_test.go` files, plus `go.mod`/`go.sum` for module builds (a committed `vendor/` tree is optional). The rule fails if `srcs` is empty. |
| `deps`              | label list  | `[]`        | no       | Local `go_library` dependencies (staged as sources).                                                                                                             |
| `goflags`           | string list | `[]`        | no       | Extra flags passed to `go test`.                                                                                                                                 |
| `packages`          | string list | `["./..."]` | no       | Package patterns to test.                                                                                                                                        |
| `cgo`               | bool        | `False`     | no       | Opt in to CGO. Off by default.                                                                                                                                   |
| `size`              | string      | `"medium"`  | no       | Test size: `small`, `medium`, or `large`.                                                                                                                        |
| `timeout`           | int         | `300`       | no       | Timeout in seconds (also passed as `go test -timeout`).                                                                                                          |
| `min_line_coverage` | int         | `0`         | no       | Minimum line coverage percentage — `rbs coverage` **fails** below it.                                                                                            |
| `coverage_exclude`  | string list | `[]`        | no       | File patterns (e.g. `"*.pb.go"`) removed from coverage entirely — numerator and denominator. For machine-generated code only.                                    |

```python theme={null}
load("@rbs//go/rules.rbs", "go_test")

_module_srcs = glob([
    "cmd/**/*.go",
    "internal/**/*.go",
]) + ["go.mod", "go.sum"]

go_test(
    name = "test",
    srcs = _module_srcs,
    min_line_coverage = 80,
    coverage_exclude = ["*.pb.go"],
)
```

<Note>
  Stage every file your tests read at runtime. If tests load fixtures such as SQL
  migrations, add them to `srcs` (e.g. `+ glob(["migrations/*.sql"])`) so they exist
  in the isolated test workspace.
</Note>

### go\_deps\_consistency

An **opt-in** gate for workspaces with multiple Go modules (one `go.mod` per
service). It fails the build when two modules require the same dependency at
different versions, naming every module, version, and file. Skip it if your
workspace uses a single shared `go.mod` — drift is then impossible by construction.

| Attribute          | Type        | Default | Required | Meaning                                                                                                                                                           |
| ------------------ | ----------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`             | string      | —       | yes      | Target name.                                                                                                                                                      |
| `go_mods`          | label list  | —       | yes      | `go.mod` files to cross-check — at least two.                                                                                                                     |
| `exempt`           | string list | `[]`    | no       | Module paths allowed to differ between services. Each entry deserves a comment saying why.                                                                        |
| `include_indirect` | bool        | `False` | no       | Also enforce `// indirect` requires (lockstep upgrades). Off by default — indirect versions legitimately differ between modules with different dependency graphs. |

```python theme={null}
load("@rbs//go/rules.rbs", "go_deps_consistency")

go_deps_consistency(
    name = "go_deps_check",
    go_mods = [
        "billing/go.mod",
        "gateway/go.mod",
        "worker/go.mod",
    ],
)
```

When a conflict is found, the build fails with a fix-it message: align the versions
(`go get <module>@<version> && go mod tidy` in each service) or list the module in
`exempt` with a reason.

### go\_proto\_library

Generates Go protobuf/gRPC code from `.proto` sources using the hermetic `protoc`
and pinned `protoc-gen-go` / `protoc-gen-go-grpc` plugins. Generated `.go` files are
written **into the source tree** (stamped `DO NOT EDIT`) so they can be committed
next to the code that imports them — `go_binary`/`go_test` build from committed
sources, so generated code must live beside them. Requires `proto_toolchain()` in
`WORKSPACE.rbs`.

| Attribute    | Type        | Default | Required | Meaning                                                                                                    |
| ------------ | ----------- | ------- | -------- | ---------------------------------------------------------------------------------------------------------- |
| `name`       | string      | —       | yes      | Target name.                                                                                               |
| `srcs`       | label list  | —       | yes      | `.proto` source files (package-relative paths).                                                            |
| `go_package` | string      | —       | yes      | Must repeat the proto's `option go_package` — used to flatten output paths.                                |
| `out_dirs`   | string list | —       | yes      | Workspace-relative package directories to write generated `.go` files into. One copy per consuming module. |

```python theme={null}
load("@rbs//go/proto.rbs", "go_proto_library")

go_proto_library(
    name = "go",
    srcs = ["v1/orders.proto"],
    go_package = "example.com/orders/internal/api/ordersv1",
    out_dirs = [
        "services/orders/internal/api/ordersv1",
        "services/gateway/internal/api/ordersv1",
    ],
)
```

Re-run the target (`rbs build //proto/orders:go`) after editing the `.proto` to
refresh the committed stubs; catch drift in CI by regenerating and diffing.

## Third-party dependencies

Go dependencies are declared exactly as Go expects — in your module's `go.mod` and
`go.sum` — and `rbs` resolves them hermetically. Compilation always runs with module
fetching disabled, so dependencies must be on disk before the compiler starts. Which
mode you are in follows from what you commit:

<Tabs>
  <Tab title="go.sum (default)">
    Commit `go.mod` + `go.sum` (and list both in `srcs`), with no `vendor/` tree.
    In a single network-enabled staging step, `rbs` downloads modules from the Go
    module proxy into a shared per-workspace cache, **verifying every download
    against your committed `go.sum`**. The build and all future builds then run
    offline against that cache.

    If `go.sum` is missing entries, the build fails and tells you to run
    `go mod tidy` and commit the result — incomplete checksums are never silently
    self-healed.
  </Tab>

  <Tab title="vendor/">
    Commit a `vendor/` tree (and include it in `srcs`). Builds run fully offline
    with `-mod=vendor`.

    `vendor/modules.txt` must be present — a `vendor/` directory without it usually
    means vendoring is stale, so the build fails and asks you to run
    `go mod vendor` and commit the complete tree.
  </Tab>

  <Tab title="No dependencies">
    A module with `go.mod` but no `go.sum` builds offline as a zero-dependency
    module. The moment a real dependency appears, the build fails loudly until a
    complete `go.sum` is committed. Sources without any `go.mod` build in
    non-module mode — fine for small stdlib-only tools.
  </Tab>
</Tabs>

<Tip>
  **Private modules:** downloads come from the module proxy only — there is no
  fallback to `git` or VCS credentials, so builds stay reproducible on any machine.
  Point the `RBS_GOPROXY` environment variable at an internal module proxy to
  resolve private modules.
</Tip>

## Testing and coverage

Run tests with `rbs test`:

```bash theme={null}
rbs test //services/myservice:test    # one target
rbs test //...                        # every test target in the workspace
rbs test :test --watch                # TDD: re-run on file changes
```

Enforce coverage thresholds with `rbs coverage`:

```bash theme={null}
rbs coverage //services/myservice:test
rbs coverage //...
```

`rbs coverage` runs the tests with coverage instrumentation across **all** staged
packages — untested packages count as 0% instead of silently dropping out — converts
the Go cover profile to lcov, and fails the target if line coverage falls below
`min_line_coverage`.

<Warning>
  `rbs test` does not enforce coverage thresholds — it prints a reminder when a
  target declares them. Gate coverage in CI with `rbs coverage`.
</Warning>

<Note>
  Go coverage measures statements, so `go_test` exposes a **line** coverage threshold
  only — there are no branch or function thresholds that could never be measured.
</Note>

Use `coverage_exclude` sparingly and only for machine-generated files (for example
`"*.pb.go"` from `go_proto_library`) — excluded files disappear from both sides of
the ratio, and the coverage log names every active exclusion so the narrowed
denominator is never silent.

## Cross-compilation

`go_binary` cross-compiles per target via the `goos` and `goarch` attributes — the
standard workflow for packaging Linux images from a macOS workspace:

```python theme={null}
go_binary(
    name = "myservice_linux",
    srcs = _module_srcs,
    pkg = "./cmd/myservice",
    goos = "linux",
    goarch = "arm64",
)

oci_image(
    name = "image",
    binary = ":myservice_linux",
    base = "scratch",
    entrypoint = ["/app/myservice_linux/runfiles/_main/myservice_linux.bin"],
)
```

Rules of the road:

* Two targets that differ only in `goos`/`goarch` are cached and built
  independently — they can never reuse each other's output.
* `cgo = True` cannot be combined with `goos`/`goarch`: cross-compiling CGO would
  need a target C toolchain, which `rbs` does not provide.
* A cross-compiled binary is for packaging and deployment, not for running locally.
  Invoking it on a mismatched host prints a clear explanation instead of the
  kernel's cryptic `exec format error`.
* Container images stamp their os/arch from the packaged binary itself, so
  packaging a host-platform binary into a Linux-stamped image fails loudly rather
  than producing a lying image.

## Caching

Go builds share one module cache and one build cache per workspace, so each
dependency version downloads once and packages compile incrementally across all
targets; completed build actions are additionally cached in the shared `rbs` store,
so unchanged targets are not rebuilt at all.
