This is a component-by-component inventory of Gameplane's third-party
dependencies and why each one is there. It covers all 16 Go modules that
share go.work (netguard, gameaction, gameproto, telemetryschema,
svcutil, operator, api, agent, audit-syslog-bridge [optional],
telemetry-receiver [optional], sentinel [optional],
capture-sidecar [optional], tunnel [optional], gp-module,
mcp-server [optional], test/e2e), the
web/ npm package, and the build/CI toolchain that isn't a code dependency
but is invoked by make/GitHub Actions.
Only direct dependencies (the require block entries without
// indirect in each go.mod, and dependencies/devDependencies in
web/package.json) get individual justifications; indirect/transitive
dependencies are mentioned only where the tree itself is notable (e.g. the
k8s.io/* and sigstore/cosign trees pull in a large indirect closure).
Every "why" below was verified against real import/call sites — where a
dependency's purpose couldn't be pinned to a call site, that's stated
plainly rather than guessed.
How to regenerate/verify this report: cat <module>/go.mod for the
direct require block, then grep -rn "<import path>" <module>/ --include="*.go"
to find call sites (or go mod why -m <path> from inside the module). For
web/, check web/package.json against grep -rln "<package>" web/src.
Toolchain versions come from Makefile, .github/workflows/*.yaml, and
.devcontainer/post-create.sh.
Accurate as of 2026-09-02, repo state v0.3.0; the gameproto,
svcutil, sentinel, capture-sidecar, tunnel, and gp-module sections
were added and verified on 2026-09-26.
Note: The dependency versions listed in the table below reflect the state at v0.3.0. For the most current versions, refer to CHANGELOG.md.
| Component | Language | Direct deps | Why it exists |
|---|---|---|---|
netguard/ |
Go | 0 (stdlib only) | Shared SSRF dial-guard used by the operator and agent |
gameaction/ |
Go | 0 (stdlib only) | Shared console-injection guard + command-template renderer used by the agent and API |
gameproto/ |
Go | 0 (stdlib only) | Shared Minecraft/Terraria handshake classifier used by sentinel |
telemetryschema/ |
Go | 0 (stdlib only) | Shared telemetry report contract (types, strict decoder, official module catalog, report signing) used by the API reporter and the telemetry receiver |
svcutil/ |
Go | 0 (stdlib only) | Shared env-parsing + graceful-shutdown helpers (no active importers yet) |
operator/ |
Go | 19 | controller-runtime reconciler for the 8 CRDs; module OCI pull/verify, git/OCI fetch, restic-backup scheduling |
api/ |
Go | 17 | chi REST + WebSocket gateway: auth (local + OIDC), RBAC, K8s client, SQLite/Postgres persistence, notifications |
agent/ |
Go | 6 | In-pod sidecar: RCON/WebRcon console, chi HTTP surface, heartbeat status patch, disk usage stats |
audit-syslog-bridge/ |
Go | 0 (stdlib only) | Standalone HTTP-JSON → syslog relay for the audit webhook sink |
telemetry-receiver/ |
Go | 1 | Standalone collector for the API's usage telemetry (daily counts; extended reports add a random install ID and its public key; on by default for new installs; api.telemetry.enabled=false turns it off) |
sentinel/ |
Go | 2 | Wake-on-connect listener holding ports for sleeping pods |
capture-sidecar/ |
Go | 3 | Ephemeral AF_PACKET packet capture sidecar with BPF filtering |
tunnel/ |
Go | 0 (stdlib only) | Relay client supervisor for frp/Tailscale/playit |
gp-module/ |
Go | 2 | Module authoring CLI: scaffold, offline validate, dry-run preview, OCI package |
mcp-server/ |
Go | 5 | Standalone, strictly read-only MCP server for AI-assisted cluster diagnostics |
test/e2e/ |
Go | 4 | kind + Helm + real-component end-to-end suite |
web/ |
TypeScript/React | 18 runtime + 26 dev | Dashboard: routing, data fetching, UI primitives, console/file editors, tests |
netguard/go.mod has no require block at all — it imports only the
Go standard library (context, net, net/http, errors, strings,
syscall, time). It is a dial-time SSRF guard: IsAllowed (permissive,
for admin-configured infrastructure) and IsPublic (strict, for user-supplied
targets) both work by inspecting the resolved IP in a net.Dialer.Control
hook, so no client library is needed — just net and syscall for the
dial-time hook itself.
Callers and policies:
- Operator (
operator/internal/modsrc/): module-source HTTP/git fetches viaIsAllowed. - API gateway (
api/internal/notify/{notify,deliver}.goandapi/internal/steam/resolver.go): admin-configured notification sinks viaIsAllowed; Steam name resolution viaIsPublic. - Agent (
agent/internal/mods/mods.goandagent/internal/rcon/websocket.go): user-supplied mod downloads viaIsPublic; loopback WebSocket RCON viaIsAllowed.
It is a local replace module (replace github.com/GameplanePanel/gameplane/netguard => ../netguard in every importer's go.mod), not a published module — each Dockerfile that builds an importer must COPY netguard/ into the build context alongside the component's own source.
gameaction/go.mod also has no require block — stdlib only
(fmt, strconv, strings, text/template). It validates raw user
input against a module's declared action parameters (Resolve, rejecting
control characters and enforcing type/length/enum constraints — the
console-injection guard) and renders a text/template-based command
string from the sanitized params (Compile/Render).
Imported by:
agent/internal/actions/actions.go— the RCON quick-action transport.api/internal/ws/actions.go— the stdin pod-attach quick-action transport.
Both importers call Resolve independently before rendering, since each
is its own trust boundary (per the package doc comment). Also a local
replace module; same Dockerfile COPY requirement as netguard.
gameproto/go.mod has no require block at all — it imports only the
Go standard library (bufio, bytes, encoding/binary, encoding/json,
errors, fmt, io, strconv, strings). It is a wire-protocol parser that
classifies Minecraft (Java) and Terraria connection handshakes from the
first bytes off the wire, so a proxy can decide whether a connection is a
real client join before waking a sleeping pod.
Imported by:
sentinel/— to hold a client's connection open while the sentinel wakes the targetGameServer's pod, replaying the buffered handshake once the pod is ready.
Also a local replace module (replace github.com/GameplanePanel/gameplane/gameproto => ../gameproto in sentinel/go.mod); same Dockerfile COPY requirement as netguard.
telemetryschema/go.mod has no require block at all — it imports only the
Go standard library (bytes, crypto/ed25519, crypto/hkdf, crypto/rand,
crypto/sha256, embed, encoding/base64, encoding/hex, encoding/json,
errors, fmt, io, math, regexp, slices, sort, strings, time). It
is the shared telemetry report contract: the basic and extended report types,
the fixed category enumerations and band functions, the strict decoder, the
embedded official-module catalog, and Ed25519 report signing.
Imported by (spec 022, in progress):
telemetry-receiver/— to decode and validate reports (Decode) and to verify signatures (Verify).api/— to build, encode and sign the reporter's payload and the admin preview.
Both importers use a local replace module (replace github.com/GameplanePanel/gameplane/telemetryschema => ../telemetryschema); the same Dockerfile COPY requirement as netguard applies.
svcutil/go.mod has no require block at all — stdlib only
(log/slog, os, net/http, context, errors, time). It provides
shared environment-variable parsing (Or, OrInt, ParseLogLevel) and a
graceful HTTP shutdown helper (RunHTTP), meant to cut down on
copy-pasted startup/shutdown boilerplate across the repo's standalone Go
binaries.
It has no active importers yet (see svcutil/specs.md for adoption
status) — it ships as an available shared module ahead of the other
services being migrated onto it.
controller-runtime operator reconciling GameServer, GameTemplate, Backup,
BackupSchedule, Restore, Module, and ModuleSource. Direct deps from
operator/go.mod (excluding the local netguard replace, covered above):
| Dependency | Version | Why | Status |
|---|---|---|---|
sigs.k8s.io/controller-runtime |
v0.24.1 | Core reconciler framework — every controller in internal/controller/* embeds client.Client; cmd/main.go builds the manager via ctrl.NewManager |
direct-runtime |
k8s.io/api |
v0.37.1 | Core/apps/batch typed API objects used across api/v1alpha1/*_types.go and controllers to build Pod/PVC/Job/Service/RBAC objects |
direct-runtime |
k8s.io/apimachinery |
v0.37.1 | metav1.ObjectMeta, runtime.Scheme, types.NamespacedName, etc. — pervasive across CRD types and controllers |
direct-runtime |
k8s.io/client-go |
v0.37.1 | internal/controller/cluster_controller.go, gameserver_stop_attach.go (pod exec/attach via kubernetes.Clientset), cmd/main.go scheme/clientset setup |
direct-runtime |
github.com/go-git/go-git/v5 |
v5.19.2 | internal/modsrc/git.go — clones a ModuleSource's git repo/ref (HTTP or SSH) in-memory to discover module directories |
direct-runtime |
github.com/go-git/go-billy/v5 |
v5.9.1 | internal/modsrc/git.go — in-memory filesystem (memfs) backing the git clone above, avoiding disk writes for an admin-configured but externally-controlled fetch |
direct-runtime |
golang.org/x/crypto |
v0.55.0 | internal/modsrc/git.go — SSH key/host-key handling for git-over-SSH ModuleSource cloning, alongside go-git's SSH transport |
direct-runtime |
github.com/google/go-containerregistry |
v0.22.0 | internal/verify/verify.go — name.NewDigest + remote.WithAuth/WithContext resolve and authenticate the image ref being cosign-verified |
direct-runtime |
oras.land/oras-go/v2 |
v2.6.2 | internal/oci/client.go — pulls Module OCI bundle artifacts from the registry (registry/remote.Repository + retry transport) |
direct-runtime |
github.com/opencontainers/image-spec |
v1.1.1 | internal/oci/client.go — ocispec.Manifest/ocispec.Descriptor types for parsing OCI manifests when pulling module bundles |
direct-runtime |
github.com/sigstore/cosign/v2 |
v2.6.5 | internal/verify/verify.go — cosign.VerifyImageSignatures/CheckOpts verify Module OCI bundle signatures, keyed (against cosign.pub) or keyless (Fulcio/Rekor) |
direct-runtime |
github.com/sigstore/sigstore |
v1.10.9 | internal/verify/verify.go — pkg/fulcioroots supplies Fulcio root certs for keyless cosign verification |
direct-runtime |
golang.org/x/mod |
v0.40.0 | semver.Compare/semver.IsValid in internal/controller/semver.go, module_controller.go, internal/modsrc/oci.go, internal/oci/client.go — sorts/validates Module version tags |
direct-runtime |
sigs.k8s.io/yaml |
v1.6.0 | internal/modsrc/bundle.go parses module.yaml; internal/controller/module_controller.go parses the bundled template.yaml into JSON-tagged Go structs |
direct-runtime |
github.com/robfig/cron/v3 |
v3.0.1 | internal/controller/backupschedule_controller.go — parses a BackupSchedule's cron expression and computes the next run time |
direct-runtime |
github.com/kubernetes-csi/external-snapshotter/client/v8 |
v8.0.0 | internal/controller/backup_volumesnapshot.go/backup_controller.go — the CSI VolumeSnapshot types backing the volume-snapshot Backup/Restore strategy |
direct-runtime |
github.com/prometheus/client_golang |
v1.24.1 | internal/controller/metrics.go — custom GameServer-phase and Backup Prometheus collectors registered with controller-runtime's metrics registry |
direct-runtime |
github.com/go-logr/logr |
v1.4.3 | No non-test import found; only used in internal/controller/helpers_envtest_test.go (logr.Discard()). Runtime logging goes through controller-runtime's zap wrapper, which satisfies the logr.Logger interface without a direct import |
test-only |
github.com/opencontainers/go-digest |
v1.0.0 | Only imported by internal/oci/testregistry_test.go (a fake OCI registry double for tests) |
test-only |
Also declared as a tool directive (Go 1.24+ syntax, not a runtime
dependency): sigs.k8s.io/controller-tools/cmd/controller-gen — invoked by
make generate/make manifests to regenerate zz_generated.deepcopy.go
and the CRD/RBAC YAML.
The indirect closure is unusually large (~150 entries) because
sigstore/cosign and go-git each pull in substantial trees of their own
(TUF, Rekor, PKCS11, credential helpers, etc.) — normal for those
libraries, not a sign of dependency sprawl in Gameplane's own code.
chi-based REST + WebSocket gateway. Direct deps from api/go.mod
(excluding the local netguard/gameaction replaces, covered above):
| Dependency | Version | Why | Status |
|---|---|---|---|
github.com/go-chi/chi/v5 |
v5.3.2 | cmd/main.go router setup and nearly every file under internal/handlers/ and internal/ws/ — the HTTP router/middleware stack for the whole REST surface |
direct-runtime |
github.com/coder/websocket |
v1.8.15 | internal/ws/attach.go, podlogs.go, dialer.go — upgrades HTTP to WebSocket for exec/attach terminal streaming and pod-log tailing, and dials the outbound WS proxy path |
direct-runtime |
github.com/coreos/go-oidc/v3 |
v3.20.0 | internal/auth/oidc.go — oidc.NewProvider + IDTokenVerifier for OIDC discovery and ID-token verification in the login flow |
direct-runtime |
golang.org/x/oauth2 |
v0.36.0 | internal/auth/oidc.go — oauth2.Config drives the OIDC authorization-code exchange |
direct-runtime |
golang.org/x/crypto |
v0.55.0 | internal/auth/password.go — argon2.IDKey hashes/verifies local-login passwords (argon2id), plus a constant-time dummy-verify path to avoid a user-enumeration timing leak |
direct-runtime |
k8s.io/api, k8s.io/apimachinery, k8s.io/client-go |
v0.37.0 | internal/kube/*.go (client wrapper, kubeconfig loading, watches, exec streaming), internal/handlers/*.go, internal/notify/sinks.go, internal/rbac/rbac.go — reading/writing CRDs and core objects |
direct-runtime |
sigs.k8s.io/controller-runtime |
v0.24.1 | cmd/main.go — only ctrl.GetConfig() is used, to load the kubeconfig/in-cluster *rest.Config at startup; not the manager/reconciler machinery (that's the operator's job — see rule "the operator is authoritative"). envtest.Environment in internal/handlers/suite_envtest_test.go is test-only |
direct-runtime (minimal) |
sigs.k8s.io/yaml |
v1.6.0 | internal/handlers/module_upload.go — parses module.yaml metadata and template.yaml from uploaded module bundles |
direct-runtime |
github.com/minio/minio-go/v7 |
v7.3.0 | internal/audit/s3.go — S3-compatible client (minio.New, PutObject) backing the S3 audit-log sink |
direct-runtime |
github.com/prometheus/client_golang |
v1.24.1 | cmd/metrics.go (promhttp handler at /metrics on the separate metrics listener); promauto counters in internal/notify/notify.go, internal/audit/s3.go, internal/audit/audit.go |
direct-runtime |
modernc.org/sqlite |
v1.57.0 | internal/db/db.go — blank-imported to register the "sqlite" database/sql driver, the default persistence backend |
direct-runtime |
github.com/jackc/pgx/v5 |
v5.11.0 | internal/db/db_postgres.go (behind //go:build postgres) — wraps the pgx/v5/stdlib connector (rebinding ? placeholders to $n) when built with -tags postgres; the alternative persistence backend |
direct-runtime (opt-in build tag) |
golang.org/x/mod |
v0.40.0 | internal/handlers/semver.go — semver.Compare orders module versions for the version-switch/update-detection UI |
direct-runtime |
golang.org/x/sync |
v0.22.0 | internal/registry/registry.go — singleflight.Group collapses concurrent DB + live-Secret registry-credential lookups into one in-flight call |
direct-runtime |
github.com/go-jose/go-jose/v4 |
v4.0.2 | No non-test import found; used only in internal/auth/oidc_issuer_test.go/oidc_more_test.go to build fake JWKS/signed JWTs for testing the OIDC verifier |
test-only |
github.com/prometheus/client_model |
v0.6.2 | No non-test import found; used in internal/audit/audit_webhook_test.go/s3_test.go (dto "prometheus/client_model/go") to assert counter values in tests |
test-only |
Confirmed policy: k8s.io/metrics is not imported anywhere in api/
(grep -rn "k8s.io/metrics" api/ returns nothing but the explanatory
comments). internal/handlers/cluster.go reads the metrics-server
aggregated API (/apis/metrics.k8s.io/v1beta1/nodes) via
h.k.Typed.CoreV1().RESTClient().Get().AbsPath(...).DoRaw(ctx) — a raw
REST call over the existing typed client-go clientset — specifically to
avoid adding a whole extra module for one read-only GET. See "Notable
policies" below.
In-pod sidecar handling console/RCON, files, logs, players, heartbeat,
and quiesce. Direct deps from agent/go.mod (excluding the local
netguard/gameaction replaces, covered above):
| Dependency | Version | Why | Status |
|---|---|---|---|
github.com/go-chi/chi/v5 |
v5.3.2 | cmd/main.go — router for the agent's whole HTTP surface; every feature package (console, logs, files, players, status, actions, quiesce, lifecycle, mods) exposes Mount(r chi.Router, ...) onto it |
direct-runtime |
github.com/coder/websocket |
v1.8.15 | Dual role: server-side WS upgrade for internal/console/console.go (RCON console stream) and internal/logs/logs.go (live log tail); client-side outbound dial in internal/rcon/websocket.go implementing the Rust dedicated server's WebRcon protocol |
direct-runtime |
k8s.io/apimachinery |
v0.37.1 | internal/heartbeat/heartbeat.go only — metav1.Now()/PatchOptions{}, types.MergePatchType to build the JSON merge-patch that updates status.agent |
direct-runtime |
k8s.io/client-go |
v0.37.1 | internal/heartbeat/heartbeat.go only — rest.InClusterConfig() then dynamic.NewForConfig(...).Resource(gvr).Namespace(...).Patch(...) to patch the owning GameServer's status every tick; only the dynamic + rest subpackages are used, no typed clientset |
direct-runtime |
github.com/prometheus/client_golang |
v1.24.1 | cmd/main.go — serves promhttp.Handler() at /metrics; only the promhttp subpackage, no custom collectors |
direct-runtime |
golang.org/x/sys |
v0.47.0 | internal/usage/usage.go — unix.Statfs/unix.Statfs_t to report the game data volume's disk usage over heartbeat. Not used for a PTY: internal/console/console.go's doc comment states the agent's console is RCON-only by design ("no real PTY"); a GameTemplate.spec.consoleMode: "pty" game is bridged instead through api/internal/ws/attach.go against the Kubernetes pod-attach API, with no agent involvement at all |
direct-runtime |
Note k8s.io/apimachinery/k8s.io/client-go are at v0.37.1 here, as in
operator and sentinel; api, mcp-server and test/e2e are still on v0.37.0.
Each Go module in the workspace resolves its own client-go version
independently (there's no shared root go.mod), so patch versions can
drift between modules as Dependabot bumps them one module at a time.
audit-syslog-bridge/go.mod has no require block — stdlib only
(crypto/subtle, crypto/tls, log/slog, net, net/http, etc.). It's
a small, schema-agnostic HTTP-JSON → syslog (RFC 5424) relay: it accepts
one JSON POST body, frames it as an RFC 5424/6587 message, and forwards it
over a lazily-dialed TCP/TCP+TLS/UDP connection (main.go's forwarder
type). Sits behind the API's audit webhook sink
(api.audit.webhook.syslogBridge.enabled). No third-party library needed
because the entire job is HTTP-in, hand-formatted-string-out.
telemetry-receiver/go.mod has exactly one direct dependency:
| Dependency | Version | Why |
|---|---|---|
github.com/prometheus/client_golang |
v1.24.1 | main.go — prometheus.NewRegistry() + CounterVec/Histogram for gameplane_telemetry_reports_total/_servers/_templates, exposed via promhttp.HandlerFor at /metrics |
Deliberately kept to "stdlib + client_golang only" (per the package doc
comment) so it can be deployed standalone on a public host collecting
reports from many installs, independent of Gameplane's release cadence.
It validates the API's daily {version, servers, templates} telemetry
POST, logs it structurally, and aggregates it into the metrics above —
never storing raw per-install reports.
Strictly read-only Model Context Protocol server for AI-assisted cluster
diagnostics. Direct deps from mcp-server/go.mod:
| Dependency | Version | Why |
|---|---|---|
github.com/modelcontextprotocol/go-sdk |
v1.8.0 | main.go/tools.go — mcp.NewServer, mcp.AddTool, mcp.StdioTransport implement the MCP (JSON-RPC 2.0) protocol itself; every registered tool (list_gameplane_resources, get_gameplane_resource, list_pods, get_pod, list_events, get_pod_logs, propose_fix) is built against this SDK's types |
k8s.io/client-go |
v0.37.0 | internal/kube/client.go — kubernetes.NewForConfig/dynamic.NewForConfig build the typed and dynamic clientsets, kept as unexported fields on Client so no exported method can reach a mutating verb |
k8s.io/api |
v0.37.0 | internal/kube/client.go — corev1 types for the typed Pod/Event reads (ListPods, GetPod, ListEvents, PodLogs) |
k8s.io/apimachinery |
v0.37.0 | internal/kube/client.go — unstructured.Unstructured(List), runtime.Scheme, schema.GroupVersionResource back the dynamic-client reads of 7 of Gameplane's 9 CRDs (not Cluster or NetworkCapture) (redeclared GVK/GVR locally rather than importing the operator module's generated types, to stay standalone) |
sigs.k8s.io/controller-runtime |
v0.25.1 | main.go — only ctrl.GetConfig(), to load the kubeconfig (in-cluster, falling back to KUBECONFIG/~/.kube/config) that builds the kube.Client above |
The read-only guarantee has two layers: the handler boundary (only
List/Get-shaped methods are exported from internal/kube, and
tools.go's handlers receive only that *kube.Client) and RBAC (a
get/list/watch-only ClusterRole), which is the layer that stops
mutation by any code in the process. The go-sdk itself has no bearing
on that guarantee; it's purely the protocol transport.
Wake-on-connect proxy that holds a listener open for a sleeping
GameServer's port, classifies the first bytes of an inbound connection
via gameproto, and triggers a pod wake before replaying the buffered
handshake. Direct deps from sentinel/go.mod (excluding the local
gameproto replace, covered above):
| Dependency | Version | Why |
|---|---|---|
k8s.io/apimachinery |
v0.37.1 | main.go — typed/unstructured plumbing for reading the target GameServer's status and idle/wake fields |
k8s.io/client-go |
v0.37.1 | main.go — in-cluster client used to read GameServer status and patch it to trigger a wake |
Ephemeral, on-demand AF_PACKET packet capture container that runs
alongside a GameServer pod with CAP_NET_RAW (see
docs/security.md for the capability/PodSecurity
trade-off). Direct deps from capture-sidecar/go.mod:
| Dependency | Version | Why |
|---|---|---|
github.com/gopacket/gopacket |
v1.7.2 | internal/capture/writer.go/afpacket.go — AF_PACKET socket capture via gopacket/afpacket, plus pcap-format frame writing via pcapgo/layers |
github.com/packetcap/go-pcap |
v0.0.0-20260731105150-c86974bbfbcd | internal/capture/filter.go — compiles the tcpdump-style filter expression to BPF without requiring libpcap at runtime |
golang.org/x/net |
v0.59.0 | internal/capture/filter.go/afpacket.go — x/net/bpf: assembling the classic-BPF program attached to the AF_PACKET socket |
tunnel/go.mod has no require block at all — stdlib only
(os/exec, encoding/json, net/http, context, etc.). It's the relay
client supervisor: it launches and monitors the frp, tailscaled, or
playitd client binary as a subprocess, configured by env vars
(GAMESERVER_NAME, TUNNEL_TYPE, etc.) that the operator renders from
spec.networking.tunnel. For playit, playit_reporter.go polls
playitd's local Unix-socket IPC (newline-delimited JSON get_state
frames) for the assigned public addresses, then PATCHes the GameServer
resource's status.tunnelEndpoints directly against the Kubernetes API
server (https://kubernetes.default.svc, using the pod's service-account
token) with a stdlib net/http client — nothing is reported to the
agent. The frp and Tailscale addresses are computed by the operator
itself rather than polled by the supervisor. No third-party library is
needed because process supervision, JSON/Unix-socket IPC, and HTTP status
patching are all stdlib jobs.
Module authoring CLI (init, validate, preview, package) used by
module authors to scaffold, offline-validate, dry-run preview, and OCI
package a game module before publishing it. Direct deps from
gp-module/go.mod:
| Dependency | Version | Why |
|---|---|---|
gopkg.in/yaml.v3 |
v3.0.1 | internal/common/yaml_ast.go, internal/validator/, internal/scaffold/, internal/preview/, internal/packager/ — parses/round-trips module.yaml/template.yaml while preserving comments and key order for scaffold output |
k8s.io/apimachinery |
v0.37.0 | internal/preview/memory.go — resource.Quantity parsing, to preview a template's resource requests/limits the same way the operator would |
React 18 + TypeScript strict + Vite dashboard (web/package.json version
0.3.0, matching the repo version):
Framework / core
react^19.2.8,react-dom^19.2.8— base UI framework, rendered viacreateRootinsrc/main.tsx, used throughout every component.
Routing / data fetching
@tanstack/react-router^1.170.32— file-based route tree;createRouter/RouterProviderinsrc/main.tsx, tree built insrc/router/tree.tsx,Link/useNavigateused acrosssrc/routes/*.tsx.@tanstack/react-query^5.102.8— server-state cache;QueryClient/QueryClientProviderinsrc/main.tsx,useQuery/useMutationused pervasively across routes andsrc/lib/— the dominant data-fetching layer.@tanstack/react-virtual^3.14.10—useVirtualizerinsrc/routes/tabs/Logs.tsx, virtualizes the log-line list.@tanstack/router-devtools(dev) — no import found anywhere insrc/. Installed but apparently unused; flag as a candidate for removal or an intentionally-deferred addition.
UI primitives (HeroUI v3)
@heroui/react^3.2.4— foundational component library (Button, Input, Select, Modal, Table, etc.); composed and themed insrc/components/ui/(Gameplane's composition layer over HeroUI).@heroui/styles^3.2.4— companion style package with semantic token infrastructure for light/dark modes and brand customization;@heroui/stylesis imported insrc/main.tsxfor global theme setup.clsx^2.1.1+tailwind-merge^3.6.0— combined into thecn()helper insrc/lib/utils.ts, used app-wide for conditional/merged Tailwind classes and with HeroUI's theme tokens.lucide-react^1.37.0— icon set, imported in roughly 35 files (AppLayout,Dashboard,Login, dialogs, etc.).react-aria— peer dependency of HeroUI, providing accessibility primitives (viareact-aria-components) used by HeroUI atoms.
Editors / terminals
@monaco-editor/react^4.7.0— theEditorcomponent insrc/routes/tabs/Files.tsx(in-browser file editor) andPlacement.tsx; given its ownmanualChunksbundle invite.config.ts.@xterm/xterm^6.0.0+@xterm/addon-fit^0.11.0—Terminal/FitAddoninsrc/routes/tabs/useConsoleTerminal.ts, driving the live server console (Console.tsx); also chunked separately invite.config.ts.
Styling / build
tailwindcss^4.3.3,autoprefixer^10.5.4,postcss^8.5.26(dev) —postcss.config.jswires Tailwind + autoprefixer;tailwind.config.tsdefines the design tokens used throughoutsrc/stylesand Tailwind classes app-wide.vite^8.2.2,@vitejs/plugin-react^6.1.1(dev) —vite.config.tsdrives the dev server (with the API/WS proxy to the in-cluster API used bymake web-dev), production build, and is the engine behindvitest.config.ts.typescript^6.0.3(dev) — strict-mode compile pertsconfig.json;npm run buildistsc -b && vite build.
Lint (dev, all wired directly in eslint.config.js)
eslint^9.39.5,@eslint/js^10.0.1,@typescript-eslint/eslint-plugin^8.68.0,@typescript-eslint/parser^8.68.0,eslint-plugin-react^7.37.5,eslint-plugin-react-hooks^7.1.1,globals^17.11.0— flat-config ESLint enforcingstrict,@typescript-eslint/no-explicit-any: error,@typescript-eslint/no-floating-promises: error, run vianpm run lint.
Testing — unit tier (Vitest)
vitest^4.1.11,@vitest/coverage-v8^4.1.10(dev) —vitest.config.ts(jsdom environment, coverage thresholds), run vianpm test/test:cover.jsdom^30.0.1(dev) — the test environment itself.@testing-library/react^16.3.3,@testing-library/user-event^14.6.6,@testing-library/jest-dom^7.0.1,@testing-library/dom^10.4.1(dev) —render/screen, interaction simulation, and DOM matchers used across the co-located*.test.tsxfiles.msw^2.15.0— dual role: unit-test request mocking (src/test/server.ts/handlers.ts, used viasetupServerin most*.test.tsxfiles) and browser-mode mocking for Playwright's mock tier (src/test/browser-msw.tsusesmsw/browser'ssetupWorker, loaded bymain.tsxwhenVITE_E2E_MOCKis set).vitest-websocket-mock^0.7.0(dev) —src/test/ws.ts, mocks the WebSocket used bysrc/lib/ws.ts/console streaming in unit tests.
Testing — e2e tier (Playwright)
@playwright/test^1.62.1(dev) —playwright.config.tsdefines mock vs. live run modes (GAMEPLANE_E2E_TARGET), driven bymake test-web-e2e-mock/test-web-e2e-live. Live mode hits a real kind cluster via akubectl port-forwardglobalSetup; mock mode reuses the samemswhandlers as the unit tier.
Types
@types/react^19.2.18,@types/react-dom^19.2.5,@types/node^26.4.0(dev) — type definitions consumed implicitly by TypeScript, no direct import.
kind + Helm + real-component end-to-end suite (build tag e2e), run
against a throwaway kind cluster per CI bucket or a reused
cluster/kubelab via GAMEPLANE_E2E_REUSE_CLUSTER. Direct deps from
test/e2e/go.mod:
| Dependency | Version | Why |
|---|---|---|
github.com/coder/websocket |
v1.8.15 | api_ws_e2e_test.go — a real WebSocket client dialing the API's WS endpoints (console/logs) end-to-end, exercising the same protocol the dashboard's web/src/lib/ws.ts uses |
k8s.io/client-go |
v0.37.0 | Used across most *_e2e_test.go files to build a real cluster client, apply/watch objects, and assert on cluster state after driving the API/dashboard |
k8s.io/apimachinery |
v0.37.0 | 28 files reference it — metav1, types, runtime types used throughout the assertions and helper builders (env.go, test_helpers_e2e_test.go) |
k8s.io/api |
v0.37.0 | Core/apps typed objects used to construct and inspect Pods/Deployments/etc. during the e2e flows |
The k8s.io/* trio here is on v0.37.0, like api and mcp-server
(operator, agent and sentinel are on v0.37.1); each module in the workspace resolves its own
client-go version independently (there's no shared root go.mod), so
patch versions can drift between modules.
These aren't code dependencies of any component; they're invoked by
Makefile targets and GitHub Actions workflows.
sigs.k8s.io/controller-tools/cmd/controller-gen(tooldirective inoperator/go.mod, pulling insigs.k8s.io/controller-tools v0.20.1indirect) —make generate/make manifestsregeneratezz_generated.deepcopy.goand the CRD/RBAC YAML.github.com/vladopajic/go-test-coverage/v2@v2.18.9— pinned inMakefile'sGO_TEST_COVERAGE_PKG, run viago run(no install step) bymake cover-go-checkand CI'sgojob to enforce each module's.testcoverage.ymlthreshold.github.com/wadey/gocovmerge@v0.0.0-20160331181800-b5bfa59ec0ad— pinned inMakefile'sGOCOVMERGE_PKG, merges each module'sunit.out+envtest.outcoverage profiles before the threshold gate.sigs.k8s.io/controller-runtime/tools/setup-envtest@release-0.19— installed bymake envtest-bin; downloads the K8s 1.36.2 envtest binaries (kube-apiserver,etcd) that backoperator's andapi's-tags=envtestintegration tests.golangci-lintv2.12.2 — pinned in.devcontainer/post-create.sh(built from source, not the release binary, because the release binary is built with an older Go than the repo'sgo 1.26.0toolchain wants). Configured by.golangci.yml(bodyclose, errcheck, gosec, govet, ineffassign, staticcheck, unused, misspell, gofmt, goimports, revive, unparam, nilerr, noctx, errorlint, contextcheck). Not currently invoked by CI —ci.yaml'sgojob runs onlygo vet, notgolangci-lint run;make lint-goexists for local/devcontainer use and is called out indocs/contributing.mdas a pre-PR step, but nothing in.github/workflows/gates on it. Same for the web job: it runsnpm run buildandnpm run test:cover, notnpm run lint— ESLint isn't CI-gated either.orasCLI ≥ 1.2.0 (pinned1.2.0in the devcontainer;oras-project/setup-oras@v2.0.1action inrelease.yaml) —modules/build.shpushes eachmodules/<name>/directory as an OCI module bundle (make modules-push); this is a different thing fromoperator'soras.land/oras-go/v2library dependency, which is the operator's own pull-side client, not the CLI.cosignCLI v3.1.2 (sigstore/cosign-installer@v4.1.2) — used inpublish-edge.yaml/release.yamlto sign published container images, the Helm chart, and module bundles againstCOSIGN_PRIVATE_KEY, recording each signature in the public Rekor transparency log, and to round-trip-verify against the derived public key before publishing. Signatures use the classic format (--new-bundle-format=false --use-signing-config=false), which is what keeps them readable by the operator'ssigstore/cosign/v2library; cosign's newer Rekor v2 flow additionally requires an RFC3161 timestamp authority that keyed signing cannot satisfy.modules/build.shgates the same behaviour behind its own--tlog-uploadflag so local and air-gapped authoring stays offline. Distinct fromoperator'ssigstore/cosign/v2library dependency, which only ever verifies (never signs) at reconcile time.kindv0.32.0 (devcontainer pin) /helm/kind-action@v1.14.0(install_only: truein CI) — spins up the ephemeral clusters formake dev-up,make test-e2e, and everye2e-*CI job.helm(azure/setup-helm@v5.0.1in CI;kubectl-helm-minikubedevcontainer feature,helm: latest) — chart lint (helm lint), chart render sanity-check,helm template, andhelm upgrade --installindeploy/kind/*.sh/Makefile'sdev-install.docker buildx bake(containers/docker-bake.hcl,docker/bake-action@v6) — builds the operator/api/agent e2e images concurrently with a shared GitHub Actions layer cache (.github/actions/e2e-images); also builds thee2e-gameprobeheadless bot image used only by the game-bot CI job.docker/build-push-action@v6+docker/metadata-action@v5— the actual multi-arch (linux/amd64,linux/arm64) image builds and GHCR tagging inpublish-edge.yaml(main →:edge) andrelease.yaml(v*tags → versioned images +oci://ghcr.io/<owner>/chartsHelm push).
The Helm chart itself (charts/gameplane/Chart.yaml) declares no chart
dependencies: — it's a single chart with no subcharts.
- No
k8s.io/metricsmodule.api/deliberately avoids addingk8s.io/metricsas a dependency for the one place it needs metrics-server data (internal/handlers/cluster.go's node CPU/mem panel): it issues a rawGET /apis/metrics.k8s.io/v1beta1/nodesthrough the existing typed client-go clientset'sRESTClient()instead. Adding a new module for a single read-only GET was judged not worth the extra dependency surface — the same reasoning that keepsnetguard,gameaction,audit-syslog-bridge, andtelemetry-receiverthis lean. - Persistence is driver-selectable, but Postgres needs a rebuild.
api/internal/dbregistersmodernc.org/sqlite(pure-Go, default) unconditionally, and compiles ingithub.com/jackc/pgx/v5'sstdlibdriver only under thepostgresbuild tag (db_postgres.go); without that tag,db_nopostgres.gostands in andOpen(ctx, "postgres", dsn)returns"postgres support not compiled in — rebuild with -tags postgres". Confirmed:api/Dockerfile(and thus the publishedghcr.io/.../apiimage) does not pass-tags postgres— this is intentional and documented, not an oversight:charts/gameplane/values.yaml'sapi.db.drivercomment explicitly tells an operator who wants Postgres [experimental] to setdsnand rebuild the image with-tags postgresthemselves. Sopgx/v5is a real, live dependency, but it ships dormant in the default binary. - netguard's two policies, three importers. Both
IsAllowed(permissive, for admin-configured infrastructure) andIsPublic(strict, for user-supplied targets) are used by operator (module sources), API gateway (notification sinks viaIsAllowed; Steam resolver and module registry viaIsPublic), and agent (mod downloads viaIsPublic; loopback WebSocket RCON viaIsAllowed). The split prevents private-registry access from being re-opened (if strict rules apply to the operator) or agent SSRF from being introduced (if permissive rules apply to the agent). - Local
replacemodules need explicit DockerCOPYs.netguardandgameactionare both resolved viareplace ... => ../netguard/../gameactiondirectives, not published modules. Every Dockerfile that builds an importer (operator/Dockerfile,api/Dockerfile,agent/Dockerfile) mustCOPYthose directories into the build context; a Dockerfile edit that forgets this breaks the build with an unresolved module error, not a subtle bug — but it's a manual step to remember when adding a new local module. - Signing has two independent code paths that must be kept in sync.
Verification (
operator'ssigstore/cosign/v2library, checked at Module-reconcile time againstcosign.pub) and signing (thecosignCLI v3.0.6,sigstore/cosign-installer@v4.1.2in CI) both use classic cosign signature format (--new-bundle-format=false --use-signing-config=false) for mutual compatibility. A bump to either side needs to preserve that format compatibility and the ability to sign with--tlog-upload(Rekor logging). - Lint is configured but not CI-enforced.
.golangci.ymlandweb/eslint.config.jsare both real, actively-maintained rule sets (see "Fix, don't silence" inCLAUDE.md), but neithergolangci-lint runnornpm run lintcurrently appears in.github/workflows/ci.yaml— onlygo vet(Go) andnpm run build/test:cover(web) are CI-gated. Lint is a devcontainer/local (make lint) anddocs/contributing.mdpre-PR-checklist step today, not an automated CI gate. - Workspace-wide
k8s.io/*versions. The six client-go consumers are one patch apart:operator,agentandsentinelare onk8s.io/*v0.37.1,api,mcp-serverandtest/e2eon v0.37.0. Becausego.worklinks independent modules rather than a single rootgo.mod, Dependabot bumps them one module at a time and patch versions drift between bumps.