Each dispatched poll is a durable job with two job log streams, so an unbounded poll history would grow the platform store by roughly two thousand jobs a day per server. A new poll now retires older terminal jobs of the same template together with their job log streams, leaving at most two rows per template while nothing is in flight.
platform
Backend control plane for the game server management platform.
Responsibilities
- Users, roles, permissions, and sessions.
- Game management plugin installation metadata and marketplace views.
- Server instance records and lifecycle orchestration.
- AI provider configuration and platform-mediated AI invocation.
- Run registration, capabilities, jobs, artifacts, log stream metadata, and storage adapters.
Required Directory Plan
Implementation should use dedicated directories for:
api/: route wiring and HTTP/gRPC adapters.dto/: request and response structures.domain/: business types and aggregates.model/: database models only.repo/: repository interfaces and persistence implementations.service/: use cases and orchestration.protocol/: run, plugin, artifact, log, and AI contracts.validator/: validation rules.config/: configuration structures and loading.shared/: small shared helpers.
Do not put DTOs, database models, or protocol structs inside handlers or service functions.
Development Baseline
Tooling:
- Go 1.25.1.
- Module:
browser.local/platform.
Commands:
go test ./...
go run ./cmd/platform
Runtime configuration:
PLATFORM_ADDR: local listen address, default:8080.PLATFORM_STORAGE_BACKEND: storage backend, defaultfile; usememoryonly for tests or disposable local runs.PLATFORM_MYSQL_DSN: MySQL DSN used whenPLATFORM_STORAGE_BACKEND=mysql, for exampleplatform:platform@tcp(127.0.0.1:3306)/platform?parseTime=true.PLATFORM_DATA_DIR: default platform data directory, default.platform-data.PLATFORM_METADATA_PATH: file-backed metadata snapshot path, default.platform-data/metadata.json; high-frequency Run endpoint/session state uses a sibling runtime snapshot to avoid rewriting the full metadata file on every heartbeat.PLATFORM_LOG_BODY_BACKEND: log body backend, default follows metadata backend except MySQL usesfile; supported values arefileandmemory.PLATFORM_LOG_DIR: segmented log body directory, default.platform-data/logs.PLATFORM_ARTIFACT_DIR: private durable artifact body/transfer directory, default.platform-data/artifacts.PLATFORM_BOOTSTRAP_ADMIN_EMAIL: optional initial platform administrator email.PLATFORM_BOOTSTRAP_ADMIN_PASSWORD: optional one-time bootstrap password; the platform applies no default and persists only a password verifier.PLATFORM_SECRET_ENVELOPE_KEY: external secret used to derive the AES-GCM component-key envelope key; use at least 32 random characters and keep it stable across restarts.PLATFORM_BUILDER_DOCKER_BINARY: Docker-compatible CLI used by the platform builder, defaultdocker.PLATFORM_BUILDER_IMAGE: prebuilt, explicitly versioned or digest-pinned builder image, defaultbrowser-platform-distribution-builder:1.0.0; floating tags such aslatestare rejected.PLATFORM_BUILDER_SOURCE_DIR: persistent Run checkout location used by the platform builder. The checkout may be absent at startup; each Run build creates it when needed, fetchesPLATFORM_BUILDER_SOURCE_REVISION, exports that revision into the isolated job workspace, injects package configuration, and then compiles.PLATFORM_BUILDER_SOURCE_REPOSITORY: Run Git repository used when the checkout is absent or needs a build-time refresh.PLATFORM_BUILDER_SOURCE_REVISION: branch, tag, or commit fetched for each Run build; defaults tomain.PLATFORM_BUILDER_WORKSPACE_DIR: private per-plugin/per-job build workspace, default<PLATFORM_DATA_DIR>/distribution-builds.PLATFORM_BUILDER_CACHE_DIR: persistent Go build/module cache, default<PLATFORM_DATA_DIR>/distribution-build-cache; it contains no job inputs or component keys.PLATFORM_BUILDER_TIMEOUT_SECONDS: positive build deadline, default1800.PLATFORM_RUN_RELEASE_URL: public platform URL embedded into generated Run packages, defaulthttps://scum.npc0.com/. It must be reachable from the target server; loopback and unspecified addresses are rejected.
Build the dedicated toolchain image before enabling distribution generation:
docker build --pull -t browser-platform-distribution-builder:1.0.0 distribution-builder
export PLATFORM_BUILDER_SOURCE_DIR=/absolute/path/to/run-checkout
export PLATFORM_BUILDER_SOURCE_REPOSITORY=git@git.npc0.com:admin343/run.git
export PLATFORM_BUILDER_SOURCE_REVISION=main
export PLATFORM_BUILDER_IMAGE=browser-platform-distribution-builder:1.0.0
go run ./cmd/platform
Each build runs in a separate read-only container. For Run, the platform first exports the fetched revision into a clean per-job source directory; source and per-job input are mounted read-only, only the job build/output directories are writable, and the component auth key is passed through a mode-0600 input file. The key is not sent through the machine-side job channel or Docker arguments. Production deployments may use an internal-registry image@sha256:... reference; the selected image must already exist in the Docker daemon because builds run with --pull never.
MySQL configuration example:
export PLATFORM_STORAGE_BACKEND=mysql
export PLATFORM_MYSQL_DSN='platform:platform@tcp(127.0.0.1:3306)/platform?parseTime=true'
export PLATFORM_LOG_BODY_BACKEND=file
export PLATFORM_LOG_DIR=.platform-data/logs
go run ./cmd/platform
MySQL is the platform metadata database here. It stores the platform metadata snapshot table and should later hold normalized users/plugins/servers/jobs/log stream indexes. It is not the high-volume log body store; keep log bodies in segmented files locally, or add a future ClickHouse/Loki/OpenSearch/object-storage LogBodyStore adapter for production scale.
For local direct debugging, copy platform/.env.example to platform/.env, edit the values, and run:
go run ./cmd/platform
The platform process automatically reads root .env and platform/.env before loading configuration. Explicitly exported process environment values still take precedence over values in those files.
For Docker, the root docker-compose.yml sets platform data under /data/platform and mounts it through the platform-data named volume.
Current executable behavior includes the platform API, durable hashed auth/Run sessions with expiry/revocation/rotation, strict production route authorization, durable file-backed metadata, segmented log bodies, authenticated run control/job/log/artifact routes, plugin bridge dispatch, platform-mediated AI invocation, real typed dependency execution orchestration with reviewed plan digests, and target-fenced transactional Run self-update staging/health/rollback projections.
Validated plugin runtime profiles and per-server runtime bindings are part of durable metadata for advanced logical transports. Server creation requires only the plugin type and server name, and plugin-declared deployment/lifecycle actions must be enough for user-facing start/stop and generated Run package flows without forcing operators through a manual runtime-profile binding screen. Browser and plugin-facing responses expose readiness only, not binding values. Platform-owned Docker builds need no registered Run endpoint with distribution.build; component keys remain in platform-held per-job input. This change uses scoped secret references and an injectable AES-GCM component-key envelope. The built-in envelope key is a disposable-development compatibility fallback; deployments must set PLATFORM_SECRET_ENVELOPE_KEY. This is not a production vault/KMS or machine-side runtime resolver. Durable scheduling, process supervision, durable log/artifact bodies, bounded metrics/backups, declaration-backed remote adapter envelopes, typed dependency installation, plugin lifecycle dispatch, and transactional Run self-update are implemented. Production signing/fleet rollout, external provider/storage adapters, and real AI-provider integration remain separate tasks.