High-performance Mountebank-compatible HTTP/HTTPS mock server written in Rust
Rift is a high-performance, Mountebank-compatible mock server that delivers ~20–150x faster throughput on typical workloads, and up to ~450x on large regex predicate sets. Use your existing Mountebank configurations and enjoy faster test execution.
Documentation | Quick Start | Examples
- Same REST API - Works with existing Mountebank clients and tooling
- Same Configuration - Load your
imposters.jsonwithout changes - Same Behavior - Predicates, responses, behaviors all work identically
Both engines, same machine, same load — measured on two very different hosts so you can see how much of the gap is the engine and how much is the hardware:
| Workload | Apple M4 laptop Mountebank → Rift |
AMD EPYC 9V74, 16 vCPU Mountebank → Rift |
|---|---|---|
| Simple static stub | 8,898 → 214,818 RPS (24x) | 5,982 → 324,952 RPS (54x) |
| Deep path match (410 stubs) | 1,344 → 209,523 RPS (156x) | 542 → 322,530 RPS (595x) |
| Complex AND/OR predicates | 4,703 → 191,987 RPS (41x) | 1,814 → 259,548 RPS (143x) |
| JSON body equals | 7,611 → 199,670 RPS (26x) | 2,730 → 294,294 RPS (108x) |
| JSONPath predicate | 4,312 → 199,404 RPS (46x) | 1,921 → 304,796 RPS (159x) |
| XPath predicate | 5,542 → 187,869 RPS (34x) | 1,966 → 247,897 RPS (126x) |
| Regex path (100 patterns) | 112 → 207,024 RPS (1,857x) | 52 → 317,851 RPS (6,160x) |
Read the two columns together, not separately. Rift gets faster with more cores (215k → 325k); Mountebank gets slower (8,898 → 5,982), because it is single-threaded and the server's individual cores are slower than the laptop's. So the EPYC multipliers are inflated at both ends — the M4 column is the more conservative read, and it is still 24x–1,857x.
Measured 2026-07-20 — Rift built from master (924cf73) vs Mountebank 2.9.1, native
processes (no Docker), oha at 50 keep-alive connections, 20s/scenario after warmup, each engine
run alone on the same machine. Each figure is the median of 3 repetitions; per-scenario spread was
≤12% on the M4 (a laptop thermally throttles over a 30-minute run — both engines lost ~7% between
the first and last repetition) and ≤5% on EPYC. Throughput scales with matching complexity: Rift
stays flat while Mountebank's per-request cost grows with stub count and predicate type. Full
methodology and all 13 scenarios: tests/benchmark. Your numbers will vary
with hardware and config.
- Imposters - HTTP/HTTPS mock servers
- Predicates - equals, contains, matches, exists, jsonpath, xpath, and, or, not
- Responses - Static, proxy, injection
- Behaviors - wait, decorate, copy, lookup
- Proxy Mode - Record and replay
# Pull and run
docker pull zainalpour/rift-proxy:latest
docker run -p 2525:2525 -p 4545:4545 zainalpour/rift-proxy:latest
# Create your first imposter
curl -X POST http://localhost:2525/imposters \
-H "Content-Type: application/json" \
-d '{
"port": 4545,
"protocol": "http",
"stubs": [{
"predicates": [{ "equals": { "path": "/hello" } }],
"responses": [{ "is": { "statusCode": 200, "body": "Hello, World!" } }]
}]
}'
# Test it
curl http://localhost:4545/hello# Load your existing imposters.json
docker run -p 2525:2525 -v $(pwd)/imposters.json:/imposters.json \
zainalpour/rift-proxy:latest --configfile /imposters.jsondocker pull zainalpour/rift-proxy:latestbrew tap achird-labs/rift
brew install riftcargo install rift-http-proxyDownload pre-built binaries from GitHub Releases:
# Example for Linux x86_64
curl -LO https://github.com/achird-labs/rift/releases/latest/download/rift-vX.X.X-x86_64-unknown-linux-gnu.tar.gz
tar -xzf rift-vX.X.X-x86_64-unknown-linux-gnu.tar.gz
sudo mv rift-vX.X.X-x86_64-unknown-linux-gnu/bin/* /usr/local/bin/Available platforms:
- Linux:
x86_64-unknown-linux-gnu,aarch64-unknown-linux-gnu,x86_64-unknown-linux-musl,aarch64-unknown-linux-musl - macOS:
x86_64-apple-darwin,aarch64-apple-darwin - Windows:
x86_64-pc-windows-msvc
git clone https://github.com/achird-labs/rift.git
cd rift
cargo build --release
./target/release/rift-http-proxyFor Node.js projects, use the official rift-node SDK
(@rift-vs/rift on npm). It runs the engine three ways — embedded in-process (FFI via the
companion @rift-vs/rift-embedded package, no Docker), connected to any running admin endpoint, or
as a managed spawned binary — with a fluent DSL for stubs/predicates/responses/scenarios, plus
Vitest and Jest testkits. Requires Node.js >= 20, ESM-only, zero runtime dependencies:
npm install @rift-vs/riftimport { rift, imposter, onGet, okJson, times } from '@rift-vs/rift';
await using engine = await rift.embedded(); // or rift.connect(url) / rift.spawn()
const users = await engine.create(
imposter('users').stub(onGet('/api/users/1').willReturn(okJson({ id: 1, name: 'Alice' }))));
await fetch(`${users.url}/api/users/1`);
await users.verify(onGet('/api/users/1'), times(1)); // throws with a diff on mismatchAlready on Mountebank, or migrating from the pre-monorepo @rift-vs/rift? The Mountebank-compatible
create() stays available as a permanent drop-in, so adopting the typed DSL above is incremental,
not a forced rewrite.
See the rift-node docs for the full feature surface.
For JVM projects, use the official rift-java SDK.
It runs the engine three ways — embedded in-process (Panama FFM, no Docker), connected to any
running admin endpoint, or as a managed spawned binary — with a fluent DSL plus JUnit 5, Spring,
and Testcontainers integrations. Available on Maven Central under io.github.achird-labs:
<dependency>
<groupId>io.github.achird-labs</groupId>
<artifactId>rift-java-core</artifactId>
<scope>test</scope>
</dependency>try (Rift rift = Rift.embedded()) { // or Rift.connect(uri) / Rift.spawn()
Imposter users = rift.create(
imposter("users").stub(onGet("/api/users/1").willReturn(okJson("{\"id\":1}"))));
// point your SUT at users.uri(), then assert:
users.verify(onGet("/api/users/1"), times(1));
}See the rift-java docs for the full feature surface.
For Scala 3, use the official rift-scala SDK. It is effect-library-native — ZIO, Cats Effect 3 / FS2, Kyo, or no effect system at all — over the same four transports (embedded, connect, spawn, container):
libraryDependencies += "io.github.achird-labs" %% "rift-scala-zio" % "0.1.0" % Testimport rift.dsl.*
import rift.zio.Rift
for
users <- Rift.create(
imposter("users").record.stub(
get("/api/users/1").reply(ok.json("""{"id":1}"""))
)
)
_ <- callSut(users.uri) // point your SUT at users.uri
_ <- users.verify(get("/api/users/1"), 1)
yield ()
// .provideShared(Rift.embedded) — or Rift.connect(uri) / Rift.spawn() / Rift.container()It also ships a zio-bdd MockControl adapter, certified
against zio-bdd's published conformance catalogue. See the
rift-scala docs.
- Installation - Docker, binary, build from source
- Quick Start - Create your first imposter
- Node.js Integration - npm package for Node.js
- Java / JVM SDK - rift-java for JUnit 5, Spring, and Testcontainers
- Scala SDK - rift-scala for ZIO, Cats Effect, FS2, and zio-bdd
- Migration Guide - Using Rift with Mountebank configs
- Imposters - Mock server configuration
- Predicates - Request matching
- Responses - Response configuration
- Behaviors - wait, decorate, copy
- Proxy Mode - Record and replay
- Mountebank Format - JSON configuration
- Native Rift Format - YAML for advanced features
- CLI Reference - Command-line options
- Fault Injection - Chaos engineering
- Scripting - Rhai, JavaScript
- TLS/HTTPS - Secure connections
- Metrics - Prometheus integration
- TUI - Interactive terminal interface
- Docker - Container deployment
- Kubernetes - K8s patterns
- REST API - Admin API reference
- Performance - Benchmarks
{
"port": 4545,
"protocol": "http",
"name": "User Service",
"stubs": [
{
"predicates": [{ "equals": { "method": "GET", "path": "/users" } }],
"responses": [{
"is": {
"statusCode": 200,
"headers": { "Content-Type": "application/json" },
"body": [{ "id": 1, "name": "Alice" }]
}
}]
},
{
"predicates": [{
"and": [
{ "equals": { "method": "GET" } },
{ "matches": { "path": "/users/\\d+" } }
]
}],
"responses": [{
"is": { "statusCode": 200, "body": { "id": 1, "name": "Alice" } }
}]
}
]
}More examples in examples/.
Prometheus metrics on :9090/metrics:
curl http://localhost:9090/metricsMetrics include request counts, latency histograms, fault injection stats, and more.
Rift includes additional command-line tools. All tools are included when you install via Homebrew or download release binaries.
Manage imposters and stubs through an interactive terminal interface:
# If installed via Homebrew or release binary
rift-tui
# Connect to a different admin URL
rift-tui --admin-url http://localhost:2525Features:
- View and manage imposters with vim-style navigation (j/k)
- Create, edit, and delete stubs with JSON editor
- Generate curl commands for testing stubs
- Import/export imposter configurations
- Search and filter imposters and stubs
- Real-time metrics dashboard
Automatically test your imposters by generating requests from predicates:
rift-verify --show-curlValidate imposter configuration files before loading:
# If installed via Homebrew or release binary
rift-lint ./imposters/
# Via Docker (for CI/CD)
docker run --rm -v $(pwd):/imposters zainalpour/rift-lint .
# Via cargo
cargo install rift-lint
rift-lint ./imposters/# Build
cargo build --release
# Run tests
cargo test --all
# Run with debug logging
RUST_LOG=debug ./target/release/rift-http-proxy
# Run benchmarks (Rift vs Mountebank; see tests/benchmark/README.md)
cd tests/benchmark && python3 scripts/bench_direct.py --run-all \
--rift-bin ../../target/release/rift-http-proxy \
--mb-bin ~/bench-mb/node_modules/mountebank/bin/mbRift powers HTTP mocking in the following projects:
- zio-bdd — a Gherkin-style BDD testing framework for ZIO
- zio-openfeature — a ZIO-native wrapper around the OpenFeature Java SDK
Using Rift somewhere? Open a PR to add it here.
Contributions welcome! Please read our contributing guidelines and submit PRs.
Apache License 2.0 - see LICENSE for details.
- Mountebank - The original service virtualization tool that inspired Rift's API and configuration format