Rift¶
High-performance Mountebank-compatible HTTP/HTTPS mock server written in Rust
Rift implements Mountebank's REST API and imposter format, so most existing
imposters.json files and Mountebank clients work unchanged. It keeps its throughput flat as a stub file
grows, and the same engine embeds in-process in Java, Node, Go and Scala.
Rift is compatible with Mountebank 2.9.5, the latest release
(@mbtest/mountebank). Every pull request that changes
the engine runs a differential suite that sends the same requests to a real Mountebank and to Rift. The
differences it finds are listed in Differences from Mountebank. Compatibility
is established by those tests, so behaviour they do not exercise can still differ: if you find a case,
open an issue.
Why Rift?¶
Mountebank Compatible¶
Rift implements the Mountebank REST API, so you can usually:
- load existing Mountebank configuration files without changes;
- keep your current Mountebank clients and test tooling;
- check the documented differences for anything your tests rely on.
Performance¶
Rift indexes stubs instead of scanning them in order, so its cost does not grow with the size of the stub file. Measured against Mountebank on the same machine:
| Feature | Mountebank | Rift | Speedup |
|---|---|---|---|
| Static stub (first match) | 7,240 RPS | 209,192 RPS | 29x faster |
| Regex (100th pattern) | 110 RPS | 201,067 RPS | 1,821x faster |
| JSONPath predicates | 4,586 RPS | 190,329 RPS | 42x faster |
| API stub — no match (404) | 1,376 RPS | 204,358 RPS | 149x faster |
| Complex predicates | 4,884 RPS | 185,867 RPS | 38x faster |
Apple M4 laptop, 50 connections, median of 3 repetitions, 2026-09-17. On a 16-vCPU AMD EPYC server the same suite reaches 325k RPS and 6,160x on regex — but Mountebank is slower there, so the M4 figures above are the conservative read.
See the performance page for both hosts, the full suite, and the method.
Mountebank Features¶
Rift supports Mountebank's HTTP and HTTPS feature set (not its TCP and SMTP protocols):
- Imposters - HTTP/HTTPS mock servers on any port, including mutual TLS
- Stubs - Request matching with responses
- Predicates - equals, contains, matches, exists, jsonpath, xpath, and, or, not
- Responses - Static, proxy, injection with behaviors
- Behaviors - wait, decorate, copy, lookup, shellTransform
- Recording - Proxy mode with response recording
And Then Some¶
Compatibility is the floor, not the ceiling. Rift adds the things you would otherwise build by hand on top of a mock server:
- Fault Injection - probabilistic latency, error and TCP faults, declaratively
- Scripting - Rhai and JavaScript engines, with a
script check/script runCLI - Scenarios and Flow State - declarative state machines and a per-flow key/value store
- Correlated Isolation - per-flow partitioning so parallel tests don't collide
- Front Door and Gateway - route many imposters through one listener
- Intercept Proxy - TLS-MITM a hard-coded external host without mitmproxy, over HTTP/1.1 or HTTP/2, with WebSocket upgrades relayed to the real origin
- TLS & Mutual TLS - HTTPS imposters that require and validate client certificates, plus a private-CA trust store for proxying
- Response Templates - date tokens and the
_rift.templated{{ }}grammar, no script engine needed - Stub Analysis and Debug Mode - find shadowed stubs, and see why a request matched
- Conditional GET and Vendor-Mock Templates -
ETag/304without hand-synced validators, and ready-made mocks for third-party SaaS - Embedding & FFI - run the engine in-process from Rust or any language over the C ABI
The Features section covers all of them.
Quick Start¶
Using Docker (Recommended)¶
# Pull the latest image from Docker Hub
docker pull zainalpour/rift-proxy:latest
# Run Rift (Mountebank-compatible mode)
docker run -p 2525:2525 zainalpour/rift-proxy:latest
Create Your First Imposter¶
# Create an imposter that responds to GET /hello
curl -X POST http://localhost:2525/imposters \
-H "Content-Type: application/json" \
-d '{
"port": 4545,
"protocol": "http",
"stubs": [{
"predicates": [{ "equals": { "method": "GET", "path": "/hello" } }],
"responses": [{ "is": { "statusCode": 200, "body": "Hello, World!" } }]
}]
}'
# Test the imposter
curl http://localhost:4545/hello
# Output: Hello, World!
Using an Existing Mountebank Config¶
# Start Rift with your existing imposters.json file
docker run -p 2525:2525 -v $(pwd)/imposters.json:/imposters.json \
zainalpour/rift-proxy:latest --configfile /imposters.json
Node.js Integration¶
For Node.js projects, use the official npm package:
import { 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 mismatch
Migrating from Mountebank? The Mountebank-compatible rift.create({ port }) API stays available as
a permanent drop-in, so adopting the typed DSL above is incremental rather than a rewrite.
See the Node.js Integration Guide for complete documentation.
Java / JVM Integration¶
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.
<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 system under test at users.uri(), then assert:
users.verify(onGet("/api/users/1"), times(1));
}
See the rift-java documentation for the full feature surface, and the BOM for version-pinning every module at once.
Go Integration¶
For Go projects, use the official rift-go SDK. It runs the
engine three ways — embedded in-process, connected to any running admin endpoint, or as a managed
spawned binary — with a fluent DSL plus testing.T helpers.
The embedded transport loads the engine through
purego rather than cgo, so CGO_ENABLED=0 keeps
working: no C toolchain, and cross-compilation is unaffected.
go get github.com/achird-labs/rift-go
go run github.com/achird-labs/rift-go/cmd/rift-fetch@latest -version v0.24.0
func TestUserLookup(t *testing.T) {
users := rifttest.Imposter(t, rift.NewImposter("users").
Stub(rift.OnGet("/api/users/1").
Return(rift.OKJSON(map[string]rift.JSON{"id": 1, "name": "Alice"}))))
callSUT(t, users.BaseURL())
rifttest.AssertReceived(t, users, rift.OnGet("/api/users/1"), rift.Once())
}
See the rift-go documentation for the full feature surface.
All four SDKs¶
Java, Scala, Node/TypeScript and Go are all officially supported and all replay the same conformance corpus. The Language SDKs section has the install snippet and hello-world for each, plus the transport and version-compatibility matrices.
Documentation¶
Getting Started¶
- Installation - Docker, binary, and build from source
- Quick Start - Create your first imposter
- Node.js Integration - npm package for Node.js projects
- Language SDKs - Java, Scala, Node/TypeScript and Go, with the transport and version-compatibility matrices
- Java / JVM SDK - rift-java for JUnit 5, Spring, and Testcontainers
- Scala SDK - rift-scala for ZIO, Cats Effect, FS2, and zio-bdd
- Go SDK - rift-go for
testing.T, embedded via purego (no cgo) - Migration from Mountebank - Switch from Mountebank to Rift
Concepts¶
- Concepts Overview - The Rift mental model, start here
- Core Building Blocks - Imposters, stubs, predicates, responses, behaviors
- The Rift Model - Flow-state, scenarios, and correlated isolation
Mountebank Compatibility (reference)¶
- Imposters - Creating and managing mock servers
- Predicates - Request matching (equals, contains, regex, jsonpath, xpath)
- Responses - Configuring stub responses
- Behaviors - Response modification (wait, decorate, copy, lookup, shellTransform)
- Proxy Mode - Recording and replaying responses
Configuration¶
- Mountebank Format - JSON configuration reference
- Rift Extensions - The
_riftnamespace and other Rift-specific keys - CLI Reference - Command-line options
Features¶
- Features Overview - Every extension, with the Mountebank comparison table
- Fault Injection - Latency and error simulation
- Scripting - Rhai and JavaScript engines
- Scenarios (FSM) - Stateful stubs as declarative state machines
- Correlated Isolation (Spaces) - Per-flow stub and state partitioning
- Flow State - Per-flow key/value store
- Front Door - One listener routing to many imposters
- Single-Port Gateway - Reach every imposter through the admin port
- Intercept Proxy (TLS-MITM) - Mock a hard-coded external HTTPS host (HTTP/1.1, HTTP/2, WebSocket passthrough)
- Response Templates - Date tokens and the
_rift.templatedgrammar - Hot Reload - Re-read config without restarting
- Stub Analysis - Overlap detection and warnings
- Debug Mode - Why a request matched, or didn't
- TLS/HTTPS - HTTPS imposters, mutual TLS, outbound trust for private CAs
- Metrics - Prometheus integration
- Configuration Linting - Validate configs before they load
- Terminal UI - Interactive imposter management
Deployment¶
- Docker - Container deployment
- Kubernetes - K8s deployment patterns
- ECS / Fargate - Reference task definition, including the intercept proxy
Reference¶
- REST API - Admin API reference
- Examples - Ready-to-use imposter configurations
- Templates - Vendor-mock templates for third-party SaaS
- Performance - Benchmark results
- Rift vs WireMock - Where each one wins, and when not to switch
- Rift vs Microcks - Spec-driven vs stub-driven, and where they overlap
- Changelog - Notable user-facing changes
Embedding & Extension¶
- Embedding & SPI - Embed Rift as a library, extend it via SPI traits
- Embeddable Server -
ServerBuilder, bindable admin/metrics - Extension Points (SPI) - Pluggable flow-store, journal, proxy store, sequencer, plus exchange-inspector, no-match, admin-authorizer and front-door observer hooks
- FFI (C-ABI) - Drive Rift from any language
Project Status¶
The HTTP/HTTPS surface is stable and actively developed. Current status:
| Area | Status |
|---|---|
| HTTP / HTTPS imposters, including mutual TLS | Stable |
| All predicates, static responses, behaviors | Stable |
| Proxy mode (record & replay) | Stable |
| JavaScript injection | Stable |
| Fault injection, scripting (Rhai / JS) | Stable |
| Scenarios, flow state, correlated isolation | Stable |
| Front door, single-port gateway | Stable |
| Intercept proxy (TLS-MITM, HTTP/2, WebSocket passthrough) | Stable |
| Stub analysis, debug mode, hot reload | Stable |
| Prometheus metrics, linting, terminal UI | Stable |
| Embedding (Rust API, C ABI) and the four SDKs | Stable |
| TCP protocol | Not supported |
| SMTP protocol | Not supported |
License¶
Rift is distributed under the Apache License 2.0.