Skip to content

v0.4.0

Latest

Choose a tag to compare

@github-actions github-actions released this 11 Nov 15:48
· 161 commits to master since this release
Immutable release. Only release title and notes can be modified.
v0.4.0
46b719f

A pre-v1 release with every change made since v0.3.0: an independent central
panel, languages, the extended telemetry tier, safer backups and restores,
fleet-wide node inventory, and a pre-release security hardening pass. Read the
upgrade notes below before upgrading; several of them change behaviour. It
does not claim v1 or production support; see docs/roadmap.md.

Fixed

  • Backup, restore and wipe exclusion (#718): backups, restores and wipes
    of the same server now take one lifecycle ownership, so a backup can no
    longer read a filesystem a restore or wipe is changing, and wipe workers are
    drained before workloads come back (PRs #760, #619).
  • Backup captures: backup workers are prepared before quiesce, CSI
    snapshot captures are bounded, and a capture taken across a game container
    restart is invalidated instead of being kept (PRs #836, #846).
  • Backups with retention disabled are kept (PR #618).
  • Wake on connect: wake requests are bounded, HostPort hand-offs are
    preserved, and shared wakes retry within their budget instead of failing on
    the first cold-start error (PRs #840, #860).
  • Tunnels: frp identities are isolated per server and report relay
    connection health; Tailscale forwards every advertised TCP and UDP port and
    keeps active UDP sessions (PRs #768, #765).
  • New files are created atomically and never overwrite an existing file
    (PR #621).
  • Standalone Helm CRD isolation: the central panel profile excludes bundled
    native CRDs and their apply hook/RBAC without requiring --skip-crds.
    Default operator installations keep Helm's native CRD lifecycle; existing
    CRDs and their custom resources are retained when a profile omits them.

Upgrade Notes

  • Breaking: admin-level grants need a full administrator: holders of
    users:manage, roles:manage or config:manage without * can no longer
    grant * or another admin-level permission, edit or reset an administrator,
    or edit auth providers (/admin/config/auth, /admin/auth need *)
    (PR #862).
  • Breaking: kubelet probes reach only probe ports: a new per-server
    <server>-probes NetworkPolicy admits kubelet CIDRs to the game container's
    probe ports only, on by default with networkPolicies.probeIngress.enabled.
    The old all-ports allow-kubelet-probes rule renders only when that is
    false or extra probePorts are listed. Set
    networkPolicies.probeIngress.enabled: false to restore the old rule
    (PR #862).
  • Breaking: custom CSS: stylesheets with \ escapes or absolute,
    http(s): or protocol-relative // references are now rejected (PR #862).
  • Share links get their own Ingress: /share and /shares go through a
    second Ingress, gameplane-share, with access logging off for ingress-nginx
    (ingress.shareLinks.*). Other controllers need their own switch in
    ingress.shareLinks.annotations; see docs/install.md
    (PR #862).
  • Password reset ends every session of that account (migration
    019_session_epoch.sql, applied on API startup) (PR #862).
  • The API is a singleton in every mode: every database driver uses the
    Recreate strategy and api.replicas other than 1 is rejected, so upgrades
    no longer overlap two API processes (#726, PR #758).
  • Auxiliary resource requests and management placement: agent,
    config-init, sentinel, tunnel and Restic pods now request resources on every
    install, and chart-managed pods accept placement settings. Game and
    auxiliary pods can restart on upgrade; see
    docs/release-notes.md (#724, PR #762).
  • Standalone panel/API: run the central panel with Docker Compose or the
    standalone Helm profile, without a local game cluster or operator. Compose
    needs no Kubernetes installation on the panel host.
    Register remote clusters explicitly. Back up the database and matching
    credential encryption key separately, with restricted access and encrypted
    storage for key backups. Supplied profiles use key storage outside the database
    volume. Preserve the original key when migrating legacy /data/panel.key
    installations. See standalone installation.
  • Breaking: selected-cluster module/template grants: in combined and
    standalone installations, catalog permissions now require a cluster-wide
    grant on the selected cluster or wildcard cluster. A grant on local retains
    local access but no longer authorizes a remote catalog. Before upgrading,
    identify users who rely on remote catalog access and grant a role containing
    modules:read, modules:manage, templates:read, and/or templates:write
    on each intended remote cluster, selecting All namespaces. Grant only the
    operations each user needs; namespace-only grants do not authorize catalogs.
    See grant workload access.
  • Telemetry is on by default for new installs, after a first-login
    notice:
    a new install with a telemetry destination in effect now shares
    basic and extended data by default; the stock destination is the project's
    receiver at telemetry.gameplane.net. The first admin to sign in sees a
    notice that lists every field of both tiers and names the destination;
    nothing is sent before then. Basic is version, servers and
    templates. Extended adds a random install ID, the Kubernetes minor
    version, a distribution category, node architectures and a node-count
    band, server counts per official module plus one custom count, feature
    flags (wake-on-connect, tunnel types, packet capture, backups, single
    sign-on, audit forwarding), a cluster-count band, the database kind, the
    UI language, the install's public signing key and the send time. Each
    tier has its own toggle in Admin Settings → Telemetry, which also
    shows the destination, a preview of the next report and an install-ID
    reset. The data-handling statement is at
    https://gameplane.net/telemetry/. See
    docs/install.md.
  • Installs whose basic toggle was already on start sending to the
    project's default receiver:
    an install that saved Send anonymous usage
    metrics
    while no destination was configured (so it had no effect) starts
    sending basic reports to the project's default receiver
    (telemetry.gameplane.net) after upgrade.
    Extended stays off until an admin turns it on, and installs that never
    saved a choice stay off. Installs already using a custom
    api.telemetry.endpoint or the bundled receiver keep sending only there.
    Set api.telemetry.enabled=false before upgrading to prevent any
    sending.
  • Empty api.telemetry.endpoint now means the project default:
    It now means the project's default receiver
    (https://telemetry.gameplane.net/ingest), so the admin toggles have an
    effect on a stock install. Set api.telemetry.enabled=false for a hard off (air-gapped
    clusters, privacy-sensitive installs); it overrides the endpoint, the
    bundled receiver and the admin toggles, and hides the notice.
  • The receiver's /metrics moved to the dashboard port and needs the
    dashboard token:
    the receiver's Prometheus endpoint is no longer on
    :8080; it is on :8081 and accepts only
    Authorization: Bearer <DASHBOARD_TOKEN>. Without
    api.telemetry.receiver.dashboard.tokenSecretRef.name there is no
    dashboard listener and no metrics endpoint, and the chart renders no
    ServiceMonitor for the receiver. Update your Prometheus scrape config
    or set the token Secret. See
    docs/telemetry-provider.md.
  • The bundled receiver now stores data: with
    api.telemetry.receiver.persistence.enabled (default true, 1Gi) it
    keeps daily aggregates for 24 months and activity records for 90 days in
    SQLite on a PVC, and runs a single replica with a Recreate strategy.
    helm upgrade --reuse-values from 0.3.0 needs no new values; with no
    persistence block the receiver keeps using an emptyDir, as before.
  • The receiver's DASHBOARD_TOKEN must be at least 32 characters: a
    receiver with DASHBOARD_TOKEN set to a shorter value now exits at
    startup (DASHBOARD_TOKEN must be at least 32 characters). Replace the
    Secret behind api.telemetry.receiver.dashboard.tokenSecretRef with a
    value generated from 32 random bytes, for example
    openssl rand -base64 32, before upgrading. Replacing the token
    invalidates browser sessions and loses no data. Receivers with no token
    (no dashboard) are unaffected. See
    docs/telemetry-provider.md.

Added

  • Independent central panel: Docker Compose runs the API/dashboard without
    host Kubernetes; a standalone Helm profile runs them without a central game
    operator or workloads. SQL stores registrations, settings, and encrypted
    credentials; explicitly selected remote clusters host games through their own
    operators and agents.
  • Credential maintenance: standalone keys have separate generated storage
    or read-only provisioned custody, the offline transactional rotate-panel-key
    command, and PUT /clusters/{name}/kubeconfig preserving registration identity
    and grants. PUT/DELETE /clusters/{name}/gateway manage remote gateway
    credentials; api.remoteAllowedCIDRs restricts workload API and gateway
    destinations. See standalone setup.
  • Extended telemetry tier: a second, separately consented tier with a
    random install ID, environment, game-usage and feature-adoption
    categories. Every value comes from a fixed set, a band or a number; no
    names, namespaces, hostnames, addresses, player counts or custom module
    names. Reports are signed (Ed25519, key derived from a per-install secret
    and the install ID); a receiver binds each ID to its first key and refuses
    forged, stale and replayed reports. See
    docs/security.md.
  • Telemetry receiver dashboard and public summary: a private,
    token-protected dashboard (DASHBOARD_LISTEN_ADDR, default :8081) with
    7-, 30-, 90- and 365-day views, durable daily aggregates
    (RETENTION_DAYS, default 730, minimum 365), per-install activity
    records that expire (ACTIVITY_EXPIRY_DAYS, default 90, minimum 31),
    a per-source daily ingest limit (INGEST_SOURCE_DAILY_LIMIT, default
    20), TRUSTED_PROXY_CIDRS, an ID_PEPPER secret and an optional public
    GET /v1/summary (PUBLIC_SUMMARY=true). Helm values:
    api.telemetry.enabled, api.telemetry.interval,
    api.telemetry.receiver.{persistence,dashboard,publicSummary,pepperSecretRef,retentionDays,activityExpiryDays,ingestSourceDailyLimit,trustedProxyCIDRs}.
  • Proof-of-work on telemetry ingestion (off by default): a receiver with
    INGEST_POW=true serves GET /v1/challenge and requires a solved
    challenge on /ingest (428 pow_required / pow_invalid, 503 pow_busy
    when its used-challenge set is full). The difficulty is zero up to
    INGEST_POW_TARGET_PER_MIN challenges a minute (default 60), then rises
    with the rate between INGEST_POW_MIN_BITS (default 0) and
    INGEST_POW_MAX_BITS (default 22, at most 26), and decays one bit
    every five minutes. New metrics: gameplane_telemetry_pow_challenges_total
    and gameplane_telemetry_pow_bits; gameplane_telemetry_refused_total
    gains the reasons pow_required, pow_invalid and pow_busy, and
    gameplane_telemetry_rate_limited_total the route challenge. Helm values:
    api.telemetry.receiver.ingestPow.{enabled,targetPerMinute,minBits,maxBits}.
    The API reporter solves a challenge when its destination offers one and
    sends without it otherwise. See
    docs/telemetry-provider.md.
  • Provider runbook: docs/telemetry-provider.md
    covers deploying the receiver, TLS, keeping :8081 private, secrets and
    rotation, retention, scraping /metrics and backing up telemetry.db.
  • Optional Steam display names on player lists: with
    api.steam.apiKeySecretRef.name set to a Secret holding a Steam Web API key
    (key name api-key by default), the API looks up public display names for
    Steam IDs in player lists through ISteamUser/GetPlayerSummaries/v2, with an
    in-memory cache tuned by api.steam.cache.{maxEntries,ttl,negativeTTL,timeout}
    (defaults 10000 entries, 12h, 15m, 2s). Without a key, or when Steam is
    unreachable, player lists show raw Steam IDs as before. The key is env-only,
    never logged and never sent to the browser. See
    docs/install.md and
    docs/security.md.
  • Nuclear Option player lists are structured: the agent's player output
    for JSON player lists now includes an entry per player (steamId and
    faction), which the API uses for display-name lookup.
  • Language selection and translations (spec 019): the dashboard can be
    shown in another language. A user's language choice is saved to the account (or
    to the browser when signed out), and an admin sets the instance default. Languages are
    shared packs under i18n/locales/; a language is listed once its pack is at
    least 80% translated, and anything missing falls back to English. French is
    the first pack and is listed once it reaches 80%.
    Dates, numbers and relative times follow the active language. A pseudo
    language (en-XA) exists for maintainers behind VITE_I18N_PSEUDO=1 and is
    never listed in a release. See docs/translating.md.
  • Translated server messages (opt-in problem+json): API errors are sent
    as application/problem+json with a stable messageId and arguments only
    when the request lists that type in Accept; every other client keeps the
    unchanged English plain-text body. The dashboard opts in and shows the
    message in the active language. Game server condition messages carry
    status.conditionArgs so the dashboard can translate them too; the English
    message is unchanged.
  • Notification language per destination: each notification destination
    has an optional language; notifications and test sends to Discord, email
    and webhooks are rendered in it, and destinations with none use the
    instance default. Webhook payloads gain additive language, title and
    text fields; existing fields are unchanged.
  • Module translations: a module can ship locales/<tag>.json next to
    module.yaml (OCI layer type application/vnd.gameplane.module.locale.v1+json
    and the same files in every other source). The operator validates them on
    install, stores the valid entries in a <template>-locales ConfigMap and
    reports problems as the LocalesValid condition; install never fails
    because of translations. Each file is read up to 900 KiB and all of a
    bundle's files together up to 4 MiB. gp-module i18n extract|stamp and
    gp-module validate support authoring. See
    docs/module-authoring.md.
  • Fleet-wide node inventory: clusters are organized on their own pages and
    a Nodes page lists the nodes of every cluster you can see, with the same
    search and filters as Backups (PR #859).
  • Game performance and relay monitoring: bounded per-game performance and
    relay metrics (PR #838).
  • Stopped multi-volume Restic recovery: a stopped server with several data
    volumes can be recovered from its Restic backups (PR #861).
  • Wake on connect from server settings: users can turn wake on connect on
    from the server's Lifecycle settings (PR #818).
  • Reserve less than the limits: a server can request less CPU and memory
    than its limits (Burstable QoS) from Resources and Create Server (PR #815).

Changed

  • No RCON Secret for unauthenticated consoles: a new optional
    GameTemplate field spec.rcon.authentication (default true) lets a
    template declare a console with no password, such as Nuclear Option's
    loopback-only remote-command port. When false, the operator mints no
    per-server <gameserver>-rcon Secret, mounts nothing for the
    agent and injects no password environment variable.

Removed

  • Hell Let Loose module: hell-let-loose is withdrawn from the
    official catalog. Its dedicated-server files are distributed only to
    Team17's licensed hosting partners, so no public image can install it.
    Its hand-run e2e test and probe package are removed with it.

Security

  • Pre-release hardening (PR #862): OIDC discovery, key fetches and code
    exchange, and operator OCI registry dials go through the dial guard; agent
    and capture proxies can no longer override the API's security headers; a
    login-device cookie stops failed logins elsewhere from locking the owner
    out; the API pod no longer mounts the agent CA key; a ValidatingAdmissionPolicy
    (Kubernetes 1.30+) limits waker service accounts to the wake annotation;
    template env Secret and ConfigMap references follow the server ownership
    rule; the agent serves regular files only and confines mod installs to the
    mods root; the dashboard ships Monaco in the image and sends a
    Content-Security-Policy and security headers; share tokens stay out of
    access and error logs.
  • Snapshot restores authorize their data source against the live Backup
    owner and reject unfinished or aborted snapshots (PR #756).
  • Operator Secret access is scoped to the configured namespaces (PR #757).
  • Image CVE fixes: eMudhra CA distrust and Alpine/Debian updates in game
    images, the Tailscale tunnel built with fixed Go modules, and updated expat,
    musl, zlib, brotli and curl in the dashboard image (PRs #775, #845,
    #816).
  • Standalone credential protection: encryption keys are kept outside
    database volumes, with durable publication, fail-closed file-access checks,
    read-only provisioned custody, and authenticated transactional rotation.
    Database and key backups need separate access policies; recovery requires
    the matching key. See storage and recovery.
  • Remote workload boundaries: standalone connections require verified TLS,
    reject unsafe destinations at dial time, and bound finite responses, watch
    frames, and retained observations. Private networks remain reachable unless
    destination CIDRs restrict them; configure GAMEPLANE_REMOTE_ALLOWED_CIDRS
    or api.remoteAllowedCIDRs for the workload APIs and gateways. Module and
    template authorization follows the selected cluster in every deployment mode.
    See security.