Architecture
Components
Section titled “Components”| Panel | .NET 10, ASP.NET Core minimal APIs, EF Core with PostgreSQL, ASP.NET Core Identity. Serves the API, the live updates (SignalR), the agent endpoint and the web app from one origin. |
| Web app | React, Vite, TypeScript, shadcn/ui, TanStack Router and Query; installable (PWA) with its own service worker. |
| Agent | a .NET worker in a container with the Docker socket, on each node. |
| Database | PostgreSQL. |
Every library is MIT, Apache-2.0 or PostgreSQL-licensed.
The agent connection
Section titled “The agent connection”- The agent dials the panel and keeps a WebSocket open; messages are protobuf, typed and versioned. WebSockets pass any reverse proxy or load balancer, unlike gRPC streams, which need HTTP/2 end to end.
- The connection is outbound only and uses the panel’s HTTPS certificate: nodes need no open management port and no certificate.
- Enrollment exchanges a one-time token for the node’s own secret; the panel keeps only a hash.
- An agent that reconnects reports its servers; jobs it lost are marked failed.
Containers
Section titled “Containers”- One container per game server, created on every start from the server’s current settings.
- Hardening: all capabilities dropped,
no-new-privileges, limits for memory (without swap), CPU and processes, one Docker network per tenant; optionally read-only, user namespaces and gVisor (Hardening a node). - Installs, updates and Steam sign-ins are jobs in throwaway containers.
Secrets
Section titled “Secrets”- Secrets are encrypted with ASP.NET Core Data Protection, one purpose per tenant. The key ring lives in PostgreSQL and is itself encrypted (AES-256-GCM) with the master key file, so a database dump alone isn’t enough.
- Steam passwords reach only job containers, typed into SteamCMD’s prompt: never in the environment, the command line or a file. Environment variables and command lines are visible in
docker inspectandps, and plain Docker has no tmpfs that can be filled before a container starts. - Steam logins are kept in a Docker volume per account and node, mounted only into jobs.
- The agent serves SFTP itself, like Pterodactyl’s Wings: file transfers go straight to the node, never through the panel.
- At login, the agent asks the panel, which checks the password or SSH key and the user’s access, and answers with the effective file permissions and paths. The agent enforces them per request with the panel’s own rules.
- Only SFTP is offered: no shell, commands or port forwarding; symbolic links can’t be created or read.
Port forwarding
Section titled “Port forwarding”- Providers are modules implementing
IExposureProvider; each declares its settings, which become a form, with secret fields stored encrypted. - The panel computes the rules from the node’s allocations in use, compares them with what the provider has, and applies the difference: after changes, and every 15 minutes to repair drift.
- Providers only touch rules prefixed
server-panel:.
Config files
Section titled “Config files”Templates carry configFiles, applied by the agent before every start, as Wings does for eggs: without it, imported games would ignore their allocated ports.