gameplane / docs
AUTHOR MODULES

Module Authoring

Package game server templates as signed, versioned OCI artifacts with metadata, config schema, and capabilities.

Modules & Sourcesv0.212 MIN

A module is a reusable, versioned game-server blueprint distributed as an OCI artifact. Gameplane hosts modules in registries (official or self-hosted) and lets operators install them into the cluster as GameTemplate resources, so game servers on the platform inherit their console protocol, configuration schema, and operational capabilities — backup quiesce, player moderation, and lifecycle management — without any platform-code changes.

Module FormatAn OCI artifact with typed layers carrying template.yaml, module.yaml, README, and icon.
Source LayoutDirectory structure on disk with YAML metadata and optional documentation.
Distribution & InstallationRegister modules via ModuleSource, install them into the cluster as GameTemplate resources.
Supply Chain SecurityCosign signing and digest pinning protect against tampering and tag drift.

Source layout

A module lives on disk as a directory holding four files:

File Required Purpose
template.yaml yes GameTemplate spec (Kubernetes resource)
module.yaml yes Module metadata (name, version, display info)
README.md no Operator documentation, rendered in the catalog
icon.png no 256×256 PNG for the dashboard catalog tile

The official modules live in the standalone GameplanePanel/module repository, checked out as the modules/ submodule of the main Gameplane repo.

Module metadata (module.yaml)

Every module declares its identity and display properties in module.yaml:

apiVersion: gameplane.local/module/v1
name: minecraft-java                         # required, DNS-1123 label
displayName: Minecraft (Java Edition)        # required
version: 1.0.0                               # required, semver, must match OCI tag
game: minecraft-java                         # required, free-form game family
categories: [Sandbox, Survival, Modded]      # optional, catalog grouping
summary: Paper / Forge / Fabric / Vanilla    # required, one-line description
homepage: https://minecraft.net              # optional
license: MIT                                 # optional, SPDX identifier
gameplaneMinVersion: 0.2.0                   # optional, operator version gate
icon: icon.png                               # optional, filename in the bundle

Key rules:

  • name is the canonical module identifier within a source. Two modules with the same name in the same source are rejected.
  • version must exactly match the OCI tag the bundle is pushed under — drift is an error.
  • game is a free-form identifier grouping templates with the same upstream game, used for versioning schemes (e.g., minecraft-java, minecraft-bedrock).
  • categories appears as filter chips in the dashboard Modules catalog. Use official values (Sandbox, Survival, Shooter, etc.) or define your own — the dashboard auto-discovers new chips.
  • gameplaneMinVersion gates installation on operator version; a module that requires v0.3.0+ will refuse install on v0.2.0.

Bundling and distribution

Modules are distributed as OCI artifacts — the same registries that hold your game-server images also hold module metadata. A bundle is a single artifact with typed layers.

Layer Media type Notes
module.yaml application/vnd.gameplane.module.metadata.v1+yaml Metadata, required
template.yaml application/vnd.gameplane.module.template.v1+yaml Template spec, required
README.md application/vnd.gameplane.module.readme.v1+md Operator docs, optional
icon.png image/png Dashboard tile icon, optional

The artifact manifest is tagged as:

mediaType: application/vnd.oci.image.manifest.v1+json
artifactType: application/vnd.gameplane.module.v1+json

Each layer carries an org.opencontainers.image.title annotation with its filename so the puller can identify layers by name.

Pushing a bundle

Install oras (>= 1.2.0):

brew install oras                           # macOS
# or download from https://oras.land/docs/installation

Then push a bundle from a module directory:

PUSHING A BUNDLE
oras push \
  --artifact-type application/vnd.gameplane.module.v1+json \
  ghcr.io/gameplanepanel/gameplane-modules/minecraft-java:1.0.0 \
  module.yaml:application/vnd.gameplane.module.metadata.v1+yaml \
  template.yaml:application/vnd.gameplane.module.template.v1+yaml \
  README.md:application/vnd.gameplane.module.readme.v1+md \
  icon.png:image/png

Or use the Makefile target:

make modules-push REGISTRY=ghcr.io/gameplanepanel/gameplane-modules

For private registries, log in once with oras login <registry>. The cluster uses a kubernetes.io/dockerconfigjson secret (ModuleSource.spec.oci.pullSecretRef) — the same credential format kubelet uses for private images.

Bundle format

A module bundle is a single OCI artifact with named layers, tagged application/vnd.gameplane.module.v1+json.

Module sources

Once a module is pushed to a registry, operators register it by creating a ModuleSource resource that tells Gameplane where to fetch modules:

apiVersion: gameplane.local/v1alpha1
kind: ModuleSource
metadata: { name: community }
spec:
  type: oci                              # oci | git | http | local | upload
  oci:
    url: ghcr.io/gameplanepanel/gameplane-modules
    modules: [{ name: minecraft-java }]  # explicit list
  verify:                                # optional, supply-chain security
    key: { name: cosign-pub }            # signed with cosign ECDSA key
  refreshInterval: 30m

ModuleSource types:

Type Location Versioning Use case
oci OCI registry Semantic version tags Official bundles, curated registries
git GitHub/GitLab repo One stream: version field in module.yaml Community contributions, gitops-style
http HTTP tar.gz/zip URL One stream: content hash Binary archives, air-gapped sources
local Pod mount path One stream: content hash On-cluster module directory
upload ConfigMaps in operator ns One stream: content hash Web dashboard uploads, manual entries

Supply-chain security

Two mechanisms protect modules from tampering:

Digest pinning — Module.spec.digest pins the bundle to an exact OCI manifest digest:

spec:
  name: minecraft-java
  version: 1.0.0
  digest: sha256:abc123…  # install fails if the manifest changes

Signature verification (OCI sources only) — ModuleSource.spec.verify requires every bundle to carry a valid cosign signature:

verify:
  key: { name: cosign-pub }              # offline-verified ECDSA signature
  requireTransparencyLog: false           # require Sigstore Rekor entry (optional)

The official Gameplane modules are signed with an ECDSA P-256 key and recorded in the public Sigstore Rekor transparency log. Keyed verification is offline — no network required — so air-gapped clusters can verify signed bundles without sigstore connectivity.

Security best practice

Always pin digests and enable signature verification for production-critical modules. This prevents a compromised registry tag or mirror from pushing unexpected code into your cluster.

Installation and template materialization

Modules appear in the Modules page of the dashboard once a source is configured. Click Install to create a Module resource:

apiVersion: gameplane.local/v1alpha1
kind: Module
metadata: { name: minecraft-java }
spec:
  source: { name: default }  # ModuleSource name
  name: minecraft-java       # module name within the source
  version: 1.0.0             # omit to track the source's latest
  digest: sha256:abc123…     # optional, enforce exact bundle

The operator pulls the artifact, parses module.yaml + template.yaml, and creates a GameTemplate owned by this Module. The template then appears in the “Create server” wizard. Deleting the Module deletes the GameTemplate, but the operator refuses deletion while servers still reference it.

If a module fails to upgrade (pull error, bad signature, incompatible operator), the previous template keeps running unchanged; the Module.status.previousVersion field records the last-known-good version for safe rollback.

Next steps