The baseline TrailMQ stack. Use this recipe when you want:
- TLS-secured MQTT broker
- REST API for control and audit retrieval
- Web UI via reverse proxy
- Cryptographic audit chain
- Policy-based access control
Nothing more. This is the foundation every other recipe builds on.
| Service | Image | Purpose |
|---|---|---|
| backend | rainergewalt/trailmq-backend |
MQTT broker + REST API + audit |
| frontend | rainergewalt/trailmq-frontend |
Web UI |
| nginx | nginx:stable-alpine |
Reverse proxy + TLS termination |
| Port | Purpose |
|---|---|
| 80 | HTTP / Web UI |
| 8883 | MQTT over TLS |
The backend REST API runs inside the stack on port 8443. Public access goes
through the reverse proxy on port 80.
Default local URLs:
| Surface | URL |
|---|---|
| Web UI | http://localhost/trailmq/ |
| REST API | http://localhost/api/v1 |
| MQTT TLS | localhost:8883 |
| MQTT WS | ws://localhost/mqtt |
The certs/ folder must contain:
server_cert.pemserver_key.pemca_cert.pem
./trailmq launch offers to generate local demo certificates automatically,
or you can run ./trailmq certs at any time. The launcher also generates a
JWT secret into secrets/jwtsecret.txt if openssl is available.
Two evaluation users ship with this recipe. They are for local evaluation only — change or disable them before any non-local deployment.
| User | Role | Credential source |
|---|---|---|
testadmin |
admin | secrets/testadmin.pwd (generated by launcher) |
testuser |
publisher | secrets/testuser.pwd (generated by launcher) |
On first run, ./trailmq launch generates random passwords into those files
and prints them once to stdout. Re-run cat secrets/testadmin.pwd if you lose
track of them. To rotate: delete the files and re-launch.
The active config.yaml intentionally contains only testadmin and
testuser. Do not keep disabled demo users with missing password_file
entries in the active config, because the backend validates password files
while loading the configuration.
Examples for password_file, password_hash, and placeholder plain-password
users live in:
docs/config-examples/users.yaml
Copy an example into config.yaml only after setting real credentials.
TrailMQ provides a REST API for controlling and reviewing MQTT behavior.
The Web UI uses the same API, so the most important product functions can also be accessed from scripts, local checks, monitoring tools or external integrations.
Use the API to answer questions like:
- Which MQTT topics are controlled?
- Which settings apply to a topic?
- Which policy applies to a message path?
- What is currently queued or dead-lettered?
- What was recorded in the audit trail?
- Is the audit chain still valid?
- Which evidence-oriented records can be exported for review?
Default local API endpoint:
http://localhost/api/v1
The local evaluation setup uses configured users, not open self-registration.
Read the generated admin password:
cd recipes/secure-mqtt-core
ADMIN_USER="testadmin"
ADMIN_PASS="$(cat secrets/testadmin.pwd)"Login and receive a JWT:
TOKEN="$(
curl -sS -X POST "http://localhost/api/v1/auth" \
-H "Content-Type: application/json" \
-d "{\"username\":\"${ADMIN_USER}\",\"password\":\"${ADMIN_PASS}\"}" \
| jq -r '.token // .accessToken // .data.token // .data.accessToken'
)"Check the authenticated user:
curl -sS "http://localhost/api/v1/auth/me" \
-H "Authorization: Bearer ${TOKEN}" \
| jqMost control and review endpoints require:
Authorization: Bearer <token>Use these endpoints for basic smoke tests, Docker health checks, monitoring and local readiness checks.
| Method | Endpoint | What it tells you |
|---|---|---|
GET |
/health |
REST service is reachable |
GET |
/live |
Process is alive |
GET |
/ready |
TrailMQ is ready to serve broker/API work |
GET |
/api/v1/version |
Running version/build information |
GET |
/api/v1/metrics |
JSON metrics |
GET |
/api/v1/metrics/json |
JSON metrics |
GET |
/api/v1/metrics/prometheus |
Prometheus-style metrics |
GET |
/metrics |
Prometheus-style metrics |
Example:
curl -sS http://localhost/ready | jq
curl -sS http://localhost/api/v1/metrics | jqProduct meaning:
Is TrailMQ running, reachable and ready before MQTT traffic is sent through it?
Topics are the controlled MQTT namespace in TrailMQ.
Use these endpoints to create, inspect and configure MQTT paths. This includes active state, QoS, queue behavior, audit settings and effective runtime configuration.
| Method | Endpoint | Product purpose |
|---|---|---|
GET |
/api/v1/topics |
List controlled MQTT topics |
POST |
/api/v1/topics |
Create a controlled topic |
GET |
/api/v1/topics/tree |
Inspect the topic hierarchy |
POST |
/api/v1/topics/tree |
Create a topic path with parent chain |
POST |
/api/v1/topics/autocreate |
Auto-create a topic for local evaluation |
GET |
/api/v1/topics/{topic}/settings |
Read topic behavior |
PUT |
/api/v1/topics/{topic}/settings |
Update queue, QoS, audit or active settings |
GET |
/api/v1/topics/by-name/{path} |
Read topic by MQTT path |
DELETE |
/api/v1/topics/by-name/{path} |
Delete topic by MQTT path |
GET |
/api/v1/topics/by-name/{path}/effective |
Show effective runtime configuration |
Example:
curl -sS -X POST "http://localhost/api/v1/topics" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "demo/line1/temperature",
"qosLevel": 1,
"isActive": true,
"queueEnabled": true,
"queueSize": 1000,
"auditTrail": true
}' | jqCheck what TrailMQ will effectively apply:
curl -sS "http://localhost/api/v1/topics/by-name/demo/line1/temperature/effective" \
-H "Authorization: Bearer ${TOKEN}" \
| jqProduct meaning:
Which MQTT paths are controlled, and what behavior applies to messages on this path?
Topic rules help apply behavior to topic patterns instead of configuring every topic individually.
| Method | Endpoint | Product purpose |
|---|---|---|
GET |
/api/v1/topic-rules |
List topic rules |
POST |
/api/v1/topic-rules |
Create a topic rule |
GET |
/api/v1/topic-rules/{id} |
Read one topic rule |
PUT |
/api/v1/topic-rules/{id} |
Update a topic rule |
DELETE |
/api/v1/topic-rules/{id} |
Delete a topic rule |
GET |
/api/v1/topic-rules/validate |
Validate a topic rule pattern |
GET |
/api/v1/topic-rules/match/{topic} |
Test which rules match a topic |
POST |
/api/v1/topic-rules/apply |
Apply topic rules |
Product meaning:
Which rule affects this topic, and why does the topic behave this way?
Policies describe how TrailMQ should treat messages on selected topic paths.
They can be used to inspect, resolve and validate message behavior before or during local evaluation.
| Method | Endpoint | Product purpose |
|---|---|---|
GET |
/api/v1/policies |
List policies |
POST |
/api/v1/policies |
Create a policy |
GET |
/api/v1/policies/{id} |
Read a policy |
PUT |
/api/v1/policies/{id} |
Update a policy |
DELETE |
/api/v1/policies/{id} |
Delete a policy |
GET |
/api/v1/policies/stats |
Inspect policy usage |
GET |
/api/v1/policies/violations |
Inspect denied or invalid behavior |
POST |
/api/v1/policies/resolve |
Resolve policy for a topic |
POST |
/api/v1/policies/validate |
Validate a message against policy |
POST |
/api/v1/policies/handshake |
Generate client policy handshake |
GET |
/api/v1/policies/handshake/{clientId} |
Read client policy handshake |
GET |
/api/v1/policies/bindings |
List policy-to-topic bindings |
POST |
/api/v1/policies/bindings |
Bind a policy to a topic pattern |
Create a policy:
curl -sS -X POST "http://localhost/api/v1/policies" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"id": "demo-policy",
"description": "Demo policy",
"maxPayloadKb": 64,
"qosRequired": 1,
"onViolation": "deny",
"auditLevel": "basic",
"enabled": true
}' | jqBind it to a topic pattern:
curl -sS -X POST "http://localhost/api/v1/policies/bindings" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"pattern": "demo/line1/#",
"policyId": "demo-policy",
"priority": 100,
"enabled": true
}' | jqResolve the policy for a concrete topic:
curl -sS -X POST "http://localhost/api/v1/policies/resolve" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{"topic":"demo/line1/temperature"}' | jqProduct meaning:
Why would this MQTT message be allowed, denied, audited or treated as a violation?
Queue endpoints show how TrailMQ behaves when messages cannot be delivered immediately or need to be isolated for later review.
| Method | Endpoint | Product purpose |
|---|---|---|
GET |
/api/v1/queue/status |
Inspect global queue health |
GET |
/api/v1/queue/deadletter |
Review dead-lettered messages |
PUT |
/api/v1/queue/config |
Update global queue behavior |
POST |
/api/v1/queue/purge/{topic} |
Reset queue state for one topic |
GET |
/api/v1/queues/{topic}/stats |
Inspect per-topic queue state |
GET |
/api/v1/queues/{topic}/messages |
Read queued messages |
POST |
/api/v1/queues/{topic}/enqueue |
Enqueue a test message |
POST |
/api/v1/queues/{topic}/dispatch |
Dispatch queued message |
POST |
/api/v1/queues/{topic}/ack |
Acknowledge delivery |
POST |
/api/v1/queues/{topic}/nack |
Negative acknowledge delivery |
Example:
curl -sS "http://localhost/api/v1/queue/status" \
-H "Authorization: Bearer ${TOKEN}" \
| jqProduct meaning:
If delivery is delayed or fails, can I see what happened instead of losing visibility?
Audit endpoints expose the reviewable record of what happened inside TrailMQ.
They are useful for evaluation, debugging, traceability checks and exported review data. TrailMQ can support evidence-oriented workflows, but using TrailMQ does not by itself make a system compliant with GMP, GxP, 21 CFR Part 11 or any other regulatory framework.
| Method | Endpoint | Product purpose |
|---|---|---|
GET |
/api/v1/audit |
List audit entries |
GET |
/api/v1/audit/summary |
Read audit summary |
GET |
/api/v1/audit/summary/extended |
Read extended audit summary |
GET |
/api/v1/audit/validatechain |
Validate audit chain |
GET |
/api/v1/audit/validatechain/details |
Inspect chain validation details |
GET |
/api/v1/audit/contract-status |
Check evidence contract status |
POST |
/api/v1/audit/contract-status/reset |
Reset evidence contract status |
GET |
/api/v1/audit/search |
Search audit records |
GET |
/api/v1/audit/auth |
List authentication audit events |
GET |
/api/v1/audit/auth/stats |
Read authentication audit stats |
GET |
/api/v1/audit/export?format=json |
Export audit data |
GET |
/api/v1/audit/export/json |
Export audit data as JSON |
Some builds also expose focused review views such as /api/v1/audit/gmp.
Treat these as audit/event views for evaluation and review, not as compliance
certification.
Example:
curl -sS "http://localhost/api/v1/audit/validatechain/details" \
-H "Authorization: Bearer ${TOKEN}" \
| jqProduct meaning:
What happened, was it recorded, and is the audit chain still intact?
Product read models provide higher-level views over broker, queue, audit and capability state.
They are useful for dashboards, demos, evaluation reports and external tooling that should not need to reconstruct product state from low-level records.
| Method | Endpoint | Product view |
|---|---|---|
GET |
/api/v1/flows |
Controlled message/control flows |
GET |
/api/v1/flows/{scope} |
Flow detail for one topic/scope |
GET |
/api/v1/trust/summary |
Trust and evidence summary |
GET |
/api/v1/capabilities/summary |
Enabled capability/plugin state |
GET |
/api/v1/events/gmp |
Focused event view for review |
GET |
/api/v1/exports/trust?format=jsonl |
Export trust/evidence records |
GET |
/api/v1/exports/trust?format=csv |
Export trust/evidence records |
The /api/v1/events/gmp view is a focused event view for regulated evaluation
scenarios. It is not a compliance claim.
Examples:
curl -sS "http://localhost/api/v1/flows" \
-H "Authorization: Bearer ${TOKEN}" \
| jqcurl -sS "http://localhost/api/v1/trust/summary" \
-H "Authorization: Bearer ${TOKEN}" \
| jqcurl -sS "http://localhost/api/v1/exports/trust?format=jsonl" \
-H "Authorization: Bearer ${TOKEN}"Product meaning:
TrailMQ should not only move MQTT messages. It should help explain what happened around those messages.
A simple evaluation flow is:
# 1. Check runtime readiness
curl -sS http://localhost/ready | jq
# 2. Login
cd recipes/secure-mqtt-core
ADMIN_USER="testadmin"
ADMIN_PASS="$(cat secrets/testadmin.pwd)"
TOKEN="$(
curl -sS -X POST "http://localhost/api/v1/auth" \
-H "Content-Type: application/json" \
-d "{\"username\":\"${ADMIN_USER}\",\"password\":\"${ADMIN_PASS}\"}" \
| jq -r '.token // .accessToken // .data.token // .data.accessToken'
)"
# 3. Inspect controlled topics
curl -sS "http://localhost/api/v1/topics" \
-H "Authorization: Bearer ${TOKEN}" \
| jq
# 4. Resolve policy for a topic
curl -sS -X POST "http://localhost/api/v1/policies/resolve" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{"topic":"demo/line1/temperature"}' \
| jq
# 5. Check queue and DLQ behavior
curl -sS "http://localhost/api/v1/queue/status" \
-H "Authorization: Bearer ${TOKEN}" \
| jq
# 6. Validate audit chain
curl -sS "http://localhost/api/v1/audit/validatechain/details" \
-H "Authorization: Bearer ${TOKEN}" \
| jq
# 7. Read product-level trust state
curl -sS "http://localhost/api/v1/trust/summary" \
-H "Authorization: Bearer ${TOKEN}" \
| jqThis demonstrates the core evaluation path:
Configure MQTT behavior, inspect the effective state, send or simulate traffic, review what happened and check whether the recorded evidence is still intact.
TrailMQ is developed with an internal scenario-based verification approach.
The internal verification suite is not part of the public evaluation package. It is used during development to exercise TrailMQ through its real product surfaces:
- MQTT over TLS
- REST authentication
- topic control
- policy resolution
- queue and dead-letter behavior
- audit recording
- audit-chain validation
- health, readiness and metrics endpoints
- negative and security behavior
The goal is not only to check whether MQTT messages can be transported.
The goal is to check whether TrailMQ remains controllable, inspectable and reviewable under realistic conditions.
Internally verified product areas include:
| Area | What is checked internally | Product meaning |
|---|---|---|
| Authentication | Missing, invalid or wrong credentials are rejected | Access is explicit |
| Authorization | Restricted actions are blocked for insufficient roles | Runtime control is role-aware |
| Topic control | Topic creation, hierarchy and effective settings are exercised | MQTT namespaces are manageable |
| Queue behavior | Queue pressure, ACK/NACK behavior and DLQ paths are exercised | Delivery issues remain visible |
| Audit trail | Audit entries and chain validation are exercised | Runtime behavior can be reviewed |
| Integrity | Audit-chain behavior and tamper-oriented checks are exercised | Evidence changes should become visible |
| Durability | Topics, queues, policies and audit state are exercised across runtime changes | State should survive more than one process moment |
| Concurrency | MQTT traffic and REST changes are exercised in parallel | Runtime behavior is tested under realistic interaction |
| API contracts | Common response shapes and error behavior are exercised | Automation can depend on stable API behavior |
| Negative behavior | Bad input and forbidden actions are exercised | Failures should be explicit and diagnosable |
This principle is central to TrailMQ:
TrailMQ should not only transport MQTT messages. It should make MQTT behavior easier to control, inspect and review.
The public REST API is therefore the main integration surface for local evaluation, automation and external tooling.
TrailMQ is built for environments where traceability, reviewability and controlled messaging matter.
It can support regulated engineering practices by exposing audit records, policy decisions, queue state and evidence-oriented exports.
However, TrailMQ itself does not certify a system as GMP, GxP, CSV, Annex 11 or 21 CFR Part 11 compliant. Compliance depends on the full validated system, configuration, operating procedures, user management, infrastructure and organizational controls.
cd recipes/secure-mqtt-core
docker compose up -dYou usually don't need to — use ./trailmq up from the repo root.