# 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://:8090/api/v1/factions?host=127.0.0.1&port=7028' → {"factions":[{"origin":1,"factionId":1,"name":"Human","abbrev":"Hum"}]} ```