Files
browser/platform
npc0-hue 6488f6b31d Keep SCUM poll jobs and drop orphan job log streams
The metadata snapshot deleted every SCUM sqlite query job while persisting, so
the recurring database poll lost its job records as soon as anything wrote the
snapshot and left its stdout/stderr streams behind. Those polls are the only
producer for the platform scum_user and scum_vehicle tables, and the service
already retires older terminal polls with their streams, so the snapshot no
longer drops them.

Startup now removes job log streams whose job no longer exists, keeping the
autonomous lifecycle streams the Run posts without a platform job. That clears
the streams left by the removed poll producers instead of carrying them in
every snapshot.
2026-09-16 18:41:44 +08:00
..
2026-07-11 14:56:10 +08:00
2026-09-14 13:41:44 +08:00
2026-07-11 14:56:10 +08:00
2026-07-11 14:56:10 +08:00
2026-07-11 14:56:10 +08:00
2026-07-11 14:56:10 +08:00
2026-07-11 14:56:10 +08:00
2026-07-11 14:56:10 +08:00
2026-09-07 20:26:20 +08:00

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, default file; use memory only for tests or disposable local runs.
  • PLATFORM_MYSQL_DSN: MySQL DSN used when PLATFORM_STORAGE_BACKEND=mysql, for example platform: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 uses file; supported values are file and memory.
  • 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, default docker.
  • PLATFORM_BUILDER_IMAGE: prebuilt, explicitly versioned or digest-pinned builder image, default browser-platform-distribution-builder:1.0.0; floating tags such as latest are 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, fetches PLATFORM_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 to main.
  • 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, default 1800.
  • PLATFORM_RUN_RELEASE_URL: public platform URL embedded into generated Run packages, default https://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.