fix(server): make remote updates rollback-safe - #5181
Merged
Conversation
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Remote server updates had accumulated multiple version owners and handoff paths, so a failed candidate could leave the service pinned to a broken version.
This replaces that implementation with one stable systemd launcher that owns version selection and durable update state. Candidates are staged and checked against the exact migration manifest, complete all fallible startup before reporting prepared, then activate only after the launcher commits. Clients correlate reconnects with the launcher-generated update ID, while foreground servers keep the explicit manual-update path.
Verification
Built with GPT-5.6-sol in the Codex harness via T3 Code.
Note
High Risk
Changes core boot/update infrastructure: systemd units, version pinning, DB migration gating, and process handoff—failures can brick startup or leave the service on a bad runtime until rollback.
Overview
Replaces remote self-update paths that pointed systemd at a versioned
bin.mjs(and foreground respawn) with a stableservice-launcherunder~/.t3/runtime, durableservice-state.json, and IPC between the running server and the launcher.Boot service now installs the launcher artifact, writes initial state, and sets
ExecStarttonode …/service-launcher.mjs(withKillMode=control-group). Pinned runtimes are npm-installed into a staging tree, validated (including__service-preflightagainst the live DB migration manifest), then atomically published; blocked preflights surface the localnpx t3@… service updatepath instead of mutating the DB remotely.Remote update RPC stages the target version, runs preflight on the staged entry, then
requestUpdateon the launcher and returns a launcher-generatedupdateIdfor client correlation—no direct unit rewrite or detached respawn from the server process.Startup ordering adds
ServerActivation/forkParkedso background reactors and side effects (runtime state, Tailscale, cloud link reconcile) wait until activation; HTTP ingress is parked behindawaitCommandReady. Launcher trial children skip WAL and migrations so preflight stays read-only.Servers without an installed launcher-managed service can no longer self-update remotely; the error directs users to
t3 service install.Reviewed by Cursor Bugbot for commit ff26b5a. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Make remote server updates rollback-safe via a stable systemd launcher and staged installs
serviceLauncher.tsprocess that owns update state and child process lifecycle. The systemd unit now points to a stable launcher file instead of a versioned CLI entry, so rollbacks don't require rewriting the unit.pinnedRuntime.tsnow stages npm installs into a temp directory, validates via a preflight check, writes a sentinel, then atomically publishes via rename. Staging is cleaned up on failure or interruption.__service-preflightCLI command checks that DB migrations and launcher protocol match before an update is committed; if migrations are required, the update is blocked with a message to runnpx t3@<version> service updatelocally.readylifecycle event using a launcher-providedupdateIdand immediately surfaces aServerUpdateTerminalErroron rollback/failure instead of waiting for a generic reconnect timeout (extended to 4 minutes).forkParked/ServerActivationpattern so background fibers (tailscale, cloud link reconcile, runtime state persistence) are held until the activation gate is signaled, preventing races during startup.removePinnedRuntimeInstallationis removed as a public API; callers that relied on it will break at compile time.Macroscope summarized ff26b5a.