Files
browser/openspec/changes/support-custom-server-deployment-workflows/design.md
T

77 lines
6.4 KiB
Markdown

## Context
The platform currently creates an `installing` server instance only when a Run endpoint is already online. It records runtime bindings as safe logical references and dispatches only plugin/profile/action metadata. This prevents self-hosted operators from defining a server before Run is installed, from pointing at an existing absolute directory, and from using a nonstandard command such as a Python virtual environment launcher.
The independent Run repository remains the only host-side executor. Repository rules forbid returning host paths, raw credentials, or direct sockets from Run to Platform or plugins.
## Goals / Non-Goals
**Goals:**
- Persist an editable server deployment definition without a Run binding.
- Support guided install, existing-server adoption, and custom lifecycle command modes.
- Accept operator-entered absolute paths and command lines as protected write-only execution inputs.
- Render and validate plugin create fields, including port and player-count fields.
- Bind a saved definition to an online Run only at deployment/start time and send Run a versioned, redaction-safe execution plan.
- Surface queued, claimed, preflight, install, configure, start, and health stages to the operator.
**Non-Goals:**
- Platform-side SSH, shell execution, direct sockets, or a Run source tree in this repository.
- Cloud hosting, billing, provider marketplaces, or automatic network/firewall provisioning.
- Returning stored full paths, command text, or raw secrets through normal Platform APIs.
- Guaranteeing a generic command works on a node whose Run policy disallows it.
## Decisions
### 1. Separate deployment definitions from runtime bindings
Add a server deployment definition associated with a server instance. It holds deployment mode, plugin create inputs, protected path/command fields, configuration revision, and a binding state. Runtime bindings remain for plugin-declared logical adapters such as RCON and file transports.
This avoids weakening the existing logical-reference contract simply to accommodate physical deployment input. Reusing runtime bindings would make host paths appear in APIs that intentionally redact those values.
### 2. A server can exist as an unbound draft
`draft` is added as an editable server state. A draft has no `runEndpointId`, no queued lifecycle job, and can be created or edited before any Run registers. Binding and deployment are explicit later operations. `installing`, `ready`, `running`, `stopped`, and `failed` retain their existing lifecycle meaning.
The alternative—requiring a placeholder Run endpoint—would preserve the current coupling and create misleading jobs.
### 3. Paths and commands are protected write-only fields
The browser can submit full paths and command text as an operator action. Platform stores them in a protected deployment record and only sends them to the assigned Run via a leased job input. Read APIs return configured flags, a non-sensitive display mode, and a content fingerprint, never the value. Editing a protected value requires resubmission; an empty update preserves the stored value.
Raw credentials are rejected from commands and must be represented by secret references. This satisfies the host-path redaction rule while supporting real input such as `/srv/server/.venv/bin/python`.
### 4. Plugin templates are recommendations; custom commands override per lifecycle action
Plugins publish create-field schemas and optionally map inputs to recommended install/start/stop templates. Guided mode resolves these templates. Existing-server and custom-command modes permit an operator to provide a working directory plus install/start/stop commands; a missing install command is valid for adoption.
Run receives an argv-oriented command plan by default. A full shell command is allowed only when the operator explicitly selects a shell kind and the Run endpoint advertises the corresponding custom-process policy. This avoids accidental shell interpretation while allowing deliberate venv, batch, PowerShell, and shell-wrapper deployments.
### 5. Run preflight and lifecycle phases are first-class job progress
The lifecycle job execution input includes deployment revision, mode, protected plan, create inputs, and a bounded phase vocabulary: `queued`, `claimed`, `preflight`, `install`, `configure`, `start`, and `health`. Run validates paths/executables, policy, port availability, and plugin compatibility before any write. It reports only phase, percent, safe summary, and structured safe error code.
Platform shows these phases after submit and distinguishes an unclaimed job from a running job. A Run implementation is required in its independent repository; until it supports this input version, Platform must fail safely with an actionable compatibility reason.
## Risks / Trade-offs
- [A custom command can be destructive] → Require server-owner/node-operator authorization, Run policy opt-in, explicit shell selection, bounded timeout, command fingerprint audit, and confirmation before dispatch.
- [A path is sensitive operational data] → Treat it as write-only in read models and strip it from logs, job summaries, plugin bridge results, and diagnostics.
- [Existing persisted instances assume a Run endpoint] → Migrate existing records unchanged; only newly created drafts omit it.
- [Run protocol rollout lags Platform] → Version the execution input and make deployment unavailable with a clear compatibility result rather than silently ignoring user input.
- [Port collision cannot be known from Platform] → Validate form shape in Platform, then make the Run preflight authoritative and return its safe diagnostic.
## Migration Plan
1. Add deployment definition persistence and draft state while accepting all existing bound instances unchanged.
2. Release Platform/Web support for draft creation and protected deployment updates.
3. Release the versioned contract to Run; enable guided and custom dispatch only after Run reports the deployment-plan capability.
4. Update first-party SCUM and Minecraft templates and add Palworld only as a separate plugin change.
5. Roll back by retaining deployment definitions as drafts and refusing dispatch to incompatible Run versions; no host-side rollback is initiated automatically.
## Open Questions
- The independent Run repository must define its exact supported custom shell identifiers and endpoint policy advertisement.
- A separate Palworld plugin remains required; this change provides the shared deployment capability but does not invent a Palworld launcher.