Compare

Stampdrill and Bruno

Bruno made the same bet about files: requests belong in a folder in your repository, not in a workspace you sign into. If that part is already settled for you, the choice between the two is narrower than a feature list suggests. What differs is what one file can say, what a run does with it, and which machines you need it on.

Where the two agree

A Bruno collection is a folder of files on disk. So is a Stampdrill workspace: every .stamp file under it holds requests, and an environment.stamp at the root holds the values they share. In both cases the folder goes into git next to the code it calls, a change to a request arrives as a diff in a pull request, and a clone is a working set of requests. Neither needs an account to run what is in the folder, and both have a command line tool, so the same files run in a terminal and in a build pipeline. None of that separates them.

What one file can say

A .bru file is one request, written as named blocks: meta with its name and its place in the order, a block named after the method holding the URL, then headers, a body block such as body:json, an auth: block, and an assert block of checks. Past a check, the logic is JavaScript, in script:pre-request, script:post-response or tests. Environments are separate .bru files under environments/.

A .stamp file holds as many requests as you like, separated by a ### line, and the work around them is part of the same small language rather than an escape into JavaScript. This is one whole file:

plan Checkout {
  tags smoke

  step "Log in, then list" {
    run logIn
    expect status == 200
    run listOrders
    expect all(body.items, order => order.total > 0)
  }
}

load Orders {
  users 50
  ramp 30s
  duration 5m
  threshold p95 < 800ms
  threshold errors < 1%

  scenario {
    run listOrders
    expect status == 200
  }
}

### Log in
POST {{baseUrl}}/auth/login
Content-Type: application/json

{ "username": "{{username}}", "password": "{{password}}" }

> assert status == 200
> set token = body.accessToken

### List orders
@needs logIn
@auth bearer {{token}}
GET {{baseUrl}}/orders?limit={{limit}}

> assert status == 200
> assert body.items.length <= limit

Four things in there are not the request itself:

Environments differ in shape as well. Instead of one name picked from a flat list, a workspace declares an axis per thing that varies, and variable sets say which values they apply to:

dimension env = test, staging, prod
dimension region = latam, mena, apac

vars {
  limit = 25
}

vars env=staging|prod {
  host = "api." + env + "." + region + ".example.com"
}

vars env=test {
  host = localhost(8080)
  limit = 5
}

Then stamp run . env=staging region=mena picks a combination rather than a file, and stamp env . prints what every name is worth and which vars block decided it. Variables and dimensions covers it in full.

What a run can do

A race condition is a test, not a different product. concurrently N starts N actors at one instant, sync holds them together until the last one arrives, and results afterwards is each actor's last response, so the thing you want to assert, that exactly one withdrawal succeeded, is one line:

plan DoubleSpend {
  setup { run logIn }

  concurrently 10 {
    sync "ready"
    run withdraw with amount = 100
  }

  expect count(results, r => r.status == 200) == 1, "only one withdrawal may succeed"
}

The same files reach past HTTP. WS opens a socket and scripts the conversation with send and receive, GRAPHQL sends an operation with its variables after a blank line, STUN and TURN check the ICE servers a WebRTC call needs, and MCP connects to a Model Context Protocol server the way a client would, lists its tools, resources and prompts, and calls them with call, read and getPrompt. All of it is in the example workspace that ships with the repository, which stamp check counts as 18 files and 58 requests: Plans.stamp, Load and races.stamp, Playground/WebSockets.stamp, Playground/GraphQL.stamp, Playground/MCP.stamp and Playground/Mock data.stamp for repeatable sample data from a seed.

How much of that Bruno covers is a question for Bruno's own documentation, which moves. This page does not answer it on their behalf.

What Bruno has that Stampdrill does not

Three things, and any one of them can settle it.

One more limit worth knowing before you buy: the App Store app is sandboxed, so it cannot start another program. MCP servers launched with stdio: run from the command line tool, and the app connects to servers over HTTP.

Moving a collection across

Point the importer at the collection folder, the one with bruno.json in it, and it writes .stamp files. Bruno's YAML collections work the same way: a folder with opencollection.yml, or the bundled single file. A lone .bru file is also accepted.

stamp import ./my-collection -o api
stamp check api

It prints a line per file written, then the collection's name with how many requests landed in how many files, and a note line for anything it could not carry over as it was. Nothing is sent, and it never writes over a file that already exists: it stops and asks for another folder. In the app the same thing is File ▸ Import… (⇧⌘I).

What arrives:

What does not, which it tells you about rather than dropping quietly: script:pre-request is not converted. Common lines of script:post-response and tests are, and the rest are kept as comments above the request. Scripts attached to the collection or to a folder are reported by name. A .bru request that is not HTTP is named and skipped.

Variable names that are not identifiers are renamed the same way everywhere, api-key to apiKey, and an import never takes a name the workspace already declares, so it cannot change what an existing request sends. Run stamp check on the result before anything else: it parses the folder and sends nothing.

Side by side

StampdrillBruno
Requests on disk.stamp text, several requests per file.bru files, one request each
In gitthe whole folderthe whole folder
Account to run itnonenone
Logic past a checkexpressions, lambdas and fn in the same fileJavaScript in script: and tests blocks
Environmentsa dimension per axis, combinedone file per environment
PlatformsmacOS for the app, macOS and Linux for stampmacOS, Windows and Linux
Open sourceengine and command line tool (MPL 2.0); the Mac app is notyes
Price$49.99 once for the app, command line freethe client is free

If you need Windows, or a client whose own source you can change, Bruno is the answer and this is a short comparison. On a Mac, the question is what you want the file to be able to say: if it keeps turning out to be the plan, the load test and the race belonging next to the request that they exercise, in one language and one review, that is what this is. Trying it costs an import and a stamp check, because the command line tool is free: start at the quick start, or read Bringing collections in first.

The guide Quick start Stampdrill and Postman

Updated