Experimental · pre-1.0 Rust · MIT or Apache-2.0 · BDEW EDI@Energy FV2025-04-01 → FV2026-10-01
German energy-market operations,
from raw EDIFACT to production — in Rust.
Every German electricity and gas market participant is required to exchange regulated messages with its counterparties — Marktkommunikation, MaKo. That is a few hundred prescribed processes carried over EDIFACT and AS4, each with a published decision tree and a deadline. mako implements them: the BDEW formats are read as data, every process runs as an event-sourced workflow, and settlement, billing and the ledger sit on the same events.
cargo add edi-energy
for the format layer, or
docker run ghcr.io/hupe1980/makod
for the daemon.
Three views of one catalogue. Every one of the 482
Prüfidentifikatoren the BDEW Anwendungsübersicht publishes is imported; 469 are
routed to a workflow — including the DVGW gas codes BDEW does not publish — and
434 additionally carry validated AHB segment rules. The 35 that do not
are exactly that DVGW block: ALOCAT, NOMINT/NOMRES and SSQNOT ride dvgw-edi,
for which no BDEW AHB exists to import rules from. A release that would shrink any of the
three fails the build.
Who runs this
German market law splits the industry into four roles, and every message has one on each end. mako implements all four from one engine — and a deployment scoped to a role contains no other role's code, which is what § 6a EnWG unbundling asks for.
NB Netzbetreiber
Distribution grid operator
Answers supplier registrations and terminations from the published Entscheidungsbäume, bills network use (NNE), settles Mehr-/Mindermengen, reports MaBiS Summenzeitreihen and executes Sperrung orders.
processd · netzbilanzd · mabis-syncd →LF Lieferant
Energy supplier
Wins and switches customers, prices contracts against the tariff catalogue, bills retail consumption, issues EN 16931 e-invoices (XRechnung, ZUGFeRD) and collects by SEPA.
billingd · vertragd · invoicd →MSB Messstellenbetreiber
Metering point operator
Runs the WiM device processes — installation, change of operator, fault handling — manages the smart-meter gateway lifecycle, and delivers metered values to everyone entitled to them.
edmd · makod →ESA Energieserviceanbieter
Energy service provider
Orders measured values under the § 34 MsbG Wertebestellung, quotes its own measurement products, and invoices the service against the quotation the counterparty accepted.
makod — ESA messages →What happens to a message
A market partner sends an interchange over AS4. Everything after that is the same path for every process — parse, validate against the AHB in force on that date, route to a workflow, and answer inside the Frist.
graph LR
MP["Marktpartner"]
AS4["AS4 / ebMS3
sign · encrypt · receipt"]
VAL["Parse + validate
MIG · AHB · semantic"]
WF["Workflow
event-sourced, replayable"]
FRIST["Frist
BDEW Werktag calendar"]
OUT["Outbox
persist before dispatch"]
ERP["ERP · CloudEvents"]
MP -->|"UTILMD · MSCONS · ORDERS · INVOIC"| AS4
AS4 --> VAL
VAL -->|"Prüfidentifikator"| WF
WF --> FRIST
WF --> OUT
OUT -->|"answer interchange"| AS4
OUT --> ERP
AS4 -->|"CONTRL / APERAK"| MP
The format layer reads the BDEW profiles as data, so an annual release is an import run rather than a rewrite. The domain layer never sees EDIFACT: it receives typed commands and emits typed events.
What it looks like
The format layer stands alone. Add one crate and an interchange becomes a typed message, validated against the AHB profile in force on its date.
use edi_energy::{EdiEnergyMessage, Platform};
let msg = Platform::with_all_profiles().parse(wire)?;
msg.try_message_type(); // Some(Utilmd)
msg.detect_release()?.as_str(); // "S2.1"
msg.detect_pruefidentifikator()?; // 55001 — Anmeldung Lieferbeginn
let report = msg.validate()?; // MIG + AHB + semantic
report.is_valid(); // true
An unregistered Prüfidentifikator is reported, never silently accepted — and the profile is chosen by the message's own date, because two Formatversionen can share one wire release code and only the date tells them apart.
The shape of the platform
Seventeen services along one path: transport and protocol at the edge, master data in the middle, settlement, billing and the customer's documents at the end — each owning its own data, talking over typed REST and CloudEvents.
graph TB
subgraph edge["Edge — protocol"]
MAKOD["makod
AS4 · EDIFACT · workflows"]
PROCESSD["processd
STP decisions"]
SPERRD["sperrd
Sperr-/Entsperraufträge"]
end
subgraph core["Core — market data"]
MARKTD["marktd
BO4E hub · fan-out"]
VERTRAGD["vertragd · productd
contracts · products"]
EDMD["edmd
metering · SMGW"]
end
subgraph money["Settlement and billing"]
NETZ["netzbilanzd
NNE · MMM"]
EINSD["einsd
EEG · KWKG"]
MABIS["mabis-syncd
MaBiS Summenzeitreihen"]
BILLINGD["billingd · invoicd
retail · EN 16931"]
ACCT["accountingd
FI-CA ledger"]
OUTPUTD["outputd
documents · delivery"]
end
subgraph ops["Operations"]
OBSD["obsd
BNetzA KPIs · Fristen"]
AGENTD["agentd
AI specialists"]
PORTALD["portald
customer portal"]
end
PROCESSD --> MAKOD
MAKOD --> SPERRD
MAKOD --> MARKTD
MARKTD --> VERTRAGD
MARKTD --> EDMD
EDMD --> NETZ
EDMD --> EINSD
EDMD --> BILLINGD
EDMD --> MABIS
VERTRAGD --> BILLINGD
NETZ --> ACCT
BILLINGD --> ACCT
BILLINGD --> OUTPUTD
ACCT --> OUTPUTD
EINSD --> ACCT
PORTALD --> BILLINGD
OBSD -.->|"watches"| MAKOD
AGENTD -.->|"MCP reads"| MARKTD
AGENTD -.->|"MCP reads"| OBSD
What you get
Protocol & process
Parse, validate and build all 17 EDI@Energy EDIFACT types plus the four DVGW gas formats, and run every MaKo process as an event-sourced, replayable workflow over AS4/ebMS3 — BDEW AS4-Profil, sign and encrypt, signed receipts, retry with duplicate elimination.
makod · processd →Market data
A typed BO4E market-data hub — MaLo/MeLo, contracts, supply status, device configuration, grid-operator registry — with durable CloudEvents fan-out and OIDC/JWT on every route.
marktd · vertragd · productd →Metering & settlement
Quarter-hourly metering with § 60 MsbG substitute values and SMGW lifecycle; role-neutral grid settlement (NNE/MMM/MSB), EEG/KWKG feed-in and MaBiS Summenzeitreihen — each position carrying a legal-reference calculation trace.
edmd · netzbilanzd · einsd →Billing & ledger
Retail billing with EN 16931 e-invoicing (XRechnung, ZUGFeRD), SEPA collection and returns, a hash-chained FI-CA ledger with § 147 AO period seals and Merkle inclusion proofs, and the documents themselves — rendered from operator templates and delivered with per-channel evidence.
billingd · invoicd · accountingd · outputd →Operations & oversight
BNetzA KPI observability with Fristen tracking, a customer portal, and an agent plane over the platform's 163 MCP tools. Its 28 declarative specialists are advisory by construction: no active manifest grants a mutating tool, every model and tool call is a journaled effect, and a triage worklist carries what a person has to see.
obsd · portald · agentd →Testable from Python
makotest binds the same Rust core the platform runs — BDEW check digits, the Werktag calendar, the published answer Fristen, EDIFACT build and AHB validation — so a test and production can never disagree about what “valid” means, nor about when a Frist expires.
makotest →Built for the regulatory pace
German market communication moves fast — LFW24, § 14a, § 41a dynamic tariffs, § 42b/c energy sharing, the EDIFACT→API transition, the MaBiS-Hub cutover. mako's domain layer is deliberately split from transport and format, so when the market moves, the platform moves with an import run — not a rewrite.
- Auditable by construction — event-sourced processes, replayable state, hash-chained ledger with § 147 AO period seals.
- Four market roles, one engine — NB, LF, MSB and ESA answer from the published Entscheidungsbäume, and a role-scoped build contains only its own arm's code.
- Validation that fails loudly — an unregistered Prüfidentifikator is reported, never quietly accepted.
- Fristen that agree with each other — one BDEW Werktag calendar, from the APERAK clock to an agent's approval window.
- Readable, verifiable, extensible — source you can audit and run.
Start in minutes
# Parse & validate an EDIFACT interchange
cargo add edi-energy
# Run the protocol daemon
docker run --rm -p 8080:8080 \
ghcr.io/hupe1980/makod:latest
# Drive it from Python — generators, simulators, pytest plugin
pip install makotest
Experimental · pre-1.0: interfaces may change between releases, and a production deployment needs your own testing against your own counterparties. Dual-licensed MIT or Apache 2.0.