Skip to content

feat(updates): bootstrap host updater installs - #839

Merged
mcharles-square merged 31 commits into
mainfrom
codex/one-click-updates-05-installer-bootstrap
Aug 8, 2026
Merged

feat(updates): bootstrap host updater installs#839
mcharles-square merged 31 commits into
mainfrom
codex/one-click-updates-05-installer-bootstrap

Conversation

@mcharles-square

@mcharles-square mcharles-square commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Reviewable diff: +907/-32 across 6 files (excludes generated, test, and story files).

Summary

Bootstraps the packaged host updater on supported Linux/systemd installations with rootful Docker, then exposes only its secured Unix socket to Fleet. Unsupported hosts and any bootstrap path that cannot reach a verified safe state explicitly retain the existing copy-command experience.

Stack: #841#842#843#844#845#835#836#837#838#839#840. This is 5/6 of the one-click phase. All ancestors are merged, so this diff is directly against main; those PRs provide release discovery, capability reporting, the privileged updater, and the server trigger path. This PR installs and connects that updater, while #840 adds the operator-facing one-click action. Manual upgrades remain available throughout.

How it works

The installer validates the requested release name, downloads the bundle and matching SHA-256 sidecar into a private temporary directory, checks the exact filename and digest, and captures any legacy optional-overlay choices before replacing the deployment. On a compatible host it installs the updater binary, root-only environment, and systemd unit; verifies root and the installer use the same Docker daemon; and waits up to 60 seconds for an authenticated request over the Unix socket to succeed. Only then does run-fleet.sh persist ENABLE_ONE_CLICK_UPDATES=true and layer the socket-only Compose overlay. Every unsupported or safely recoverable failure persists false; cleanup uncertainty aborts the install. Uninstall stops and verifies the service before touching Docker, then removes exact updater files, state, runtime sockets, and hash-named staging directories.

flowchart LR
  I["Installer"] --> D["Private bundle download"]
  D --> V["Validate version, sidecar name, and SHA-256"]
  V --> H{"Compatible systemd and rootful Docker host?"}
  H -->|"yes"| S["Install service and verify Unix-socket readiness"]
  S --> O["Persist enabled state and layer updater socket"]
  H -->|"no or safe fallback"| M["Persist disabled state and keep copy command"]
  O --> F["Start Fleet"]
  M --> F
Loading
sequenceDiagram
  participant I as Installer
  participant G as GitHub Releases
  participant U as Host updater service
  participant R as run-fleet
  participant F as fleet-api
  I->>G: Download bundle and checksum sidecar
  I->>I: Verify exact asset digest and migrate legacy flags
  I->>U: Install, enable, restart
  I->>U: Poll status over secured Unix socket
  alt updater is ready
    I->>R: Enable one-click overlay
    R->>F: Start with read-only socket mount
  else unsupported or safely disabled
    I->>R: Disable one-click overlay
    R->>F: Start with manual fallback only
  end
Loading

Areas of the code involved

Area / package / file What changed Why it matters for review
deployment-files/install.sh Release validation, private checksum download, legacy option capture, daemon compatibility, service bootstrap/readiness, and fail-closed fallback Highest-risk host mutation and capability boundary
deployment-files/run-fleet.sh Persists the one-click setting and conditionally layers the updater socket overlay Prevents the UI capability from diverging from the active Compose model
deployment-files/uninstall.sh Serializes service shutdown ahead of teardown and removes exact updater/staging artifacts Review destructive scope, privilege use, and failure ordering
deployment-files/README.md, README.md Supported-host contract, exact-version install flow, trust boundary, logs, and recovery Defines operator expectations and fallback behavior
.github/workflows/deployment-config-checks.yml Adds individual shell syntax validation Catches installer/uninstaller syntax outside sourced test helpers
Focused deployment tests Covers migration ambiguity, CLI enable/disable persistence, overlay rendering, activation handoff, and cleanup verification Tests — review alongside the lifecycle paths

Key technical decisions & trade-offs

  • One-click is enabled only when root and the installer see the same Docker daemon, avoiding an unsafe rootless-Docker privilege bridge.
  • Service readiness requires both a successful socket status request and an active unit before Fleet receives the socket mount.
  • Failures before readiness must produce a verified inactive/disabled service; once readiness succeeds, the service stays available if later deployment work fails so persisted enabled state never points at a dead socket.
  • The SHA-256 sidecar detects corruption and binds the installer to the named release asset; it is not independent publisher authentication. GitHub Releases remains the publisher trust anchor, and detached signing is outside this PR.
  • Uninstall removes only fixed paths and the updater's exact lowercase 64-hex staging-name contract; alternate/multi-instance ownership remains outside the supported single-install model.

Testing & validation

  • Individual bash -n validation for installer, runner, uninstaller, and deployment test scripts.
  • ./deployment-files/tests/test-install-overlay-migration.sh
  • ./deployment-files/tests/test-profiles.sh
  • ./deployment-files/ha/tests/test-profile.sh
  • ./deployment-files/tests/test-run-fleet-upgrade.sh
  • git diff --check origin/main..HEAD
  • Pre-commit and pre-push hooks passed.
  • A real systemd/rootful-Docker clean-install and upgrade matrix was not run locally; that remains explicit release-host validation.

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown

🔐 Codex Security Review

Note: This is an automated security-focused code review generated by Codex.
It should be used as a supplementary check alongside human review.
False positives are possible - use your judgment.

Scope summary

  • Reviewed pull request diff only (dff6538cd00678458c5b6135852e83aed9248781...edd1075934462f5510b29c657494fbe1927368e6, exact PR three-dot diff)
  • Model: gpt-5.6-sol

💡 Click "edited" above to see previous reviews for this PR.


Review Summary

Overall Risk: NONE

Findings

No concrete security, correctness, or reliability defects were found in the changed hunks.

Notes

Bash syntax and diff-integrity checks passed. Functional shell tests could not run because the review environment’s temporary filesystem is read-only.


Generated by Codex Security Review |
Triggered by: @mcharles-square |
Review workflow run

@mcharles-square
mcharles-square force-pushed the codex/one-click-updates-05-installer-bootstrap branch from f031dd7 to 1ed35a3 Compare July 30, 2026 08:50
@mcharles-square
mcharles-square force-pushed the codex/one-click-updates-05-installer-bootstrap branch 2 times, most recently from 455254a to d79c649 Compare July 30, 2026 13:26
@mcharles-square
mcharles-square force-pushed the codex/one-click-updates-05-installer-bootstrap branch from d79c649 to faa6e55 Compare July 30, 2026 14:16
@mcharles-square
mcharles-square force-pushed the codex/one-click-updates-05-installer-bootstrap branch from faa6e55 to 1e46cc9 Compare July 30, 2026 15:25
@mcharles-square
mcharles-square force-pushed the codex/one-click-updates-05-installer-bootstrap branch from 1e46cc9 to 83cc576 Compare July 30, 2026 16:56
@mcharles-square
mcharles-square force-pushed the codex/one-click-updates-05-installer-bootstrap branch from 83cc576 to 15d9faf Compare July 30, 2026 17:37
@mcharles-square
mcharles-square force-pushed the codex/one-click-updates-05-installer-bootstrap branch from 8ff8e73 to f9a6056 Compare August 8, 2026 05:05

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f9a60566a3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread deployment-files/install.sh Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: edd1075934

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +855 to +856
${privilege[@]+"${privilege[@]}"} systemctl disable --now \
proto-fleet-updater.service >/dev/null 2>&1 || true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Disable runtime-enabled updater units with --runtime

When the existing service has UnitFileState=enabled-runtime, this plain systemctl disable --now edits only persistent unit links; the /run/systemd/system/...wants link remains, so the following state check still sees enabled-runtime and aborts every installer upgrade after stopping the updater. Fresh evidence beyond the earlier runtime-restoration finding is that systemctl --root left the unit runtime-enabled after this exact command, while systemctl --help says --runtime applies enable/disable changes “temporarily until next reboot.” The same unqualified disable in disable_direct_run_updater and prepare_host_updater_removal blocks fallback or leaves a stale link, so select runtime disablement for this state in all three lifecycle paths.

AGENTS.md reference: AGENTS.md:L141-L144

Useful? React with 👍 / 👎.

Comment on lines +1329 to +1330
if ! load_state=$(systemctl show --property=LoadState --value \
proto-fleet-updater.service 2>/dev/null); then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Fall back when no systemd manager is reachable

On a Linux host where systemctl and /run/systemd/system exist but the manager bus is unavailable, such as a container or chroot exposing that runtime path, this query returns nonzero and marks cleanup failed before checking whether any updater artifact exists. Fresh evidence beyond the earlier runner fallback finding is that the installer therefore aborts a clean manual install before extraction instead of reaching the documented no-systemd copy-command fallback; systemctl --help describes show as querying units/jobs or the manager, and a no-manager invocation returns status 1. Detect manager availability before this query and treat it as unsupported when no updater artifacts need reconciliation.

AGENTS.md reference: AGENTS.md:L141-L144

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation github_actions Pull requests that update GitHub Actions code review-policy: needs-review Managed by the Review Policy workflow. server

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants