Files
dbledeez 295eb22826 panel public release
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 01:26:41 -07:00
..
2026-07-15 01:26:41 -07:00
2026-07-15 01:26:41 -07:00
2026-07-15 01:26:41 -07:00

panel-empyrion-bridge

A small .NET 8 ASP.NET Core sidecar that proxies Empyrion's native Mod API (TCP/protobuf-net) into a JSON REST surface for the panel dashboard.

Why this exists

Empyrion's only first-class admin tool is a Windows-only desktop app (Empyrion Admin Helper / EAH) speaking protobuf-net to an in-process mod called EPM. Re-implementing protobuf-net contract decoding in Go is a losing battle — every Empyrion update could shift [ProtoMember] tags.

Instead, this bridge:

  • References Eleon's Mif.dll directly (the official mod-API SDK).
  • Re-uses EPMConnector (TCP framing + protobuf-net dispatch) by compiling its source files in.
  • Exposes a thin REST API the panel calls.

Build

docker build -t panel-empyrion-bridge:latest modules/empyrion-bridge

Run

The bridge needs network reach to wherever EPM is listening on each Empyrion instance. On a single-host setup with network_mode: host empyrion containers, hostname 127.0.0.1 works:

docker run -d --name panel-bridge --network host \
  -e BRIDGE_URLS=http://127.0.0.1:8090 \
  panel-empyrion-bridge:latest

For multi-host setups, pass each Empyrion's LAN IP + EAH_API_PORT in the query string per request.

API (Phase 2 — read-only)

All endpoints take ?host=…&port=… query parameters identifying the Empyrion EPM instance.

Method Path Returns
GET /healthz bridge health + version
GET /api/v1/factions [{ origin, factionId, name, abbrev }]
GET /api/v1/players { playerIds: [int], count }
GET /api/v1/players/{id} full PlayerInfo (raw protobuf-net contract)
GET /api/v1/playfields { playfields: [name] }
GET /api/v1/playfields/{name}/entities { entities: [...] }
POST /api/v1/console fire-and-forget; body is the console line

POST /api/v1/console is currently EXPERIMENTAL — see "Known issues" below.

Known issues / Phase 3+ work

  • Console command cmd-byte truncation suspected. Empyrion's CmdId enum has grown past 255 entries; the legacy EPMConnector.ModProtocol writes cmd as a single byte, which means commands with id > 255 alias to other commands. Symptom seen: sending Request_ConsoleCommand shows up on the EPM side as Event_ChatMessageEx. Fix: extend the protocol encoding to use a UInt16 cmd OR build a tested mapping table from Mif.dll's CmdId enum values. Tracked for Phase 3.
  • Authentication. EPM has none. The bridge currently has none either. Before exposing any port to the WAN, add a shared-secret bearer token matching the panel's existing service-API pattern.
  • Streaming events. Right now unsolicited Event_* packages (chat, player join, etc) are dropped. A GET /api/v1/stream SSE endpoint will fan these out to subscribers in Phase 4.
  • Connection persistence. The pool reaps idle connections after 5 minutes. If Empyrion restarts, we don't yet auto-reconnect; the next request opens a fresh connection but in-flight requests fail. Phase 5.

Source layout

empyrion-bridge/
  bridge/                  ASP.NET Core minimal API project
    Bridge.csproj
    Program.cs             endpoint definitions
    BridgePool.cs          per-(host,port) connection cache
    BridgeConnection.cs    request/response correlation by (seq, expectedCmd)
    EPMConnector/          source files compiled in (from MichaelGoulding/sample-empyrion-mod, MIT)
  lib/
    Mif.dll                Eleon Modding SDK reference
  Dockerfile               multi-stage build, alpine runtime

Verified end-to-end on 2026-04-30

curl 'http://<agent-host>:8090/api/v1/factions?host=127.0.0.1&port=7028'
→ {"factions":[{"origin":1,"factionId":1,"name":"Human","abbrev":"Hum"}]}