Files
browser/openspec/changes/add-scum-source-rcon-transport/design.md
T

7.5 KiB

Context

The prior UE4SS DLL change provisions ue4ss/Mods/scum_simple_rcon/config.ini before a normal SCUM start, but the declared Run rcon adapter is only an envelope placeholder. The shipped DLL is a Source RCON server that is intentionally loopback-only and accepts raw SCUM commands on its game-thread queue; SendChat <type> "message" [SteamID64] is its supported chat form.

Platform and Run already use leased jobs, signed Run-only input routes, scoped workspaces, and durable job state. Sending a raw admin command through the generic remote-adapter inputs map would persist it in the Platform job and Run journal, conflicting with the requested no-history/no-audit behavior.

Goals / Non-Goals

Goals:

  • Let an authorized server operator send a typed chat message or raw SCUM command through the existing job channel without a second confirmation or command audit record.
  • Transfer a command exactly once to the active leased Run worker without storing the raw text or RCON password in Platform persistence, job assignments, Run journals, browser DTOs, logs, or result messages.
  • Bind every dispatch to a ready, selected Windows amd64 UE4SS DLL declaration and a fixed Source RCON loopback plan.
  • Have Run authenticate to 127.0.0.1 using the protected generated config and implement the Source RCON wire protocol with bounded I/O.

Non-Goals:

  • Do not modify, build, or redeploy the UE4SS DLL source as part of command transport.
  • Do not create a generic remote TCP/RCON gateway, expose an RCON host or password, or allow browser-to-Run sockets.
  • Do not persist raw command text, chat text, source responses, a command history, or per-command audit events.
  • Do not retry a mutating command automatically, provide a Linux .dll fallback, or implement a separate Linux UE4SS .so runtime.

Decisions

Decision: Use a dedicated typed SCUM RCON request instead of generic remote-adapter inputs

Platform will expose a typed server-scoped request that accepts either a chat payload (type, message, optional SteamID64) or a bounded raw command. It validates the source DLL grammar and formats chat as SendChat internally. It validates ownership, the declared RCON capability, selected runtime binding, ready extension release, Windows amd64 endpoint, and the selected profile's RCON transport before it creates a job.

The job contains only an opaque input reference and a frozen source-rcon connection plan: extension key/mod key, logical config reference, and port. It contains no host, command, response, or secret. The existing generic remote-adapter endpoint remains suitable for declared non-secret inputs but is not used for raw SCUM commands.

Alternative considered: put command in ExecutionInput.Inputs. Rejected because Platform persistence, job claims, and Run's durable journal would retain the command.

Decision: Use a one-time in-memory input broker fenced by the active Run lease

Platform stores each validated command in a mutex-protected, TTL-bounded in-memory broker before the corresponding job becomes claimable. The broker key is the job ID and cannot overwrite an outstanding submission with the same idempotency key. The signed Run-only RCON-input route first verifies endpoint, session, job, lease, attempt, capability, and frozen plan, then consumes and removes the payload.

The RCON job has exactly one attempt. A Platform restart, expired broker entry, worker crash after consume, or repeat input read fails closed rather than replaying a possibly mutating game command. Job/audit/result summaries use only generic delivery state.

Alternative considered: encrypting a durable broker row. Rejected because the explicit product boundary is no raw command persistence; encryption would still create history and recovery/replay semantics.

Decision: Freeze a source-RCON loopback plan from the selected ready UE4SS extension

Platform derives the config reference from the extension's declared DLL layout (ue4ss/Mods/<modKey>/config.ini) and freezes the published port plus mod/extension identity. Because the actual SCUM executable may be below the workspace root, Run also owns a verified deployment-state marker that records the scoped logical config location written during DLL activation. The frozen plan carries only that safe marker reference; Run validates its extension/mod/port and expected config suffix before it reads the protected file. Run accepts only the source-rcon kind, Windows amd64, safe logical references, and an unprivileged declared port. There is no configurable host; Run always dials 127.0.0.1:<port> in the server's scoped workspace.

The generator is corrected to write the DLL's actual bind_address=127.0.0.1 setting. Run rejects a managed config whose port or bind address no longer matches the frozen plan.

Alternative considered: resolve paths or credentials in Platform. Rejected because Platform must not receive host paths or the generated RCON password.

Decision: Treat Source RCON result bodies as sensitive transient data

Run authenticates, executes one command, and reads bounded response packets until the DLL's empty response sentinel. It classifies an error: response as a failed job but returns only a safe status/error code; it never includes raw command text, password, or response body in logs, artifacts, result messages, or Platform-facing payloads.

Alternative considered: stream full RCON output to the browser. Rejected because source responses may echo command text or game/player data and would reintroduce command history through job results/logs.

Decision: Give the management console two direct entry points

The existing server detail surface gets a compact chat form and a raw-command form. Both submit immediately using typed API contracts and display only the current safe queue/result status. They intentionally do not add a confirmation modal, saved form history, command list, secret field, host field, or response transcript.

Risks / Trade-offs

  • [Platform or worker restarts during a command] → the broker payload is gone or consumed; the job fails closed and an operator can explicitly submit a new command.
  • [SCUM/UE4SS is not loaded or the port is unavailable] → Run returns a safe connection/auth/protocol failure without falling back to a remote address.
  • [An RCON response echoes sensitive input] → Run only classifies it and does not surface the body.
  • [A user submits an unsupported chat target or malformed text] → Platform rejects it before a job/broker entry is created.
  • [A DLL declaration is still unpublished] → the feature stays unavailable until real immutable release pins are published and selected.

Migration Plan

  1. Deploy Platform, Run, plugin manifest, and management-console changes together.
  2. Publish a real ready DLL declaration, ensure the Windows UE4SS bootstrap is present, and restart SCUM so Run writes the managed loopback config.
  3. Verify rcon.status or a harmless rcon.chat through the console on a non-production server, then send a broadcast and an allowed admin command.
  4. Roll back by removing the new Run capability/using the previous Platform and Run releases; no persistent commands or secrets require migration or cleanup.

Open Questions

  • The current SCUM manifest is intentionally unpublished; end-to-end production dispatch remains gated until the publisher supplies the real DLL URL, checksum, size, executable checksum, and UE4SS ABI.
  • Future command-output streaming would need a separate transient, authorization-reviewed design rather than reusing durable job results.