Back to Blog
7 Protocols, 1 Binary, 0 Dependencies
Insights 8 min read

7 Protocols, 1 Binary, 0 Dependencies

HTTP, GraphQL, gRPC, WebSocket, MQTT, SSE, and SOAP from a single 43MB Go binary. No JVM, no Node, no Docker required. Here's why and how.

ZS
Zach Snell
March 7, 2026

Last year I counted the mock tools running on my laptop. WireMock for HTTP (Java, 200MB Docker image). A custom Node script for WebSocket. Mosquitto for MQTT. A Go stub for gRPC. Four tools, four runtimes, four sets of config files, four ports to remember. I spent more time wiring up mock infrastructure than writing the code it was supposed to help me test.

So I built mockd. One binary:

mockd add http --path /api/orders --status 200 
  --body '{"orders": [{"id": "{{uuid}}", "total": {{faker.price}}}]}'
Created mock: http_c9e4c315168bcfbe / Type: http / Method: GET / Path: /api/orders / Status: 200
curl http://localhost:4280/api/orders
{"orders": [{"id": "7e53e8ca-...", "total": 405.87}]}

HTTP done. Same binary, six more protocols to go.

All 7 protocols

Every protocol follows the same pattern: mockd add <type>, configure, hit the endpoint. No plugins. No extensions. No separate downloads.

GraphQL

mockd add graphql --path /graphql --operation GetUser 
  --response '{"id":"1","name":"{{faker.name}}","email":"{{faker.email}}"}'
Created mock: graphql_... / Type: graphql / Path: /graphql / Operation: Query.GetUser

Test it: curl -X POST http://localhost:4280/graphql -H 'Content-Type: application/json' -d '{"query":"{ GetUser }"}' returns {"data":{"GetUser":{"email":"alice684@mock.io","id":"1","name":"Charlie Brown"}}}.

gRPC

mockd add grpc --proto service.proto --service greeter.Greeter 
  --rpc-method SayHello --response '{"message": "Hello, {{faker.name}}!"}'
Created mock: grpc_... / Type: grpc / Service: greeter.Greeter / Method: SayHello

You provide the .proto file, mockd handles reflection and serialization. Port 50051 by default.

WebSocket

mockd add websocket --path /ws/chat 
  --message '{"event":"connected","ts":"{{now}}"}'
Created mock: websocket_... / Type: websocket / Path: /ws/chat

MQTT

mockd add mqtt --topic sensors/temp 
  --payload '{"temp":{{random.float 20.0 35.0}},"unit":"C","ts":"{{now}}"}'
Created mock: mqtt_... / Type: mqtt / Topic: sensors/temp / QoS: 0

Built-in MQTT broker on port 1883. No Mosquitto required.

SSE (Server-Sent Events)

mockd add http --path /events --sse 
  --sse-event 'update:{"ts":"{{now}}","cpu":{{random.int 10 95}}}'
Created mock: http_... / Type: http / Method: GET / Path: /events

curl -N http://localhost:4280/events streams event:update / data:{"ts":"2026-03-07T18:26:03-06:00","cpu":70}.

SOAP

mockd add soap --path /service --operation GetUser --status 200 
  --response '<User><Id>1</Id><Name>{{faker.name}}</Name></User>'
Created mock: soap_... / Type: soap / Path: /service / Operation: GetUser

Send a SOAP envelope with SOAPAction: GetUser, get back a properly wrapped response.

All together

ID                    TYPE       PATH             METHOD  STATUS  ENABLED
list-users            http       /api/users       GET     200     true
graphql_917e99253...  graphql    /graphql         GQL     -       true
websocket_91720db...  websocket  /ws/chat         WS      -       true
mqtt_4c28d4810103...  mqtt       :1883            MQTT    -       true
http_32897b6a3bd8...  http       /events          GET     -       true
soap_edc045dfba00...  soap       /service         SOAP    -       true
grpc_7a2ea7f56902...  grpc       :50051           gRPC    -       true

Seven protocols. One process. One config file. One set of logs.

Why Go, why single binary

I get asked this a lot. Go was chosen for three reasons:

Static binary. CGO_ENABLED=0 go build produces a single statically-linked executable. No shared libraries, no runtime, no interpreter. Copy it to any Linux/macOS/Windows machine and it runs. Docker image is distroless/static — no shell, no libc.

Goroutines. Each protocol listener runs in its own goroutine. HTTP, gRPC, MQTT, WebSocket — same process, none blocking each other. In Node that’s clustering. In Java it’s thread pools. In Go it’s go serve().

Cross-compilation. GOOS=darwin GOARCH=arm64 go build from a Linux CI runner. Mockd ships six OS/arch binaries from a single GitHub Actions workflow.

The result is a 43MB binary containing all seven protocol servers, 34 faker functions, an admin API, and a request logger. No runtime to download. No plugins to install.

That matters in three places: CI/CD (curl -fsSL https://get.mockd.io | sh && mockd start — two seconds vs. pulling a 200MB Docker image), laptop onboarding (“install mockd” vs. “install Java 17 and Docker Desktop”), and air-gapped networks (a single binary can be SCP’d to a bastion host — try that with node_modules).

Shared internals

The seven protocols share core infrastructure:

  • Template engine{{faker.name}}, {{uuid}}, {{now}}, {{random.int 1 100}} — same 34 faker functions across every protocol
  • Config — one YAML or JSON file defines all mocks across all protocols
  • Request logging — HTTP, GraphQL, gRPC, WebSocket, MQTT traffic in one unified log
  • Import — OpenAPI, Postman, HAR, WireMock, cURL, Mockoon, and WSDL formats
  • Chaos — 10 profiles and 12 fault types apply across HTTP, GraphQL, gRPC, and SOAP
  • MCP — 18 tools give AI agents programmatic access to everything

How this compares

ToolLanguageProtocolsSizeRuntime
mockdGo7 (HTTP, GraphQL, gRPC, WS, MQTT, SSE, SOAP)43MB binaryNone
WireMockJavaHTTP (extensions for others)~200MB DockerJVM
MockoonElectron/NodeHTTP, GraphQL~200MB appNode
json-serverNodeREST~2MB (+ Node)Node
PrismNodeHTTP (OpenAPI validation)~50MB (+ Node)Node
MockServerJavaHTTP/HTTPS~200MB DockerJVM

WireMock is battle-tested with a huge plugin ecosystem. Mockoon has a desktop UI nothing else matches. json-server is dead simple for REST prototyping. Mockd’s angle: all seven protocols from one tool, zero runtime dependencies, sub-second startup.

The honest part

43MB is not small. It’s smaller than pulling a JVM or Node runtime, but it’s not a 5MB CLI tool. Every protocol adds weight — gRPC alone brings in protobuf libraries. I could strip features to shrink the binary, but that defeats the point.

gRPC requires a .proto file. There’s no way around this. gRPC is contract-driven — you can’t hand-wave the schema like you can with HTTP. No .proto file, no gRPC mock. That’s a protocol constraint, not a mockd constraint, but it still adds friction.

The MQTT broker is basic. QoS 0 and 1, topic wildcards, pub/sub. No clustering, no persistence, no QoS 2. For dev/test it’s fine. For benchmarking against a real broker, use Mosquitto or EMQX.

SOAP exists because it has to. Banks, insurance, healthcare — I didn’t add SOAP because I enjoy XML. I added it because developers at those companies deserve mock tooling that isn’t “install a 2008 Java web server.”

Install

brew install getmockd/tap/mockd
# or
curl -fsSL https://get.mockd.io | sh

Start the server and start adding mocks. Every protocol. One binary. Zero dependencies.

Learn more

Links

#architecture#golang#mock server#multi-protocol#devtools

Try mockd

Multi-protocol API mock server. HTTP, gRPC, GraphQL, WebSocket, MQTT, SSE, SOAP.

curl -fsSL https://get.mockd.io | sh