Skip to content

Pepitodrop/GamePage

Repository files navigation

COBOL Game Mainframe

A production-oriented launcher and single-domain gateway for:

The intended public URL is https://game.luisbenedikt.de.

COBOL is the application authority

GamePage uses COBOL as its application, registry, and policy layer. Go is intentionally restricted to networking and operating-system responsibilities: HTTP transport, reverse proxying, WebSocket forwarding, concurrent probes, bounded response rewriting, logging, and graceful shutdown.

The system does not silently duplicate COBOL decisions in Go. If the COBOL core cannot execute or returns structurally invalid data, GamePage fails closed: readiness becomes false and all launches are blocked.

One canonical COBOL registry

All game metadata now lives in one copybook:

src/game_registry.cpy

It is the source of truth for:

  • game keys and names;
  • launcher card styling and content;
  • public route prefixes;
  • upstream environment-variable names and defaults;
  • health-check paths;
  • policy override environment-variable names;
  • default enabled and required flags;
  • default maximum latency;
  • source-repository links.

Three compiled COBOL programs consume the same copybook:

Program Responsibility
src/gamepage.cob Generates launcher HTML, CSS, 404 page, favicon, robots.txt, and security.txt; game cards are generated by iterating the registry
src/gamepage_registry.cob Generates the runtime route registry, global policy file, architecture manifest, and browser status client
src/gamepage_core.cob Validates observations and raw overrides, applies policy, decides readiness and launchability, and emits status JSON

Adding or changing a game therefore starts with one COBOL registry entry instead of coordinated edits across UI, routing, and policy files.

Generated runtime artifacts:

/app/site/runtime/routes.tsv
/app/site/runtime/policy.tsv
/app/site/runtime/architecture.json

Responsibility boundary

Layer Language Responsibilities
Canonical registry COBOL copybook Game identity, routes, UI content, policy defaults, override names
Application policy COBOL Configuration validation, maintenance, enabled/required rules, latency thresholds, optional degradation, minimum availability, readiness, launchability, reason codes, status JSON
Transport adapter Go HTTP server, reverse proxy, WebSockets, upstream probes, headers/cookies/path rewriting, security headers, logs, process lifecycle
Generated browser artifacts HTML, CSS, JavaScript, SVG Launcher presentation and live policy-state display
Deployment Docker, Compose, Bash, Apache or Caddy Build, isolation, CI, smoke tests, TLS termination

COBOL policy model

Global controls

MAINTENANCE_MODE=false
MINIMUM_LAUNCHABLE_GAMES=1
  • Maintenance mode blocks all launches and makes /readyz return HTTP 503.
  • MINIMUM_LAUNCHABLE_GAMES sets the minimum number of launchable games required for readiness.
  • COBOL validates both values. Invalid values produce configuration-error and no game may launch.

Per-game controls

Each game has three policy controls:

TRUMP_ENABLED=true
TRUMP_REQUIRED=true
TRUMP_MAX_LATENCY_MS=2500

GOLF_ENABLED=true
GOLF_REQUIRED=true
GOLF_MAX_LATENCY_MS=2500

RACE_ENABLED=true
RACE_REQUIRED=true
RACE_MAX_LATENCY_MS=2500

The defaults preserve the previous behavior: all three games are enabled and required.

Enabled

  • true: COBOL evaluates health and latency and may permit launch.
  • false: the game receives policyState: disabled and cannot launch.

Required

  • A required game that is down, disabled, or slower than its threshold makes readiness false.
  • An optional game may be down, disabled, or slow while readiness remains true, provided the global minimum launchable count is met.
  • Optional failures still set overall: degraded, so reduced functionality remains visible.

Maximum latency

  • A response above the threshold receives policyState: slow.
  • Slow games remain launchable.
  • A required slow game makes readiness false.
  • An optional slow game keeps readiness true while the overall state is degraded.

Decision table

Condition Launchable Required readiness impact Optional readiness impact
Healthy and enabled Yes None None
Slow and enabled Yes Not ready Still ready
Down and enabled No Not ready Still ready if minimum is met
Disabled No Not ready Still ready if minimum is met
Maintenance No Not ready Not ready
Invalid policy No Not ready Not ready
COBOL unavailable or invalid result No Fail closed Fail closed

Runtime decision flow

Browser / monitoring
        |
        v
Go HTTP transport
        |
        | concurrent technical HTTP probes
        v
raw observations + raw environment overrides
        |
        | mode 0600 temporary input
        v
compiled GnuCOBOL gamepage-core
        |
        | canonical registry defaults + policy evaluation
        v
COBOL-generated status JSON
        |
        | structural validation only
        v
/api/status, /readyz, and route launch gates

Go does not interpret boolean policy values or latency overrides. It transports them to COBOL exactly as configured.

Status endpoints

URL Purpose
/healthz Go transport-process liveness; does not claim application readiness
/readyz Forces fresh probes and a COBOL decision; returns 200 only when COBOL reports ready: true
/api/status Cached COBOL decision document used by the launcher and monitoring

Example:

{
  "overall": "degraded",
  "ready": true,
  "mode": "degraded",
  "decisionEngine": "gnucobol",
  "checkedAt": "2026-07-14T10:00:00Z",
  "policy": {
    "minimumLaunchableGames": 1,
    "requiredFailures": 0
  },
  "summary": {
    "total": 3,
    "up": 2,
    "down": 1,
    "enabled": 3,
    "required": 2,
    "launchable": 2,
    "healthy": 2,
    "slow": 0,
    "disabled": 0
  },
  "games": {
    "golf": {
      "name": "Crazy Mini Golf",
      "path": "/play/golf/",
      "status": "down",
      "policyState": "down",
      "httpStatus": 503,
      "latencyMs": 8,
      "error": "http-error",
      "enabled": true,
      "required": false,
      "maxLatencyMs": 2500,
      "launchable": false,
      "reason": "http-error"
    }
  }
}

Public URL map

Public URL Service Internal target
/ COBOL registry-generated launcher GamePage transport
/play/trump/ Trump vs. Shakespeare http://trump-vs-shakespeare:8000/
/play/golf/ Crazy Mini Golf http://crazy-mini-golf:8080/
/play/race/ Crazy Race http://crazy-race:8080/
/api/status COBOL application status GamePage
/healthz Go transport liveness GamePage
/readyz COBOL application readiness GamePage

Only GamePage publishes a host port. The game containers are reachable only on the internal Docker network.

Reverse-proxy behavior

The games were originally designed to run at the root of an origin. Go performs protocol-level work that does not belong in the COBOL policy layer:

  1. strips the public /play/... prefix before forwarding;
  2. restores the prefix in root-relative textual URLs;
  3. rewrites Location response headers;
  4. rewrites cookie Path attributes;
  5. preserves WebSocket upgrades;
  6. forwards origin and prefix headers;
  7. disables upstream compression before bounded rewriting.

A text response larger than 16 MiB is rejected rather than rewritten without bounds.

Docker image

The GamePage image has three stages:

  1. COBOL builder
    • copies the canonical registry copybook;
    • compiles all three COBOL programs with the same include path;
    • generates launcher and runtime artifacts;
    • builds the runtime gamepage-core executable;
    • verifies registry-derived output.
  2. Go builder
    • runs Go tests;
    • builds the static transport binary.
  3. Runtime
    • contains the Go transport, compiled COBOL core, generated site, libcob, and CA certificates;
    • contains neither compiler nor source tree;
    • runs as non-root user 65532:65532.

The container has a read-only root filesystem and a bounded noexec /tmp tmpfs for short-lived COBOL decision files.

Repository layout

GamePage/
├── src/
│   ├── game_registry.cpy
│   ├── gamepage.cob
│   ├── gamepage_registry.cob
│   └── gamepage_core.cob
├── cmd/gateway/main.go
├── internal/gateway/
├── scripts/docker-smoke-test.sh
├── deploy/
├── .github/workflows/
│   ├── ci.yml
│   └── full-stack-docker.yml
├── Dockerfile
├── docker-compose.yml
├── .env.example
└── README.md

Required server layout

/srv/games/
├── GamePage/
├── TrumpVsShakespeare/
├── CrazyMiniGolf/
└── CrazyRaceGame/
sudo mkdir -p /srv/games
sudo chown "$USER":"$USER" /srv/games
cd /srv/games
git clone https://github.com/Pepitodrop/GamePage.git
git clone https://github.com/Pepitodrop/TrumpVsShakespeare.git
git clone https://github.com/Pepitodrop/CrazyMiniGolf.git
git clone https://github.com/Pepitodrop/CrazyRaceGame.git

Configure and start

cd /srv/games/GamePage
cp .env.example .env
chmod 600 .env
docker compose config --quiet
docker compose build --pull
docker compose up -d --wait --wait-timeout 240 --remove-orphans
bash scripts/docker-smoke-test.sh

For local HTTP testing only:

PUBLIC_ORIGIN=http://127.0.0.1:8090

Restore the production HTTPS origin before public deployment.

Useful policy configurations

Temporarily remove an optional game

GOLF_ENABLED=false
GOLF_REQUIRED=false

The launcher displays DISABLED. Other games remain available and readiness can remain true.

Keep a game visible but exclude it from readiness

GOLF_ENABLED=true
GOLF_REQUIRED=false

If Golf is down or slow, the stack is degraded but may remain ready.

Require at least two usable games

MINIMUM_LAUNCHABLE_GAMES=2

Even optional outages make readiness false when fewer than two games remain launchable.

Maintenance mode

MAINTENANCE_MODE=true

The launcher and status API stay available, all launch links are removed, and /readyz returns 503.

Recreate the GamePage container after changing environment policy:

docker compose up -d --force-recreate gamepage

Continuous integration

Core CI verifies:

  1. go test -race ./... and go vet ./...;
  2. compilation of all COBOL programs using game_registry.cpy;
  3. registry-generated launcher, route, policy, architecture, and status-client artifacts;
  4. healthy policy behavior;
  5. optional-down behavior with readiness preserved;
  6. required-down behavior with readiness rejected;
  7. slow required-game behavior while launch remains allowed;
  8. optional disabled-game behavior;
  9. maintenance mode;
  10. invalid policy input and fail-closed output;
  11. Compose validity and the production image build.

Full-stack Docker CI checks out all four repositories, builds and starts all services, runs routed smoke tests, verifies decisionEngine: gnucobol, policy states, launchability, redirects, private ports, and clean shutdown.

Security model

  • only the gateway is published, and only on 127.0.0.1;
  • games use an internal: true Docker network;
  • the gateway has fixed upstreams and cannot become an open proxy;
  • registry and environment fields are delimiter- and JSON-safe before entering COBOL;
  • COBOL validates policy values and canonical game keys;
  • Go validates COBOL identity, timestamps, route identities, state vocabulary, and summary consistency;
  • missing or malformed COBOL output fails closed;
  • temporary files use mode 0600, bounded tmpfs storage, and immediate cleanup;
  • containers use read-only filesystems, resource limits, log rotation, capability drops where supported, and no-new-privileges;
  • TLS terminates at Apache or Caddy.

Public deployment

Create a DNS A record for game pointing to the server. Add AAAA only after IPv6 routing and proxying are verified.

Forward HTTP and WebSocket traffic to 127.0.0.1:8090 and set:

RequestHeader set X-Forwarded-Proto "https"

Deployment examples are in deploy/. After TLS is active:

curl --fail https://game.luisbenedikt.de/healthz
curl --fail https://game.luisbenedikt.de/readyz
curl -sS https://game.luisbenedikt.de/api/status | python3 -m json.tool

Finally, create a Trump vs. Shakespeare online room and confirm that a second public client reaches Live.

Updating and rollback

Record all four repository SHAs before updating, then:

git pull --ff-only
git -C ../TrumpVsShakespeare pull --ff-only
git -C ../CrazyMiniGolf pull --ff-only
git -C ../CrazyRaceGame pull --ff-only
docker compose build --pull
docker compose up -d --wait --wait-timeout 240 --remove-orphans
bash scripts/docker-smoke-test.sh

To roll back, check out the recorded commits, rebuild, restart, and rerun the smoke test. Active rooms are held in memory and do not survive container replacement.

Known limits

  • The stack targets one server and hobby or small-community traffic.
  • The runtime starts one short-lived COBOL process per uncached status decision.
  • Horizontal replicas require shared room storage and coordination in the duel and race games.
  • Active rooms are not preserved across restarts.
  • Final public verification still requires real DNS, TLS, and an external WebSocket test.

License

GamePage is licensed under the MIT License. Each sibling repository retains its own license and third-party notices.

About

No description, website, or topics provided.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors