diff --git a/changes/CH-012-create-rumble-client/proposal.md b/changes/CH-012-create-rumble-client/proposal.md index 9d268e565..3c8e516bc 100644 --- a/changes/CH-012-create-rumble-client/proposal.md +++ b/changes/CH-012-create-rumble-client/proposal.md @@ -12,7 +12,7 @@ title: Create Rumble Client Create the community-owned `robocode-dev/rumble-client` repository: a local ranked and practice battle client built on Tank Royale's Battle Runner. In ranked mode it will follow the Rumble data repository's canonical-location and engine-pin records, obtain the synchronized bot catalog and per-game-type matchmaking advice, run the selected battle against the pinned engine, record every completed result in a local journal with replay evidence, and submit batches through the registered-client issue-ops flow. Practice mode will run local battles without creating or submitting any ranked result. -The client will make ranked execution reproducible and intentionally constrained: it will verify schema versions, source hashes, catalog commit, game type, and `behaviorVersion` before a battle; it will preserve unsent journal entries across failures; and it will never receive repository-content, branch, projection, or Git-history write access. A rebuildable container will be the primary distribution and sandbox boundary, with documented bare-metal operation as a fallback. +The client will make ranked execution reproducible and intentionally constrained: it will verify schema versions, source hashes, catalog commit, game type, and `behaviorVersion` before a battle; it will preserve unsent journal entries across failures; and it will never receive repository-content, branch, projection, or Git-history write access. Contributors may run the client directly on a supported host or use the recommended rebuildable Docker image. The image supplies the pinned multi-runtime environment and separates online synchronization and submission from credential-free, offline battle execution. ## Why @@ -26,7 +26,7 @@ M-006 supplies the reviewed bot sources and M-007 supplies the engine pin, catal - Implement ranked selection for `1v1`, `twinduel`, and `melee`, preferring configured own bots while using seeded selection from published advice without treating advice as a reservation. - Integrate the Battle Runner to execute a full pinned battle, transcribe `BattleResults` into the data repository's result-envelope format, hash and retain a local replay, append it to the journal, and reject incompatible engine or catalog state before execution. - Implement registered-client issue-ops submission with batch retry/backoff and acknowledgement-driven journal rollover. Define the fork-pull-request transport as the portable fallback where the data-repository contract supports it. -- Provide a rebuildable, egress-constrained container carrying the pinned Tank Royale runtime and the Java, .NET, Python, and Node.js bot runtimes, with documented bare-metal fallback operation. +- Provide a rebuildable Docker image carrying the pinned Tank Royale runtime and the Java, .NET, Python, and Node.js bot runtimes, plus a supported native distribution whose preflight checks the same runtime requirements. Docker launchers run synchronization online without credentials, battles without external networking or submission credentials, and submission online without starting bot code. - Record M-008 evidence in P-003 only after a registered-client ranked battle reaches `robocode-dev/rumble-data` through issue-ops and its CI ingests it without manual intervention. ## Non-goals diff --git a/changes/CH-012-create-rumble-client/tasks.md b/changes/CH-012-create-rumble-client/tasks.md index 6101db788..0069dcec9 100644 --- a/changes/CH-012-create-rumble-client/tasks.md +++ b/changes/CH-012-create-rumble-client/tasks.md @@ -13,8 +13,10 @@ title: Task breakdown for CH-012 - [x] Pause for maintainer approval of this proposal before implementation, as required by the Rumble roadmap - [x] Define client capability ownership, contracts, acceptance criteria, and durable decisions in the Tank Royale corpus - [x] Resolve durable result acknowledgement so successful receipts follow publication of accepted facts -- [ ] Create and configure the community-owned `rumble-client` repository with contributor, security, configuration, container, and operating guidance +- [x] Create and configure the community-owned `rumble-client` repository with contributor, security, configuration, container, and operating guidance +- [x] Revise the CH-012 runtime-distribution contract so native execution is supported and Docker is the recommended isolated path (RCL-010, RCL-011) - [ ] Implement validated synchronization, bot cache preparation, ranked and practice selection, and Battle Runner execution for every V1 game type - [ ] Implement local journal and replay-evidence handling, result transcription, and registered-client issue-ops submission with acknowledgement-driven retry and rollover -- [ ] Add the rebuildable runtime container, bare-metal fallback, and focused positive and negative acceptance evidence, including a fully automated end-to-end ranked submission +- [ ] Add native distributions, the rebuildable Docker runtime, phased launchers, and focused positive and negative acceptance evidence (RCL-010, RCL-011) +- [ ] Publish and pin the Docker image only after a Tank Royale release containing the M-005 contracts is available - [ ] Record M-008 evidence in P-003, then run the external repository's focused checks, `clue validate`, and `clue-verify`; mark each task as it completes diff --git a/docs/capabilities/CAP-015-rumble-result-data/design.md b/docs/capabilities/CAP-015-rumble-result-data/design.md index 424c46c7f..2b4386b6c 100644 --- a/docs/capabilities/CAP-015-rumble-result-data/design.md +++ b/docs/capabilities/CAP-015-rumble-result-data/design.md @@ -14,7 +14,7 @@ The external `robocode-dev/rumble-data` repository owns result storage and proje ## Input and immutable-fact contracts -`engine.json` pins a positive `behaviorVersion`, Tank Royale release and image references, and ranked settings for `1v1`, `twinduel`, and `melee`. `catalog.json` is the synchronized published bot catalog; only its active name-and-version entries can occur in ranked results. A contributor is admitted by a reviewed `clients/.json` registration that declares stable client IDs. +`engine.json` pins a positive `behaviorVersion`, Tank Royale release and image references, an optional immutable `clientImage` reference for the recommended Rumble Client Docker distribution, and ranked settings for `1v1`, `twinduel`, and `melee`. The client image is installation guidance rather than the compatibility axis and is published only for a Tank Royale release carrying the Rumble contracts. `catalog.json` is the synchronized published bot catalog; only its active name-and-version entries can occur in ranked results. A contributor is admitted by a reviewed `clients/.json` registration that declares stable client IDs. An issue-ops submission is one fenced JSON batch envelope with `schemaVersion`, `clientId`, `clientVersion`, and one to sixty result records. Each record includes a UUID `battleId`, completion time, matching nested client identity, behavior version, game type, pinned battle dimensions, and the complete Battle Runner participant result model. The validator checks the contract independently for every record, then normalizes valid records with the submitting account and payload hash. A content-addressed JSON file under `results/raw///` is the authoritative fact; issue bodies are transport, never state. The workflow publishes successful per-result receipts only after accepted facts reach the canonical repository, and an identical retry of an already retained result receives the same successful outcome. diff --git a/docs/capabilities/CAP-016-rumble-client/criteria.md b/docs/capabilities/CAP-016-rumble-client/criteria.md index c081159cd..b8ade7ff5 100644 --- a/docs/capabilities/CAP-016-rumble-client/criteria.md +++ b/docs/capabilities/CAP-016-rumble-client/criteria.md @@ -68,13 +68,13 @@ Feature: rumble-client — Local ranked and practice battle client Then it creates labelled result issues containing between one and sixty results as required by the result-data contract and correlates each post-publication ingestion receipt to its journal records And it neither requests nor uses permission to modify repository contents, branches, releases, packages, Pages, facts, or projections - @RCL-008 @draft - Scenario: The client runtime can execute every supported bot platform within its declared boundary + @RCL-008 @retired + Scenario: Retired — the client runtime can execute every supported bot platform within its declared boundary Test-type: E2E - Given the pinned Rumble engine and catalog contain Java, .NET, Python, and TypeScript bots - When a contributor builds the primary container or follows a supported bare-metal setup - Then the client can boot each platform with the pinned runtime versions and run a battle through Battle Runner - And the container restricts bot networking to the local server while client egress is limited to repository synchronization and result submission + Given criterion RCL-008 coupled runtime portability and an ambiguous all-in-one container boundary + When CH-012 separates supported distribution from phase isolation + Then RCL-010 supersedes its runtime portability promise + And RCL-011 owns the Docker phase-isolation promise @RCL-009 @draft Scenario: A ranked result reaches the immutable Rumble facts without manual handling @@ -83,4 +83,21 @@ Feature: rumble-client — Local ranked and practice battle client When the client synchronizes, selects and completes a battle, journals its result, and submits it through issue-ops Then the result-data ingestion workflow accepts the record as an immutable raw fact and regenerates the affected projections And no person copies, edits, or grants repository-content write access to deliver the result + + @RCL-010 @draft + Scenario: Native and Docker distributions execute every supported bot platform + Test-type: E2E + Given a released Rumble engine and catalog contain Java, .NET, Python, and TypeScript bots + When a contributor uses the supported native distribution or the recommended Docker image + Then both distributions validate the pinned runtime versions and run each platform through Battle Runner + And native preflight reports missing prerequisites without installing or changing them + + @RCL-011 @draft + Scenario: Docker execution keeps bot code offline and submission credentials out of battles + Test-type: Integration + Given the Docker launcher uses one bounded client work directory for synchronization, battle, and submission + When it runs a ranked session + Then synchronization runs online without a submission credential and without starting bot code + And battle execution runs without external network access or a submission credential + And submission runs online with the Issues-only credential without starting bot code ``` diff --git a/docs/capabilities/CAP-016-rumble-client/design.md b/docs/capabilities/CAP-016-rumble-client/design.md index a8657f0c4..a6c468cc0 100644 --- a/docs/capabilities/CAP-016-rumble-client/design.md +++ b/docs/capabilities/CAP-016-rumble-client/design.md @@ -40,7 +40,9 @@ Fork-pull-request submission remains a portability option only after the result- ## Runtime boundary -The primary distribution is a rebuildable container containing the pinned Tank Royale engine, Battle Runner, and the Java, .NET, Python, and Node.js runtimes required by the catalog. Client network egress is limited to repository synchronization and issue submission; bot processes receive only the local server connection. Documented bare-metal setup remains available with an explicit warning that it does not provide the container boundary. +Direct host execution and a rebuildable Docker image both run the same client phases and contracts. Docker is the recommended installation because the image contains the pinned Tank Royale engine, Battle Runner, and the Java, .NET, Python, and Node.js runtimes required by the catalog. The native distribution checks those prerequisites but does not install them and warns that direct bot execution has no Docker isolation. + +The Docker launcher invokes the image as separate synchronization, battle, and submission processes over one bounded local work directory. Synchronization has network access but no submission credential and starts no bot code. Battle execution has no external network access or submission credential. Submission receives the Issues-only credential but starts no bot code. The image is published only from a release carrying a Tank Royale version with the M-005 contracts, and `engine.json.clientImage` identifies the accepted immutable image digest. ## External evidence diff --git a/docs/decisions/0043-rumble-client-trust-boundary.md b/docs/decisions/0043-rumble-client-trust-boundary.md index 8c6f1d0b7..dff5f6601 100644 --- a/docs/decisions/0043-rumble-client-trust-boundary.md +++ b/docs/decisions/0043-rumble-client-trust-boundary.md @@ -20,11 +20,11 @@ The client exposes mutually exclusive ranked and practice modes. Ranked mode exe The append-only local journal is the durability boundary and issue-ops is the only V1 submission transport. The client removes records only after durable ingestion acknowledgement as defined by [ADR-0044](0044-durable-rumble-result-acknowledgement.md). A client credential is limited to Issues access on the result-data repository and never grants repository-content, branch, release, package, Pages, fact, or projection write access. Fork-pull-request submission is introduced only if the result-data contract later supports it. -Replay evidence remains local, bound to its result by battle ID and SHA-256 hash. The primary runtime is a rebuildable, egress-constrained container; bare-metal execution is an explicit fallback with a weaker isolation boundary. +Replay evidence remains local, bound to its result by battle ID and SHA-256 hash. Direct host execution and a rebuildable Docker image are both supported. Docker is the recommended distribution because it supplies the complete pinned runtime and gives battle execution a narrower host boundary. Its launcher separates synchronization, battle, and submission: synchronization runs online without a submission credential, battle execution runs without external networking or a submission credential, and submission runs online without starting bot code. Direct execution uses the same phases and contracts but has a weaker isolation boundary. ## Rationale -Separating private experimentation from automatic ranked journaling removes selective submission from the normal workflow. Durable acknowledgement preserves results across publication and receipt-delivery failures. Issue-only credentials limit compromise impact, while local replay evidence supports moderation without making the shared data repository a binary store. The container provides one reproducible multi-runtime execution boundary without requiring central infrastructure. +Separating private experimentation from automatic ranked journaling removes selective submission from the normal workflow. Durable acknowledgement preserves results across publication and receipt-delivery failures. Issue-only credentials limit compromise impact, while local replay evidence supports moderation without making the shared data repository a binary store. The Docker distribution provides one reproducible multi-runtime environment without requiring central infrastructure, and phase separation keeps credentials out of bot execution. ## Consequences @@ -33,7 +33,8 @@ Separating private experimentation from automatic ranked journaling removes sele - Temporary forge or network failures retain unsent results locally. - Moderators may request replay evidence, but the Rumble cannot recover evidence a contributor loses. - V1 depends on the forge's issue API; transport portability is deferred until the receiving capability implements it. -- Bare-metal users accept the residual risk of running reviewed bot source outside the container boundary. +- Direct-execution users accept the residual risk of running reviewed bot source without Docker isolation. +- Docker is a contributor-side runtime option, not hosted Rumble infrastructure. ## References diff --git a/docs/design/rumble/README.md b/docs/design/rumble/README.md index 9a5492a0b..a475e00d0 100644 --- a/docs/design/rumble/README.md +++ b/docs/design/rumble/README.md @@ -137,7 +137,7 @@ proposals: | **Practice mode** in the client: free local battles against rumble bots, never submitted; ranked mode auto-submits everything. | Client doc | | **Replays stay client-side** as read-only evidence, bound to results by `battleId` (UUID) + SHA-256. | Client doc | | **Batched submissions** from a local journal; clients never touch Git history (no commits, no amends). | Client doc | -| **Runtimes ship in the client container** (JVM, .NET, Python, Node.js + pinned engine), tagged by engine version; install scripts as bare-metal fallback. | Client doc | +| **Native and Docker client distributions are supported**; Docker is recommended and ships the JVM, .NET, Python, Node.js, and pinned engine, while native preflight checks user-installed prerequisites. | Client doc | | **Forge ToS reviewed**: this usage is a software project, not detached storage; design keeps traffic and repo size deliberately modest. | Aggregation doc | | **Bot names are bound to their owner** at first merge; only the owner's registered accounts may submit new versions. Owners may register multiple forge accounts; account changes require a PR from an already-registered account. | Submission doc | | **Official Bot APIs required** for ranked bots (Java, C#, Python, TypeScript); custom frameworks are not eligible. Resolves the dependency allowlist per platform. | Submission doc | @@ -145,7 +145,7 @@ proposals: | **Bot slots are configurable**, defaulting to 5 active bot entries per owner for launch. The value must be a named validation/governance constant, not a magic number. | Submission doc | | **License required per bot**, validated in CI against a small permissive allowlist; missing or wrong license dismisses the PR. | Submission doc | | **Evidence backups are the user's responsibility**, actively encouraged by the client; replays are never held centrally. | Client doc | -| **Submission at battle boundaries** (a battle = the game type's full round count); container gets a forge-only egress allowlist; journal staleness is bounded by the engine pin. | Client doc | +| **Submission at battle boundaries** (a battle = the game type's full round count); Docker separates online synchronization and submission from offline credential-free battle execution; journal staleness is bounded by the engine pin. | Client doc | | **Issue-ops spelled out**: submissions are labeled forge issues carrying a JSON batch envelope, drained and closed by CI; spam is handled by strict format, per-account budgets, and forge-level blocking as last resort. | Aggregation doc | | **Onboarding PR required from day one** for result submitters: a one-time, moderated registration under `clients/` in the data repo; unregistered submissions are closed unprocessed. | Aggregation doc | | **GitHub Pages confirmed** for the dashboard; compaction policy settled (monthly rollups to the archive branch after three full months). | Aggregation doc | diff --git a/docs/design/rumble/bot-submission.md b/docs/design/rumble/bot-submission.md index 607e30dd7..4e7729af7 100644 --- a/docs/design/rumble/bot-submission.md +++ b/docs/design/rumble/bot-submission.md @@ -317,9 +317,7 @@ Without central sandbox infrastructure, review cannot make untrusted code safe; - The source-tree hash in `index.json` guarantees that what clients run is exactly what was reviewed (clients run the sources from the pinned commit, so the hash covers precisely what executes). -- Actual containment happens on the client via the recommended sandbox container, described in the - [client document](./client-battles-and-results.md#sandboxing). This split of responsibility - (review reduces malice, the container contains it) should be stated plainly in contributor docs. +- Host containment is available through the recommended Docker distribution described in the [client document](./client-battles-and-results.md#runtimes-native-and-docker-distributions). Its battle phase runs offline without submission credentials; supported native execution knowingly runs reviewed bot code with the contributor's permissions. This split of responsibility should be stated plainly in contributor docs. ## Licensing diff --git a/docs/design/rumble/client-battles-and-results.md b/docs/design/rumble/client-battles-and-results.md index d9ba95fdd..c8e2e9239 100644 --- a/docs/design/rumble/client-battles-and-results.md +++ b/docs/design/rumble/client-battles-and-results.md @@ -257,8 +257,9 @@ in `rumble-data/engine.json`: { "schemaVersion": 1, "behaviorVersion": 7, - "release": "1.1.4", - "clientImage": "ghcr.io//rumble-client:1.1.4" + "tankRoyaleVersion": "1.1.4", + "image": "ghcr.io//tank-royale:1.1.4", + "clientImage": "ghcr.io//rumble-client@sha256:" } ``` @@ -268,7 +269,7 @@ Semantics: that carries the pinned behavior version is acceptable; the client and the validator both compare the behavior version reported by the running server against the pin. A GUI-only release 1.2.0 with unchanged `behaviorVersion 7` causes no rollout, no client obsolescence, - and no epoch reset. `release`/`clientImage` in the pin are convenience ("which build to + and no epoch reset. `tankRoyaleVersion`/`clientImage` in the pin are convenience ("which build to install"), not the compatibility contract. - **A `behaviorVersion` bump is the rollout event.** All clients become obsolete at that moment, by design: mixed behavior versions would silently corrupt result comparability. On its next @@ -281,30 +282,20 @@ Semantics: an intended one requires bumping `behaviorVersion` in the same change. The Tank Royale preparation proposal defines the hook for this guard; the replay corpus can be expanded in a later proposal. -- Upgrading must be **one step**: `docker pull` the image named in the new pin for container - users, or re-running the platform install script for bare-metal users. - -## Runtimes: the Client Container and Install Scripts - -A rumble client must be able to boot bots for **all four platforms**: Java (JVM), C# (.NET), -Python, and TypeScript (Node.js). Requiring users to hand-assemble four runtimes is a -participation killer, so the primary distribution is a container image: - -- **`rumble-client` image**: bundles the pinned server, booter, runner, the rumble client itself, - plus the exact runtime versions (JRE, .NET SDK, Python, Node.js/npm) matching the engine pin. - Tagged by release version (`rumble-client:1.1.4`, the image named in `engine.json`), so - upgrading engine and runtimes is one pull; the pinned image implies the pinned - `behaviorVersion`. -- The image doubles as the **sandbox**: run with no outbound network (localhost WebSocket only) - except the submission endpoint, and CPU/memory/time limits via container flags. Review reduces - malice (submission document), the container contains it; no one pretends there is a central - sandbox. +- Upgrading is explicit: Docker users pull the immutable client image named in the pin, while + native users install the matching client distribution and satisfy its checked prerequisites. + +## Runtimes: Native and Docker Distributions + +A Rumble client must be able to boot bots for **all four platforms**: Java (JVM), C# (.NET), Python, and TypeScript (Node.js). Contributors may use either the supported native distribution or the recommended Docker image: + +- **Native distribution**: ships the client and pinned Battle Runner as release archives. Its preflight checks the required Java, .NET SDK, Python, and Node.js versions and reports missing prerequisites without installing or changing them. Reviewed bot source then runs directly with the contributor's permissions, so this path provides no Docker isolation. +- **`rumble-client` image**: bundles the client, pinned server, booter, runner, and exact Java, .NET SDK, Python, and Node.js/npm versions matching the engine pin. Human-friendly release tags are published, while `engine.json.clientImage` records the immutable digest. The image is not published for an unreleased Tank Royale engine. +- **Phase isolation**: the Docker launchers run synchronization online without credentials, battle execution with no external network or submission credential, and submission online without starting bot code. The battle process runs non-root with a read-only root filesystem, a bounded work-directory mount, dropped capabilities, `no-new-privileges`, and CPU, memory, PID, and time limits. - The `Dockerfile` lives in the repo, so forks can rebuild the image even though registry packages (GHCR) do not fork with the repo (principle P2 is satisfied by rebuildability, not by the artifact). -- **Bare-metal fallback**: documented install scripts per OS (Linux, macOS, Windows) that check - for and install the required runtime versions and the pinned engine artifacts. Bare-metal users - knowingly accept the residual risk of running reviewed-but-untrusted code outside a container. +- Docker Engine or Docker Desktop is installed by the contributor. Podman compatibility is not a V1 contract. ## Client Policy Details @@ -322,6 +313,4 @@ participation killer, so the primary distribution is a container image: its game type's full round count; nothing is submitted mid-battle. The default is to submit after each completed battle, with batching used when submissions back up or rate limits require it. -- **Container network policy: egress allowlist.** The container may reach the forge for repo sync - and result submission and nothing else. Bot processes get no network beyond the localhost - WebSocket to the server. +- **Docker network policy.** Synchronization and submission are distinct online client processes that never start bot code. Battle execution uses Docker's no-network mode; its loopback interface remains available for the local server WebSocket. diff --git a/docs/design/rumble/user-documentation.md b/docs/design/rumble/user-documentation.md index 0fac4740f..acc3ab64a 100644 --- a/docs/design/rumble/user-documentation.md +++ b/docs/design/rumble/user-documentation.md @@ -58,7 +58,7 @@ flowchart TD end subgraph runnerJ["Battle contributor"] - B1["Read client quickstart"] --> B2["Pull client container
(or run install script)"] + B1["Read client quickstart"] --> B2["Choose Docker image
or native distribution"] B2 --> B3["One-time onboarding PR
(register forge account + client id)"] B3 --> B4["Configure rumble-client.json"] B4 --> B5["Run ranked mode;
results submit automatically"]