Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.grounds.gg/llms.txt

Use this file to discover all available pages before exploring further.

Use this when adding a new component to a bundle, debugging an unexpected resolved value, or building tooling that consumes the bundle. A bundle is a versioned, curated list of platform components and the default versions of each. Engineers pin against a bundle release for reproducible runs (CI), or against the moving edge for daily iteration. Forge resolves a bundle plus an optional override file into a concrete set of Helm releases, then applies them inside your per-engineer vCluster.

Top-level shape

apiVersion: platform.grnds.io/v1
kind: PlatformBundle
metadata:
  version: 0.4.0
  description: |
    Free-form description...
shared:
  nats: { ... }
  postgres: { ... }
  keycloak: { ... }
components:
  velocity: { ... }
  plugin-social: { ... }
  ...
FieldTypeRequiredNotes
apiVersionstringyesMust equal platform.grnds.io/v1. Forge rejects other values.
kindstringyesMust equal PlatformBundle.
metadata.versionsemveryesSource-of-truth for the bundle’s own version. release-please bumps this in lockstep with the git tag.
metadata.descriptionstringnoHuman-readable.
sharedobjectyesTailnet-exposed backends shared across all engineer vClusters.
componentsmapyesMap of release-name → BundleComponent.

shared

Backends that don’t run inside an engineer’s vCluster. Each in-vCluster service connects out via Tailscale to these endpoints.
The endpoints declared here are only reachable from the internal Grounds tailnet — a private Tailscale network for authorized engineers. External contributors won’t be able to resolve nats.dev / postgres.dev / keycloak.dev. The bundle composition is still publicly readable; only the runtime is gated.
shared:
  nats:
    tailnet: nats.dev
    port: 4222
    description: NATS JetStream — cross-service events and the KV store for player presence
  postgres:
    tailnet: postgres.dev
    port: 5432
    description: One database per engineer (`<handle>_<service>`); shared server
  keycloak:
    tailnet: keycloak.dev
    port: 443
    realm: grounds-test
FieldTypeRequiredNotes
<endpoint>.tailnetstringyesMagicDNS hostname on the internal Grounds tailnet. The engineer must be on the tailnet and have the matching dev-routes ACL enabled.
<endpoint>.portintyesTCP port at the endpoint.
<endpoint>.descriptionstringnoHuman-readable.
keycloak.realmstringyes for keycloakRealm name. Currently grounds-test.
The shared block is informational — forge doesn’t push these values into helm values automatically. The component charts are responsible for connecting using the same names (e.g. the in-vCluster service-config chart has NATS_URL=nats://nats.dev:4222 baked into its env).

components

A map: each key is the Helm release name (also used as namespace-DNS-label inside the vCluster, so it must be a valid DNS-1123 label — lowercase, hyphens, no underscores). Each value is a BundleComponent:
components:
  plugin-social:
    type: plugin-velocity
    chart:
      url: oci://ghcr.io/groundsgg/charts/plugin-velocity-jar
      version: 0.1.0
    image: ghcr.io/groundsgg/plugin-social
    version: edge
    optional: false
    helm:
      resources:
        requests: { cpu: 50m, memory: 64Mi }
    devspace:
      workflow: jar-sync-pod-restart

BundleComponent fields

FieldTypeRequiredNotes
typestringyesComponent-type string. See below.
chart.urlstringyesOCI Helm chart URL.
chart.versionsemveryesPinned Helm chart version.
imagestringyesContainer image without tag. Forge passes this as image.repository into helm values.
versionstringyesImage tag — semver, edge, or any other ref. Passed as image.tag.
optionalboolno, default falseWhen true, the component is not deployed by default. Override with enabled: true to bring it in.
helmmapnoFree-form Helm values. See merging rules below.
devspace.workflowstringnoSelects a DevSpace template when the component is overridden into gradle-local mode.

Component types

The type field is a string for forward-compat, but the platform-test environment understands these values:
TypeWhat it isDevSpace workflowIter-Latency
plugin-velocity-baseThe Velocity proxy itselfjar-sync-pod-restart~10–20 s
plugin-velocityA JAR plugin loaded by Velocity at startupjar-sync-pod-restart~10–20 s
plugin-paper (planned)A JAR plugin loaded by Paperjar-sync-plugin-reload~5–15 s
gamemodeMinestom or Paper game-serverjar-sync-pod-restart~10–20 s
grpc-serviceQuarkus-based gRPC backendquarkus-dev~1–3 s
utilityTest/debug tooling (e.g. nats-recorder)
Forge uses type only for default-behavior decisions (today: which DevSpace workflow template to suggest). Adding a new type is a bundle PR, not a forge PR.

Helm value merging

The values forge passes to helm install for a component are produced by:
1

Start with the component's helm: block

Or {} if absent.
2

Inject image.repository + image.tag

Deep-merge { image: { repository: <component.image>, tag: <resolved-tag> } } on top.
3

Inject DevSpace pickup annotations (when gradle-local)

Deep-merge a podAnnotations block:
podAnnotations:
  grounds.gg/devspace-mode: gradle-local
  grounds.gg/devspace-project: ./plugin-social
  grounds.gg/devspace-artifact: build/libs/*-all.jar
  grounds.gg/devspace-reload: pod-restart
  grounds.gg/devspace-quarkus-dev: "true"   # only when devspace:true
Deep-merge rules:
  • Plain-object values are merged recursively.
  • Arrays are replaced wholesale (no list-merge).
  • Primitives are replaced.
helm.image.tag = "0.5.0" in the bundle is not a useful value — forge will overwrite it with the resolved tag. Pin via the version: field instead.

Override-File schema

Engineers write an override YAML and pass it to grounds cluster up --bundle X --override ./me.yaml. Schema:
bundle: 0.4.0    # bundle ref to apply against. --bundle on cmd-line wins.

overrides:
  <component-name>: <ComponentOverride>
<ComponentOverride> is a discriminated union — exactly one of these shapes:
Use the bundle’s image but optionally override the tag.
overrides:
  velocity:
    mode: image
    tag: dev-fix-routing-123    # optional
FieldTypeRequiredNotes
mode"image"yes
tagstringnoReplaces the bundle’s version: for this component. Omit to keep the default.

Pod annotations

The fields project, artifact, reload, and devspace aren’t applied by forge — they’re echoed as Pod annotations for DevSpace to read on the engineer’s laptop. The annotations forge writes:
AnnotationValue
grounds.gg/devspace-modegradle-local
grounds.gg/devspace-projectthe project value
grounds.gg/devspace-artifactthe artifact value
grounds.gg/devspace-reloadthe reload value (only when set)
grounds.gg/devspace-quarkus-dev"true" when devspace: true

Resolution algorithm

When forge gets (bundle, override-file):
1

Filter components

Drop everything where override.enabled === false. Drop optional: true components that aren’t explicitly enabled.
2

Resolve per remaining component

  • If override is mode: image with tag: replace imageTag with the override.
  • If override is mode: gradle-local: keep image+tag from bundle; mark mode for annotation injection later.
  • If no override: use bundle defaults.
3

Compute helm values

Deep-merge per the rules above.
4

Helm install

Best-effort, parallel-friendly. If one component’s chart is missing, the others still go through. Failed components are reported in the response body but don’t fail the call.

Adding a new component

1

Ensure the chart exists

Make sure the component-type’s Helm chart is in groundsgg/charts and published as an OCI artifact (or co-publish it in the same PR).
2

Add a components: entry to bundle.yaml

plugin-foo:
  type: plugin-velocity            # or whichever type
  chart:
    url: oci://ghcr.io/groundsgg/charts/plugin-velocity-jar
    version: 0.1.0
  image: ghcr.io/groundsgg/plugin-foo
  version: edge
  devspace:
    workflow: jar-sync-pod-restart
3

Mark optional if it shouldn't run by default

Add optional: true and document the opt-in in examples/.
4

Open the PR

release-please bumps the bundle version on merge.

Versioning

release-please drives the bundle version:
Commit typeBump
feat:minor
fix:patch
feat!: / BREAKING CHANGE:major
Engineers pin against bundle versions:
bundle: 0.4.0     # reproducible
bundle: main always tracks the latest commit on main — useful for daily iteration, not for CI.

See also