sperrd Operator Guide

Operator guide for sperrd — the Sperrung execution tracking daemon that auto-dispatches IFTSTA 21039 (field confirmation) when the field-service team reports execution, preventing permanent GPKE protocol violations.

sperrd Operator Guide

sperrd bridges the gap between the BDEW ORDERS Sperrung process and the physical field-service execution. Without sperrd, a missed IFTSTA 21039 leaves the Sperrung permanently unresolved in the LF system — a GPKE protocol violation under BK6-22-024.

Port: :8780
Storage: PostgreSQL (sperr_orders table)
Role: NB (Netzbetreiber) role only

  1. TOC {:toc}

Why sperrd exists

sequenceDiagram
    participant LF as LF (Lieferant)
    participant makod as makod :8080
    participant sperrd as sperrd :8780
    participant Field as Field service team

    LF->>makod: ORDERS 17115 (Sperrung request)
    makod->>sperrd: POST /api/v1/sperr-orders<br/>{malo_id, lf_mp_id, "sperrung"}
    makod-->>LF: ORDRSP (confirmation)

    Note over Field: Field team executes disconnection

    Field->>sperrd: PUT /api/v1/sperr-orders/{id}/execute<br/>{note, executed_at}
    sperrd->>makod: dispatch IFTSTA 21039<br/>(field confirmation)
    makod-->>LF: IFTSTA 21039 (Ausführungsbestätigung)

Without sperrd, the field team would need to manually trigger IFTSTA 21039 through another system, creating a compliance risk if that step is missed. sperrd captures the execution confirmation and automatically ensures the IFTSTA reaches makod for outbound delivery.


HTTP API

POST /api/v1/sperr-orders

Register a new Sperrung or Entsperrung order.

{
  "malo_id":      "51238696780",
  "lf_mp_id":     "9900012345678",
  "order_type":   "sperrung",
  "process_id":   "550e8400-e29b-41d4-a716-446655440000",
  "planned_date": "2025-02-20"
}

order_type: "sperrung" (disconnect) or "entsperrung" (reconnect).

Response 201 Created: { "id": "<uuid>" }

GET /api/v1/sperr-orders

Query parameters:

ParameterDescription
statusFilter by status: pending, executed, failed, cancelled
malo_idFilter by Marktlokations-ID
older_than_hoursReturn only orders created more than N hours ago — use 48 in the daily BK6-22-024 compliance sweep to detect stuck orders past the 2-Werktage window
limitMaximum results (default 100, max 1000)

GET /api/v1/sperr-orders/{id}

Returns full order including status, executed_at, iftsta_ref, and fail_reason.

PUT /api/v1/sperr-orders/{id}/execute

Reports successful field execution. Triggers IFTSTA 21039 dispatch to makod.

{
  "note":        "Disconnected at main fuse panel, ref: TW-2025-0220-001",
  "executed_at": "2025-02-20T09:47:00+01:00"
}

Response 204 No Content.

PUT /api/v1/sperr-orders/{id}/fail

Reports a field failure (status → failed) and auto-dispatches IFTSTA 21039 reporting non-execution (gpke.sperrung.fehlgeschlagenmakod), so the LF learns why the Sperrung did not happen instead of waiting out its deadline.

{
  "reason": "Meter not accessible — locked gate. Rescheduled for next week."
}

Response 204 No Content.


PUT /api/v1/sperr-orders/{id}/cancel

Operator-initiated cancellation of a pending order. Only pending orders can be cancelled — executed and failed orders are terminal.

No IFTSTA 21039 is dispatched for cancelled orders (the Sperrung was never physically executed). Inform the LF if the order had already been communicated to their system.

Response 204 No Content.


GET /api/v1/sperr-orders/stats

Aggregate statistics for the BK6-22-024 compliance sweep.

{
  "total": 42,
  "pending": 3,
  "executed": 36,
  "failed": 2,
  "cancelled": 1,
  "overdue_pending": 1,
  "executed_missing_iftsta": 0
}
FieldRegulatory meaning
overdue_pendingPending orders whose planned_date is in the past — BK6-22-024 violation risk
executed_missing_iftstaExecuted orders where IFTSTA 21039 was NOT dispatched — GPKE protocol violation

Order lifecycle

stateDiagram-v2
    [*] --> pending : POST /sperr-orders
    pending --> executed : PUT /execute<br/>→ IFTSTA 21039 dispatched
    pending --> failed : PUT /fail<br/>→ IFTSTA 21039 (non-execution)
    pending --> cancelled : operator action
    executed --> [*]
    failed --> [*]
    cancelled --> [*]

Status meanings

StatusDescription
pendingOrder registered, awaiting field execution
executedField team confirmed execution; IFTSTA 21039 dispatched to makod
failedField team reported failure; IFTSTA 21039 (non-execution) dispatched to the LF; operator must reschedule
cancelledOrder cancelled before field execution (e.g. customer paid)

Configuration

# sperrd.toml
port           = 8780
tenant         = "9900357000004"   # data-isolation key (BDEW-/DVGW-Codenummer)

[database]
url       = "env:DATABASE_URL"
pool_size = 10                     # optional — the tuned pool is applied by mako-service

makod_url      = "http://makod:8080"
makod_api_key  = "env:MAKOD_API_KEY"

PostgreSQL schema

-- sperr_orders: tracks Sperrung/Entsperrung execution.
-- status: pending → executed | failed | cancelled
CREATE TABLE sperr_orders (
    id                   UUID        PRIMARY KEY DEFAULT gen_random_uuid(),
    malo_id              TEXT        NOT NULL,
    lf_mp_id             TEXT        NOT NULL,
    order_type           TEXT        NOT NULL CHECK (order_type IN ('sperrung', 'entsperrung')),
    process_id           TEXT,                    -- makod ORDERS process UUID
    planned_date         DATE,
    status               TEXT        NOT NULL DEFAULT 'pending',
    executed_at          TIMESTAMPTZ,
    execution_note       TEXT,
    fail_reason          TEXT,
    iftsta_ref           TEXT,                    -- dispatched IFTSTA 21039 command ID
    iftsta_dispatched_at TIMESTAMPTZ,             -- when IFTSTA 21039 was sent (SLA tracking)
    tenant               TEXT        NOT NULL DEFAULT '', -- data-isolation key
    created_at           TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at           TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX ON sperr_orders (malo_id, status);
CREATE INDEX ON sperr_orders (tenant, status);
CREATE INDEX ON sperr_orders (planned_date) WHERE status = 'pending';
CREATE INDEX ON sperr_orders (id) WHERE status = 'executed' AND iftsta_dispatched_at IS NULL;

iftsta_dispatched_at tracks the exact moment IFTSTA 21039 was dispatched. Any row with status = 'executed' and iftsta_dispatched_at IS NULL represents a GPKE protocol violation — the LF has not yet received execution confirmation.


Overdue pending orders

Orders that remain pending past their planned_date are a compliance risk. Query them via the REST API:

# Overdue orders past planned_date (BK6-22-024 violation risk)
curl http://sperrd:8780/api/v1/sperr-orders/stats

# All stuck pending orders older than 48 hours
curl "http://sperrd:8780/api/v1/sperr-orders?status=pending&older_than_hours=48"

Or the MCP tool list_overdue_orders surfaces all orders with planned_date < CURRENT_DATE including days_overdue — the sperrd-agent calls this in its daily compliance sweep.


Regulatory basis

RegulationRequirement
GPKE BK6-22-024IFTSTA 21039 (Ausführungsbestätigung) must be sent after physical Sperrung/Entsperrung execution
BK6-22-024 §6.2Failure to send IFTSTA 21039 leaves the process permanently unresolved in the LF system
BK6-22-024 §9Failed Sperrung must be reported to the LF within 3 Werktage
BDEW ORDERS AHB PIDs 17115–17117Sperrung/Entsperrung order + confirmation message flow
BDEW GeLi Gas 3.0 (BK7-24-01-009)Gas Sperrprozesse — ORDERS 17115/17117 + INVOIC 31011 (AWH)

Integration note: Inbound ORDERS 17115 from the LF triggers the order creation in sperrd. Outbound IFTSTA 21039 is dispatched to makod, which serializes it as EDIFACT and delivers it to the LF via AS4.


MCP server

sperrd exposes an MCP server at /mcp for LLM-based compliance automation.

Tools (5)

ToolDescription
list_sperr_orders(status, older_than_hours, limit)List orders filtered by status and/or age
get_sperr_order(id)Full order with timestamps, IFTSTA dispatch status, and process reference
get_sperr_statsAggregate compliance snapshot: pending, executed, overdue, missing IFTSTA
list_overdue_ordersAll pending orders past planned_date, with days_overdue computed
cancel_sperr_order(id)Cancel a pending order (operator-only; destructive_hint = true)

Prompts (2)

PromptDescription
execute-sperrungConfirm field execution → IFTSTA 21039 dispatch workflow
compliance-sweepDaily BK6-22-024 sweep: overdue + missing IFTSTA + AWH billing gaps

Daily compliance sweep with sperrd-agent

The sperrd-agent in agentd runs the compliance check automatically. The pattern:

1. get_sperr_stats → check overdue_pending + executed_missing_iftsta
2. If overdue_pending > 0 → list_overdue_orders → escalate field team + notify LF
3. If executed_missing_iftsta > 0 → list_sperr_orders(status=executed) → re-trigger dispatch
4. Cross-reference executed Sperrungen with netzbilanzd INVOIC 31011 drafts (AWH billing)

Edit this page ↗