# runn: YAML runbooks that behave like Go tests and CI steps

> runn is a Go tool and library that executes multi-step scenarios written in YAML, spanning HTTP, gRPC, database queries, Chrome DevTools Protocol and shell or SSH commands. It suits teams who want one scenario file to serve as an integration test, an automation script and a load generator, and it carries the maintenance weight of a YAML format that has to stay readable.

**k1LoW/runn** — runn is a package/tool for running operations following a scenario.

- Repository: https://github.com/k1LoW/runn
- Website: https://runn.run
- Stars: 655 · Forks: 60
- Language: Go
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/k1low-runn

## One YAML runbook, three ways to run it

The name is the idea: runn means Run N, and the project is described as a package and tool for running operations following a scenario. The author calls the scenario file a runbook, written in YAML.

The same file can be used in three roles. As a tool for scenario based testing, where a runbook asserts what an API or service should do. As a test helper package for the Go language, so a runbook is loaded inside a normal go test and reported by the test framework. And as a tool for workflow automation, where the same steps are driven by a pipeline rather than a test runner.

That triple use is the main argument for the project. A team that writes a login flow once can assert it in CI, replay it against staging, and use it as a smoke test after a deploy without maintaining three copies.

The project is written in Go, MIT licensed, with the documentation site at runn.run and two Japanese books, a runn tutorial and a runn cookbook, published on Zenn. The focus on Japanese-language learning material is a genuine signal about who the maintainer is and which community the tool grew in, and it is worth knowing if your team has no Japanese reading ability, since the primary long-form material is in that language.

The repository layout shows the scope: agent.go and several provider specific agent files, a capture/ directory, a cdp.go for Chrome DevTools Protocol support, db.go, exec.go, and a cmd/ directory for the CLI, alongside the runbook book.go and alias.go.

## One format covering HTTP, gRPC, SQL, browser and shell steps

The protocol coverage is the differentiator. runn supports HTTP requests, gRPC requests, DB queries, Chrome DevTools Protocol, and SSH or local command execution, with what the documentation calls an OpenAPI Document-like syntax for HTTP request testing.

The OpenAPI resemblance matters for HTTP. A step reads like a path plus a method plus a body and expectations, which means an existing OpenAPI description of your service is a natural starting point for a runbook, and a runbook is legible to people who have seen an API specification.

The generated scenario shows the shape. A runbook has a desc, a runners map that binds logical names to targets, and a steps list. Here is the fragment the tool produces from an access log:

```yaml
desc: Generated by `runn new`
runners:
  req: https://dummy.example.com
steps:
  - req:
      /?post=%3script%3ealert(1);:
        get:
          body: null
```

The runners map is the indirection that keeps files portable. The logical name req is bound to a base URL, and steps refer to req rather than to a hard coded host, so the same book can run against a local server, a staging host or a production host by changing one mapping.

For gRPC there is a separate runner kind, greq, which is shown in the Go test example as a grpc:// prefixed URL, and the project dependencies include a gRPC stub helper and a protobuf compiler package, which is how the tool builds request messages for calls that have no HTTP front end.

## runn new turns a curl command into a scenario file

The fastest way to understand the format is to let the tool write it. The quickstart shows a scenario created from a curl command, run in the same step:

```bash
$ runn new --and-run --desc 'httpbin.org GET' --out http.yml -- curl https://httpbin.org/json -H "accept: application/json"
$ runn list *.yml
$ runn run *.yml
```

The same pattern works for grpcurl, so a gRPC call can be captured the same way as an HTTP request. The runn list output shows the description, the path and the optional if condition for each scenario, and runn run then reports a count of scenarios, skipped scenarios and failures.

The more interesting input is a web server access log. The documentation pipes an access log into runn new to produce a YAML file of the requests it contained, which is a way to bootstrap scenarios from real traffic rather than from a design document. Each captured request becomes a step with its path and an empty expected body.

Note the direction of that last feature. It produces a recording of what happened, not an assertion that it should happen. Treating the generated file as a specification is your decision to make, and the generated steps carry no expected status or body.

## The same book as a CLI command and as a Go test

Running scenarios from the terminal is straightforward. runn list shows the scenarios found by a glob, with an id, a description, an if condition, a step count and the file path, and runn run executes them and prints a summary line such as 5 scenarios, 1 skipped, 0 failures. Skipping is a first class concept: a scenario can carry an if condition and be skipped rather than run.

The Go helper is where runn earns its place in an existing test suite. The documented pattern opens an httptest.Server and a sql.DB, then builds options that bind a runner to the test server and a db runner to the database:

```go
	opts := []runn.Option{
		runn.T(t),
		runn.Runner("req", ts.URL),
		runn.DBRunner("db", dbr),
	}
	o, err := runn.Load("testdata/books/**/*.yml", opts...)
	if err != nil {
		t.Fatal(err)
	}
	if err := o.RunN(ctx); err != nil {
		t.Fatal(err)
	}
```

The helper API mirrors the CLI: Load loads many runbooks and RunN runs them, while New and Run work on a single book selected with the Book option. Because the target is injected as an option, the same YAML runs in a unit test against httptest and against a real environment, which is the whole point of separating runners from steps.

There is also a load testing path, with a loadt command and a Makefile target that runs it against a fixed book for a fixed duration, and the repository carries benchmark configuration for reporting results.

## A Go module and a container with a browser inside

Packaging is where the requirements become concrete. The module is github.com/k1LoW/runn and it declares go 1.26.8, so a team on an older toolchain cannot build it without upgrading.

The dependency list is long and tells you what the tool reaches for: a wildcard matcher, an access log parser, a YAML library, an expression evaluator, chromedp and the Chrome DevTools protocol definitions for browser steps, an SSH server library for SSH steps, MySQL and Spanner drivers for database steps, a gRPC stub helper, an HTTP request helper, and several agent SDKs including Claude, Codex and Copilot clients, which correspond to the agent files in the repository root.

The container image builds from golang:1.26.8-trixie and runs on debian:trixie-slim, and it installs chromium and fonts-noto-cjk alongside git. Chromium is there for the CDP steps and the CJK fonts for the rendering and terminal output. A slim variant is also provided as Dockerfile.slim.

That image is a convenience for CI, not a free lunch: it is a large image with a browser in it, and every pipeline that pulls it pays for that. The Makefile shows the local path instead, building the binary directly with the commit and build date stamped into it:

```bash
build:
	go build -ldflags="$(BUILD_LDFLAGS)" -o runn cmd/runn/main.go
```

The Makefile also documents the quality gates the project runs for itself: golangci-lint, govulncheck and go vet with gostyle for linting, plus go-licenses check with permissive, forbidden and restricted licence types disallowed, including tests. A licence policy enforced in CI is a meaningful signal for anyone with a compliance function.

## Where runn stops, and what it does not replace

The honest limitations are visible in the design rather than hidden in a caveats section.

First, the runbook is YAML, and YAML becomes the thing your team has to maintain. A scenario that encodes a login flow, an assertion on a response and a database check is a test artifact in the same way a Go test is, except it is less familiar to most Go developers and it will not be caught by the compiler. For a team that already writes Go tests for everything, runn adds a second testing language rather than replacing one.

Second, the tool drives live systems. A runbook that calls a real endpoint needs that endpoint to exist and to be in a known state, which is exactly the fragility that unit tests avoid and integration suites inherit. The httptest and sql.DB pattern in the Go helper is the mitigation: run against a throwaway server and database in the test, not against production.

Third, there is a scope limit worth stating plainly. runn executes steps and asserts their outcomes. It does not build your service, deploy it, roll back a release or manage the fixtures around the request. It is an operations and testing layer, and a team looking for a full end to end pipeline tool will find it one component of that pipeline.

Fourth, the CDP and agent features pull in a browser and third party model SDKs, which widens the dependency surface for a tool that many teams will only use for HTTP checks.

## MIT licence, frequent releases and an unusual contribution path

The licence is MIT, with a LICENSE file at the root, and the dependency check in the Makefile goes further than most projects by disallowing permissive, forbidden and restricted licence types in its own dependency tree. In practice that means the tool keeps a tight dependency budget, which is consistent with a tool whose author cares about supply chain risk.

Maintenance looks healthy. The repository is not archived, the last push was on 2026-10-19, and the recent releases show a fast cadence: v1.10.0 on 2026-09-01, v1.11.0 on 2026-09-18 and v1.11.1 on 2026-09-28. Minor versions arriving every couple of weeks means the API can move, so pin the version your pipeline uses rather than tracking the main branch.

One detail stands out for contributors. The repository carries a CREDITS file and a separate _EXTRA_CREDITS file, and the author has historically preferred that changes come through forks of the repository rather than pull requests, in order to keep review overhead manageable. For an organisation with a mandatory contribution process, that is a real friction point to raise before you invest in runbook tooling.

The project also publishes a CHANGELOG.md and uses tagpr for tagging, so release notes are the place to look when a minor version lands.

## Conclusion

Adopt runn when your team runs multi-step integration flows against a live system and wants those flows stored as YAML that doubles as a Go test and a CI step, particularly if HTTP, gRPC, SQL and browser steps have to live in one file. Do not adopt it for unit testing, for flows that need heavy custom Go logic per step, or for a team unwilling to maintain YAML, because the format is the product. Before you commit, check that your Go toolchain matches the go 1.26.8 module requirement, confirm the container image ships the browsers the CDP steps need, and decide who owns the runbooks in version control, since the README points out the author prefers to review changes through forks.

## FAQ

### What is runn and what is a runbook in it?

runn is a Go package and tool for running operations following a scenario, where the scenario is a runbook written in YAML. A runbook has a desc, a runners map binding logical names to targets, and a steps list, and it can be run from the CLI, inside a Go test, or as part of workflow automation.

### How do I create a runn scenario from an existing curl command?

Use runn new with the command after the double dash separator, for example runn new --and-run --desc 'httpbin.org GET' --out http.yml -- curl https://httpbin.org/json. The same works for grpcurl calls, and an access log can also be piped into runn new to generate a file of captured requests.

### Which protocols can a runn step use?

HTTP requests, gRPC requests, DB queries, Chrome DevTools Protocol steps, and SSH or local command execution are supported, with an OpenAPI Document-like syntax for HTTP request testing. Runners in the YAML bind logical names such as req, greq and db to actual targets.

### Can I run runn scenarios inside a Go test?

Yes. Load the books with runn.Load and pass options such as runn.T(t), runn.Runner and runn.DBRunner to bind the test server and database, then call o.RunN(ctx) for many books or o.Run(ctx) for a single book selected with the Book option.

## Sources

- [k1LoW/runn on GitHub](https://github.com/k1LoW/runn)
- [License: MIT](https://github.com/k1LoW/runn/blob/main/LICENSE)
- [Project website](https://runn.run)
- [README](https://github.com/k1LoW/runn/blob/main/README.md)
- [Releases](https://github.com/k1LoW/runn/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/k1low-runn
