Getting Started

Run the full mako NB STP demo stack — makod, marktd, processd, and a webhook receiver — in under 5 minutes. Submit a UTILMD 55001, watch processd auto-accept via netz-checker, and receive the UTILMD 55002 confirmation. Version 0.14.0, BDEW FV2026-10-01 compliant.

Getting Started

This guide runs the full NB STP demo stack locally and walks through the complete end-to-end flow: UTILMD 55001 → automatic NB decision → UTILMD 55002.

What you're running

ServicePortRole
postgres5432PostgreSQL — one database per service
webhook8000Demo ERP event receiver (Python, in-memory)
marktd8180Market Data Hub — MaLo/MeLo/NeLo/TR, VersorgungsStatus, EventBus fan-out, event_log replay
processd8580NB STP auto-responder — netz-checker (6 checks), LF E_0624 (45 min)
makod8080EDIFACT process engine — GPKE/WiM/GeLi Gas, in-memory
sequenceDiagram
    participant LF as LF 4012345000023
    participant makod as makod :8080
    participant marktd as marktd :8180
    participant processd as processd :8580
    participant webhook as webhook :8000

    LF->>makod: POST /edifact<br/>UTILMD 55001
    makod-->>webhook: APERAK BGM+312<br/>(45-min deadline, auto)
    makod->>marktd: de.mako.process.initiated<br/>HMAC POST /api/v1/events (demo override)
    marktd->>processd: de.mako.process.initiated<br/>HMAC POST /webhook
    marktd-->>webhook: de.mako.process.initiated<br/>(ERP subscription)
    processd->>marktd: GET /api/v1/versorgung/{malo_id}
    processd->>marktd: GET /api/v1/malo/{malo_id}/grid
    processd->>marktd: GET /api/v1/partners/{lf_mp_id}
    Note over processd: netz-checker: 6 checks → Accept
    processd->>makod: gpke.lieferbeginn.bestaetigen
    makod-->>webhook: UTILMD 55002 Bestätigung

Total time: ~5 minutes.


Prerequisites

ToolVersionInstall
Docker24+ with Compose v2https://docs.docker.com/get-docker/
curlanyOS package manager
jqanyOS package manager

Step 1 — Clone and build

git clone https://github.com/hupe1980/mako.git
cd mako

# Build all demo images at once with docker buildx bake (recommended)
docker buildx bake makod marktd processd

# Or build individually:
docker build --target runtime          -t makod:dev     .
docker build --target marktd-runtime   -t marktd:dev    .
docker build --target processd-runtime -t processd:dev  .

The processd-runtime stage builds with --features integrated (includes both NB netz-checker and LF E_0624 auto-response modules).


Step 2 — Start the demo stack

cd demos/nb-stp
docker compose up -d
docker compose ps   # wait until all containers are running

Expected:

NAME               IMAGE              STATUS         PORTS
demo-postgres-1    postgres:17-alpine Up (healthy)   5432/tcp
demo-webhook-1     python:3.12-alpine Up             0.0.0.0:8000->8000/tcp
demo-marktd-1      marktd:dev         Up             0.0.0.0:8180->8180/tcp
demo-processd-1    processd:dev       Up             0.0.0.0:8580->8580/tcp
demo-makod-1       makod:dev          Up             0.0.0.0:8080->8080/tcp

What happens at startup:
processd self-registers its EventBus subscription with marktd on startup — no manual subscription curl required. Both marktd and processd run SQLx migrations automatically on first boot (databases are created by init-db.sh).


Step 3 — Verify health

curl -s http://localhost:8080/health | jq .
# → {"status":"ok","instance_id":"..."}

curl -s http://localhost:8180/health | jq .
# → {"status":"ok"}

curl -s http://localhost:8580/health/ready
# → 200 OK

Step 4 — Seed master data

processd's netz-checker needs three items in marktd to reach an Accept decision.

4a — Price sheet

curl -s -X PUT http://localhost:8180/api/v1/preisblaetter/9900357000004 \
  -H "Content-Type: application/json" \
  --data-binary @demos/nb-stp/fixtures/preisblatt-nb.json \
  -w "\nHTTP %{http_code}\n"
# → HTTP 204

4b — MaLo + NIS grid record

MALO_ID=17835382035

# MaLo (NB=9900357000004, no active LF)
curl -s -X PUT "http://localhost:8180/api/v1/malo/$MALO_ID" \
  -H "Content-Type: application/json" \
  --data-binary "$(jq --arg m "$MALO_ID" '.data.marktlokations_id=$m' demos/nb-stp/fixtures/malo-nb.json)" \
  -w "\nHTTP %{http_code}\n"
# → HTTP 201

# NIS grid record (netz-checker check 1)
curl -s -X PUT "http://localhost:8180/api/v1/malo/$MALO_ID/grid" \
  -H "Content-Type: application/json" \
  -d '{"nb_mp_id":"9900357000004","bilanzierungsgebiet":"11YN0------0STXC","netzgebiet":"DEMO-NZ-001","sparte":"STROM","source":"manual"}' \
  -w "\nHTTP %{http_code}\n"
# → HTTP 204

4c — LF trading partner (netz-checker check 5)

# Register in marktd partner directory
curl -s -X PUT http://localhost:8180/api/v1/partners/4012345000023 \
  -H "Content-Type: application/json" \
  -d '{"mp_id":"4012345000023","display_name":"Demo LF","marktrolle":"LF","sparte":"STROM","channels":{}}' \
  -w "\nHTTP %{http_code}\n"
# → HTTP 200

# Register in makod for EDIFACT routing
curl -s -X PUT http://localhost:8080/admin/partners/4012345000023 \
  -H "Authorization: Bearer demo-secret-change-me" \
  -H "Content-Type: application/json" \
  --data-binary @demos/nb-stp/fixtures/partner-lf.json | jq '.version'

Step 5 — Submit a UTILMD 55001

curl -s -X POST http://localhost:8080/edifact \
  -H "Authorization: Bearer demo-secret-change-me" \
  -H "Content-Type: text/plain; charset=utf-8" \
  --data-binary @demos/nb-stp/fixtures/utilmd-55001.edi | jq .

Expected response:

{
  "accepted": 1,
  "rejected": 0,
  "messages": [{
    "message_type": "UTILMD",
    "pid": 55001,
    "workflow": "gpke-supplier-change",
    "status": "routed",
    "process_id": "...",
    "malo_id": "17835382035"
  }]
}

Step 6 — Automatic NB decision

Within ~200 ms, processd receives the de.mako.process.initiated event from marktd's EventBus and runs all 6 netz-checker validation checks synchronously.

# Check the decision log
curl -s http://localhost:8580/api/v1/decisions | jq '.[] | {
  malo_id, decision, erc_code, decided_at
}'
# → {"malo_id":"17835382035","decision":"Accept","erc_code":null,"decided_at":"..."}

With NB_AUTO_ACCEPT=true (set in the demo compose file), Accept automatically dispatches gpke.lieferbeginn.bestaetigen to makod, which enqueues the outbound UTILMD 55002 (Bestätigung Anmeldung verb. MaLo):

curl -s http://localhost:8000/events | jq '[.[] |
  select(.body.makomessagetype=="UTILMD")
  | {edifact: .body.data.edifact}
]'

Step 7 — Run the automated smoke test

The demo ships smoke.sh which runs all of the above automatically and asserts every step passes, including the auto-accept timing:

cd demos/nb-stp
MARKTD_URL=http://localhost:8180 WEBHOOK_URL=http://localhost:8000 bash smoke.sh

Output ends with:

✓ processd NB auto-responder dispatched bestaetigen → UTILMD 55002 already arrived
✓ POST /api/v1/commands → HTTP 409 (duplicate bestaetigen correctly rejected — AntwortGesendet guard confirmed)
✓ UTILMD 55002 was already verified in step 6c (auto-responder path)
All smoke tests passed.
  Wechselprozess auto-responder: ENABLED
  Flow: UTILMD 55001 → makod → marktd ingest → validate → bestaetigen → UTILMD 55002

The HTTP 409 confirms the workflow state guardprocessd already advanced the workflow to AntwortGesendet, so the later manual command is correctly rejected with invalid_state: expected ValidationPassed, found AntwortGesendet. This is not idempotency: it is the state machine rejecting an out-of-order command.


Step 8 — Explore the APIs

InterfaceURL
makod Swagger UIhttp://localhost:8080/api/v1/docs/
makod MCP serverhttp://localhost:8080/mcp
marktd Swagger UIhttp://localhost:8180/api/v1/docs/
marktd DLQ adminhttp://localhost:8180/admin/fanout/dlq
marktd metricshttp://localhost:8180/metrics
processd decisionshttp://localhost:8580/api/v1/decisions
processd approval queuehttp://localhost:8580/api/v1/queue
ERP webhook event loghttp://localhost:8000/events

Stop and clean up

docker compose down      # keep database volumes
docker compose down -v   # destroy all volumes (full reset)

Next steps

TopicGuide
EDIFACT parsing and validationParsing guide
ERP integration — CloudEvents, HMACERP integration
makod operator referencemakod guide
marktd operator referencemarktd guide
processd — NB STP + LF E_0624processd guide
INVOIC plausibility, § 147 AO / GoBDinvoicd guide
Energy data, imbalance, billing periodsedmd guide
Process observability, §20 parityobsd guide
Full system architectureArchitecture
Process catalogue (GPKE, WiM, …)Processes
mako-service SDKmako-service README

Edit this page ↗