ProtoMQ

An MQTT 3.1.1 broker that validates and routes Protobuf messages at the network layer. Written in Zig. MIT licensed.

GitHub Benchmarks FAQ dev · Zig 0.15.2+

What it does

Standard MQTT brokers are payload-agnostic — they forward bytes without knowing or caring what is inside. ProtoMQ adds a schema layer on top: each topic is mapped to a Protobuf message type, and the broker validates every incoming PUBLISH against that schema before routing it to subscribers.

Invalid payloads are dropped at the broker, so consumers never have to write defensive parsing code. Schemas are plain .proto files — no code generation, no build step.

Usage

Requires Zig 0.15.2 or later. Download from ziglang.org.

# Build
zig build -Doptimize=ReleaseSafe

# Run
./zig-out/bin/protomq-server

# CLI — publish with automatic JSON → Protobuf encoding
./zig-out/bin/protomq-cli publish -t sensor/data \
  --json '{"device_id":"pi5","temperature":21.3,"humidity":55.0}'

# CLI — discover schemas from the running broker
./zig-out/bin/protomq-cli discover --proto-dir schemas

Drop .proto files into the schemas/ directory. They are parsed on startup. Map a topic to a message type in src/server/tcp.zig:

// src/server/tcp.zig — TcpServer.init()
try server.schema_manager.loadSchemasFromDir("schemas");
try server.schema_manager.mapTopicToSchema("sensor/data", "SensorData");

Then rebuild and restart. Or do it at runtime — see the Admin API section below.

Admin API

Build with -Dadmin_server=true to enable an HTTP server on 127.0.0.1:8080. When the flag is off, the HTTP code is entirely absent from the binary — not just disabled.

zig build -Doptimize=ReleaseSafe -Dadmin_server=true

All endpoints require Authorization: Bearer <ADMIN_TOKEN> (env var, defaults to admin_secret).

Method Path Description
GET /metrics Active connections, messages routed, loaded schema count
GET /api/v1/schemas Current topic → message-type mappings as JSON
POST /api/v1/schemas Register a new schema and mapping at runtime, no restart needed
# Register a schema at runtime
curl -X POST http://127.0.0.1:8080/api/v1/schemas \
  -H "Authorization: Bearer admin_secret" \
  -H "Content-Type: application/json" \
  -d '{
    "topic": "telemetry/gps",
    "message_type": "GpsCoord",
    "proto_file_content": "syntax = \"proto3\";\nmessage GpsCoord { float lat = 1; float lon = 2; }"
  }'

# Check metrics
curl -s -H "Authorization: Bearer admin_secret" http://127.0.0.1:8080/metrics
# {"connections":3,"messages_routed":142,"schemas":2,"memory_mb":0}

Performance

Benchmarked on two machines. Numbers are from the full benchmark suite in benchmarks/.

Scenario Apple M2 Pro Raspberry Pi 5
p99 latency · 100 clients 0.44 ms 0.13 ms
Concurrent connections 10,000 10,000
Sustained throughput 9,000 msg/s 9,000 msg/s
Peak throughput (small msgs) 208,000 msg/s 147,000 msg/s
Memory · 100 clients 2.6 MB 2.5 MB
Memory leaks · 100k cycles 0 0

Memory baseline increases from ~2.6 MB to ~4.0 MB when the Admin Server is compiled in. The Admin Server shares the same event loop as the MQTT broker — it does not run on a separate thread.