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/versions.toml (dev container pins; .devcontainer/setup.sh installs them).
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.24— 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.14.0 — pinned in.github/workflows/ci.yaml,.devcontainer/versions.toml(golangci_lint, installed by.devcontainer/setup.sh), and the exact-version guard intest/e2e/lint-gate-verify.sh. Itsgolang.org/x/tools v0.50.0importer supports the export-data version 5 emitted by Go 1.27.3; v2.13.2's importer does not. CI uses the published binary and the devcontainer builds the same release from source. The CIlintmatrix gates every selected workspace module with.golangci.yml, usingenvtesttags for operator/API ande2etags for test/e2e. Update the verifier's valid fixtures with the pin; unsupported or unpinned versions must still fail. Tests and general lint run only in CI under repository policy.orasCLI ≥ 1.2.0 (pinned1.2.0in.devcontainer/versions.toml;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 (pinned in.devcontainer/versions.toml) /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;helm4.3.1 pinned in.devcontainer/versions.tomland installed by the full dev container profile) — 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.