Services

Operator guides for the 17 production daemons — ports, config, APIs, deployment.

mako implements the BDEW Marktkommunikation between the four German market roles — LF (Lieferant), NB (Netzbetreiber), MSB (Messstellenbetreiber) and BKV (Bilanzkreisverantwortlicher); see Party Roles.

mako consists of 17 independently deployable services, each a self-contained Docker image built on the mako-service SDK, with TOML configuration (env: and _FILE secrets), OIDC/JWT and API-key authentication, Cedar ABAC, OpenTelemetry, /health/live · /health/ready · /metrics, and an MCP server at /mcp (Streamable HTTP) on 15 of the 17 — all except outputd and agentd (the MCP host). just dev <service> runs one against dev/config/<service>.toml.


Service Map

graph TB
    ext["BDEW Counterparty<br/>(NB · LF · MSB · BKV)"]

    subgraph protocol ["Protocol & Market Data"]
        makod[":8080 makod<br/>EDIFACT runtime · 71 workflows<br/>AS4 · SlateDB · MCP"]
        marktd[":8180 marktd<br/>MaLo/MeLo/NeLo · contracts<br/>VersorgungsStatus · fan-out"]
        processd[":8580 processd<br/>Anmeldung STP ≥95%<br/>LF answers · §14a"]
    end

    subgraph nb_billing ["Invoice & Grid Billing (NB)"]
        invoicd[":8280 invoicd<br/>INVOIC 8-stage plausibility<br/>auto-settle/dispute"]
        netzbilanzd[":8680 netzbilanzd<br/>NNE/KA/MMM/MSB billing<br/>SettlementResult · CalculationTrace"]
        sperrd[":8780 sperrd<br/>Sperr-/Entsperrauftrag queue<br/>ORDERS 17115/17117 · IFTSTA 21039"]
    end

    subgraph data ["Energy Data & Observability"]
        edmd[":8380 edmd<br/>MSCONS · iMSys direct push<br/>validation · Ersatzwerte · virtual"]
        obsd[":8480 obsd<br/>process projections · KPI<br/>§7a Abs. 5 EnWG parity report"]
        mabis[":8880 mabis-syncd<br/>MaBiS Summenzeitreihe<br/>MSCONS 13003 · 10. Werktag · MCP"]
        einsd[":9180 einsd<br/>EEG/KWKG settlement<br/>10 schemes · §14 UStG Gutschrift"]
    end

    subgraph lf_billing ["Retail Billing (LF)"]
        productd[":9080 productd<br/>14 categories · §42 Stromkennzeichnung<br/>EPEX §41a · B2B Angebote"]
        billingd[":9280 billingd<br/>13 categories · XRechnung 3.0<br/>RLM demand · §54 exemption"]
        outputd[":9880 outputd<br/>Typst templates · ZUGFeRD carrier<br/>issued documents · delivery evidence"]
        accountingd[":9380 accountingd<br/>Massenkontokorrent<br/>SEPA FRST/RCUR · GLN ID · Aging · §288 BGB"]
    end

    subgraph b2c ["Contract & Customer (LF)"]
        vertragd[":9780 vertragd<br/>Kunden B2C+B2B · Rahmenverträge<br/>OIDC→MaLo · 17 MCP tools"]
        portald[":9480 portald<br/>customer portal read-model<br/>§41 self-service"]
    end

    agentd[":9580 agentd<br/>28 declarative manifests on agentplane<br/>journaled effects · strict replay<br/>human triage worklist · A2A cards<br/>OIDC · HMAC · 5 model backends"]

    ext -->|AS4 / REST| makod
    makod <-->|CloudEvents| marktd
    marktd -->|webhook fan-out| processd & invoicd & edmd & obsd & agentd
    makod -->|commands| netzbilanzd & invoicd
    mabis -->|MSCONS 13003 cmd| makod
    billingd -->|de.billing.rechnung.erstellt| accountingd
    billingd -->|render · pin hash| outputd
    vertragd -->|start-supply| processd
    portald -->|aggregates| billingd & accountingd & edmd & einsd & marktd

Protocol & Market Data

ServicePortRolePurpose
makod:8080 · :4080 · :8090AllProtocol daemon — 71 workflows over 469 Prüfidentifikatoren, AS4/REST/iMS
marktd:8180AllMarket Data Hub — MaLo/MeLo/contracts, VersorgungsStatus, typed BO4E API, durable fan-out, MMMA monthly import worker
processd:8580NB + LF + MSBProcess Decision Engine — Anmeldung STP ≥95%, LF answers to the NB-initiated GPKE processes, MSB REQOTE auto-response, §14a Steuerungsauftrag produktcode check; role-gated binaries (§ 6a EnWG)

Invoice & Grid Billing

ServicePortRolePurpose
invoicd:8280LFINVOIC plausibility-check — eight stages plus the WiM 31003 send window (incl. ToU band routing via zaehlzeitregister), auto-settle/dispute, § 147 AO / GoBD receipts
netzbilanzd:8680NBNNE/KA/MMM/MSB/AWH billing — generates INVOIC 31001/31002/31005/31009/31011, full REMADV lifecycle, §14a Modul 3 ToU, §42b EnWG GGV, Redispatch 2.0 Kostenblatt, 8-tool MCP server
sperrd:8780NBSperrung execution tracking — IFTSTA 21039 auto-dispatch on field confirmation; GET /api/v1/sperr-orders/stats compliance snapshot; tenant isolation; 4-tool MCP server

Energy Data & Observability

ServicePortRolePurpose
edmd:8380AllEnergy Data Management — MSCONS, iMSys direct push, Kafka batch ingest, quality scoring and validation, virtual meters, tiered storage; 15-tool MCP server
mabis-syncd:8880ÜNB/NBMaBiS synchronisation — aggregates quarter-hourly Lastgang per Bilanzierungsgebiet via SummenzeitreiheBuilder, files with the BIKO as MSCONS 13003 on the 10. Werktag; records the BIKO-assigned Datenstatus and open Korrekturbedarf; emits de.mabis.* failure events; 4-tool read-only MCP server
einsd:9180NB/LFEinspeiser Registry + EEG/KWKG settlement — 10 settlement schemes; issues the §14 UStG Gutschrift (Gutschriftverfahren) per billable settlement as a BO4E Rechnung with per-rate USt breakdown; 19-tool MCP server
obsd:8480AllBusiness-process observability — per-PID KPIs with the APERAK and Antwortfrist clocks reported separately, deadlines read from mako-fristen (never computed here), completed_at cycle-time tracking, GET /api/v1/audit/gleichbehandlung for the § 7a Abs. 5 EnWG filing, 6-tool MCP server

edmd's quality layer is metering's validation rules, run per register with an A/B/C/F grade, and its Ersatzwertbildung; its derived products are virtual meters (UTILTS Berechnungsformel, § 42b EnWG GGV), the § 40a Abs. 2 EnWG Verbrauchsschätzung, Resampling and Ablesesteuerung (INSRPT auto-order). meterstore tiers hot PostgreSQL against cold Apache Iceberg with cross-tier OLAP and a read-only Iceberg REST catalog, and every Cedar write action is role-gated to MSB/NB/admin.

Retail Billing (LF)

ServicePortRolePurpose
productd:9080LFProduct & Tariff Catalog — user-defined energy products, EPEX Spot for §41a, B2B Angebote/quotations
billingd:9280LFEnergy Billing Engine — 13 categories, §41a dynamic, §42b EnWG GGV community solar, EN 16931 e-invoicing (XRechnung 3.0 CII / PEPPOL UBL); the ZUGFeRD PDF renders via outputd
outputd:9880—Customer Communications — operator Typst templates, the ZUGFeRD PDF/A-3 carrier, the append-only store of issued documents and their delivery
accountingd:9380LFCustomer Account Ledger — tamper-evident double-entry ledger (doubleentry: Merkle proofs, period seals for GoBD/§146 AO); per-MaLo Kontokorrent, FIFO open-item clearing, Summen- und Saldenliste §238 HGB, Verzugszinsen §288 BGB, Zahlungsvereinbarung; SEPA pain.008 and CAMT.054; dunning delivered through outputd; §40b Abs. 1 Jahresabschluss; GDPR Art. 17

outputd's templates are content-addressed and append-only, and a publish is gated by proof; besides the invoice it renders the Textform kinds — Mahnung (§ 126b BGB) and Preisanpassung (§ 41 Abs. 5 EnWG) — and delivers over portal, e-mail, print spool and ERP with per-channel evidence.

B2C & AI

ServicePortRolePurpose
vertragd:9780LF + MSBContract & Customer Management — Kunden (B2C+B2B), Rahmenverträge, Versorgungsverträge, kunden_identitaeten (N portal users per company), Tarifwechsel with its § 41 Abs. 5 EnWG Preisänderungsanzeige (rendered and delivered through outputd), Kündigung, OIDC→MaLo auth gateway for portald
portald:9480LFCustomer Portal gateway — stateless aggregation over all LF services plus the §41 EnWG self-service writes (Tarifwechsel, Kündigung, SEPA, GDPR Art. 16) and the document inbox served out of outputd; every route resolves customer ownership through vertragd; 8-tool operator MCP server
agentd:9580AllMulti-agent LLM orchestration — 28 declarative manifests on the agentplane durable runtime, every effect journaled; read-only by construction, so oversight is a triage worklist

agentd's manifests are activated through [bundled_agents]; one event yields one journaled run per subscribing specialist, and since no manifest grants a mutating tool, a finding opens a triage row rather than suspending the run.


Shared foundation — the mako-service SDK

Every daemon is built on the mako-service crate. A service's main is a single line:

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    mako_service::run::<Billingd>().await   // Billingd: Daemon impl
}

run::<D>() owns the whole lifecycle. A Daemon implementation supplies only what is service-specific — the config type, the migrations, and a build() that assembles the domain router and spawns background workers:

flowchart TD
    start(["main → run::&lt;D&gt;"]) --> check{"--check?"}
    check -->|yes| probe["GET /health/ready<br/>exit 0/1"] --> done([exit])
    check -->|no| trace["init tracing"]
    trace --> cfg["load config<br/>TOML + env + _FILE"]
    cfg --> pool["connect tuned pool<br/>DatabaseConfig::connect(url, NAME)<br/>sets application_name"]
    pool --> migrate["D::migrate<br/>sqlx::migrate! + outbox::ensure_schema"]
    migrate --> build["D::build(cfg, ctx)<br/>domain Router + spawn workers on ctx.shutdown"]
    build --> infra["mount infra routes<br/>/health · /health/live · /health/ready · /metrics"]
    infra --> serve["serve with graceful drain<br/>SIGINT / SIGTERM"]
    serve -.->|readiness| ready["/health/ready = bounded SELECT 1 + D::ready"]

What every service gets for free from the runner:

ConcernProvided by run::<D>()
TracingStructured logs + optional OTLP export (RUST_LOG, OTEL_EXPORTER_OTLP_ENDPOINT)
Config[database] + service blocks, env:/_FILE substitution, <SVC>_CONFIG path
PoolTuned sizing with a per-service application_name for pg_stat_activity
MigrationsApplied at startup before the first request
ReadinessReal /health/ready — a bounded SELECT 1 DB ping, not a static true
ShutdownSIGINT/SIGTERM graceful drain; workers observe ctx.shutdown
Health probe--check in-container HEALTHCHECK (no shell, no curl)

Event-emitting services — accountingd, billingd, edmd, einsd, mabis-syncd, netzbilanzd, sperrd and vertragd — use the SDK's transactional outbox: each CloudEvent is written to event_outbox in the same transaction as the business change and drained by an OutboxWorker with retry and a status-column dead-letter queue. marktd keeps its own event_log outbox ahead of the durable fan-out, and invoicd retries ERP notifications from columns on invoic_receipts. Emission always goes through CloudEvent::new + post_ce_with_retry, signed as Standard Webhooks (webhook-signature).

Both outboxes wake their worker from an AFTER INSERT trigger's NOTIFY, which Postgres holds until commit; the 30 s poll is the fallback. cargo xtask check-outbox-notify holds trigger, channel and listener together.

makod alone keeps a bespoke main for its SlateDB event store and AS4 runtime; every other service, the stateless portald and agentd included, runs on mako_service::run.


Deployment

All services are available as multi-stage Docker images built with cargo-chef:

# Single all-in-one daemon (makod only)
docker pull ghcr.io/hupe1980/mako-makod:latest

# NB STP demo — UTILMD 55001 Lieferbeginn end-to-end
git clone https://github.com/hupe1980/mako
cd mako/demos/nb-stp
docker compose up

# EEG billing demo — solar plant registration + §21 EEG 2023 settlement
cd mako/demos/eeg-billing
docker compose up

# Order-to-cash demo — Tarif → Vertrag → Rechnung → Dokument → Offener Posten
cd mako/demos/o2c
docker compose up

See the Getting Started guide for the full deployment walkthrough.

makod Operator Guide

makod operator guide: ports, CLI flags, the config file, persistent and volatile storage, AS4 inbound, the REST API and Kubernetes deploymen…

marktd Operator Guide

Operator guide for marktd, the market data hub: Marktpartner, Marktlokationen, price sheets and the durable CloudEvents fan-out every other …

processd Operator Guide

Operator guide for processd, the process decision engine: NB Anmeldung straight-through processing, LF and MSB answers, role-gated binaries …

invoicd Operator Guide

invoicd operator guide: the LF-side INVOIC plausibility check over ten inbound billing PIDs, with § 147 AO receipts and the answer back thro…

netzbilanzd Operator Guide

Operator guide for netzbilanzd, the NB billing daemon: Netznutzungsentgelt, Konzessionsabgabe and Mehr-/Mindermengen settlement into INVOIC …

sperrd Operator Guide

Operator guide for sperrd, the Netzbetreiber's Sperr-/Entsperrauftrag queue: ORDERS 17115/17117 in, field dispatch, IFTSTA 21039 out.

edmd Operator Guide

Operator guide for edmd, the energy-data daemon: MSCONS and iMSys readings, quality scoring, validation, virtual meters and § 60 MsbG Ersatz…

einsd Operator Guide

Operator guide for einsd, the EEG/KWKG feed-in settlement daemon: plant registry, §51 Negativpreisregel, §52 Pflichtzahlungen, 19 MCP tools,…

obsd Operator Guide

obsd operator guide: process projections, the two deadline clocks, per-PID KPIs and the § 7a Abs. 5 EnWG Gleichbehandlung parity evidence.

productd Operator Guide

Operator guide for productd, the LF product and tariff catalogue: Tarifpreisblätter, §41a EPEX dynamic pricing, the §41c comparison feed and…

billingd Operator Guide

Operator guide for billingd, the LF multi-product billing engine: 13 product categories, §41a dynamic tariffs, §14a modules and EN 16931 e-i…

outputd Operator Guide

Operator guide for outputd, the customer-communications daemon: operator-owned Typst templates, the ZUGFeRD PDF/A-3 carrier and per-channel …

accountingd Operator Guide

Operator guide for accountingd, the LF Massenkontokorrent: a tamper-evident double-entry ledger, SEPA and camt payment flows, Mahnwesen and …

vertragd Operator Guide

Operator guide for vertragd, contract and customer management: B2C and B2B Verträge, Rahmenverträge, Kündigungsfristen and §41 EnWG price-ch…

portald Operator Guide

Operator guide for portald, the customer-portal read-model gateway: one REST API over Lastgang, invoices, ledger and supply status, plus §41…

mabis-syncd Operator Guide

Operator guide for mabis-syncd: aggregates MaBiS Summenzeitreihen per Bilanzierungsgebiet and submits them to the BIKO on the Erstaufschlag-…

agentd Operator Guide

agentd operator guide: 28 specialist manifests on agentplane — typed results, durable triage, semantic trust labels and journal-backed runs.