installer: actually enable the auto-updater on Linux and macOS - #42
Merged
Conversation
The installer writes /etc/systemd/system/pilot-updater.service (Linux) and
~/Library/LaunchAgents/network.pilotprotocol.pilot-updater.plist (macOS), and
it writes ~/.pilot/auto-update.json with {"enabled": true} while printing
"Auto-updates ENABLED (opt-out)". The consent block goes further and promises:
AUTO-UPDATES (on by default)
The pilot-updater service checks GitHub for new stable releases and
installs them automatically.
None of that was true. The unit and the plist were written but never
activated, so on a fresh install `systemctl is-enabled pilot-updater` reports
`disabled` / `inactive (dead)` and launchctl has no such job. Every install
from https://pilotprotocol.network/install.sh stays pinned on whatever release
shipped at install time and never receives a security or perf fix — silently,
while telling the operator the opposite.
This is not a recent regression: `git log -S` finds no commit in this repo that
ever added the activation. The fix exists in pilot-protocol/pilotprotocol's
copy of install.sh (added specifically to close this gap, and guarded there by
install-test.yml), but the two scripts had forked, and the copy served to users
is this one — the one that never got it. The guard never covered the canonical
script, so CI stayed green while the live installer shipped the defect.
Ported both branches verbatim from the working copy:
- Linux: `sudo systemctl enable --now pilot-updater`, non-fatal on failure.
- macOS: unload-then-`launchctl load -w` on the updater plist, which keeps
re-running install.sh (the upgrade path) idempotent.
The daemon is deliberately left opt-in on both platforms — it has
operator-tunable flags (-public, -hostname, registry overrides) that an
operator may want to set before first start. Only the updater is auto-started.
Verified on real GitHub-hosted ubuntu-latest and macos-latest runners via
pilot-protocol/pilotprotocol#432, which asserts the updater is enabled+active
(Linux) and loaded (macOS) after a non-interactive install.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
TeoSlayer
pushed a commit
to pilot-protocol/website
that referenced
this pull request
Jul 27, 2026
pilot-protocol/release#42 fixed a defect the audit turned up after #174: the canonical installer wrote the systemd unit and the LaunchAgent plist but never enabled or loaded them, so every install had auto-updates silently dead while ~/.pilot/auto-update.json and the printed consent block both claimed they were on. That fix is now live (R2 serves sha256 988ddebd…, 55,527 bytes). Re-point the Pages fallback at the new canonical so the hard drift check added in this PR passes, and so the fallback carries the fix too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
TeoSlayer
added a commit
to pilot-protocol/website
that referenced
this pull request
Jul 27, 2026
…commands (#175) * installer: sync Pages fallback with canonical script, fail on future drift public/install.sh is the fallback served if the pilot-release Worker/R2 path fails. Its own sync workflow calls it "a shadowed fallback kept byte-identical" to pilot-protocol/release:install.sh — but it had drifted 12KB and 3 days behind, and still shipped three defects that were already fixed upstream: 1. PATH invisible to non-interactive shells — `bash -c 'pilotctl version'` failed after a successful install. Fixed upstream by escalating the /usr/local/bin symlink through passwordless `sudo -n` and writing PATH to ~/.profile, ~/.bash_profile, ~/.bashrc, ~/.zshenv and ~/.zshrc. 2. The post-install guide told users to pipe replies through `jq`, which the installer never installs. Replaced upstream with the native `pilotctl inbox --latest`. 3. Post-install guidance emitted `pilotctl gateway start ...`; gateway is extras-only in the core CLI and the bare form no longer exists. The stale copy also advertised `--version v1.10.5` as its pinned-version example, five minor versions behind the v1.13.7 that stable now resolves to. public/install.sh is now byte-identical to release@c8fcaa9 (sha256 4c0babf9…, 53917 bytes), which is also exactly what the live URL serves. Verified end-to-end by running THIS copy in fresh debian:12 containers as a non-root user, both with and without passwordless sudo. Install exits 0 in both; `bash -c 'pilotctl version'` prints v1.13.7 from a new shell in both (the no-sudo run has no /usr/local/bin symlink and relies purely on the profile PATH writes); and the printed GET STARTED block contains no jq invocation. The drift check that let this happen was a `::warning::` nobody read. It is now a hard failure — but deliberately moved to the END of the job, after the R2 upload and live-URL verification. A drifted fallback must never block the fast path that pushes a security fix to the live installer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * installer: fail on Pages fallback drift, use curl -f in blog install commands Follow-up to #174, which resynced public/install.sh after it drifted 12KB and 3 days behind canonical while still shipping three already-fixed defects. The guard that should have caught that was a `::warning::` nobody read. It is now a hard failure — but deliberately moved to the END of the job, after the R2 upload and the live-URL verification. A drifted fallback must never block the fast path that pushes a security fix to the live installer; the failure is a loud notification that the fallback needs syncing, not a gate on shipping. Also: five install commands across two blog posts used `curl -sSL` instead of `curl -fsSL`. Without -f, curl writes an HTTP error body to stdout and exits 0, so a 404 or a Cloudflare error page gets piped straight into `sh`. Every other install command on the site already used -fsSL; these five were the outliers. src/pages/blog/connect-agents-across-aws-gcp-azure-without-vpn.astro (4) src/pages/blog/run-agent-network-without-cloud-dependency.astro (1) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * installer: re-sync Pages fallback with the auto-updater activation fix pilot-protocol/release#42 fixed a defect the audit turned up after #174: the canonical installer wrote the systemd unit and the LaunchAgent plist but never enabled or loaded them, so every install had auto-updates silently dead while ~/.pilot/auto-update.json and the printed consent block both claimed they were on. That fix is now live (R2 serves sha256 988ddebd…, 55,527 bytes). Re-point the Pages fallback at the new canonical so the hard drift check added in this PR passes, and so the fallback carries the fix too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Teodor Calin <teodor@vulturelabs.io> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
TeoSlayer
added a commit
to pilot-protocol/pilotprotocol
that referenced
this pull request
Jul 28, 2026
* installer: sync install.sh with canonical, guard the drift in CI This repo's install.sh is a COPY. The script users actually run is served from https://pilotprotocol.network/install.sh by the pilot-release Worker out of R2, sourced from pilot-protocol/release:install.sh. This copy had drifted 3 days and ~16KB behind canonical (38,150 vs 53,917 bytes) and still carried three defects already fixed upstream: 1. PATH invisible to non-interactive shells — `bash -c 'pilotctl version'` failed after a successful install. 2. The post-install guide piped replies through `jq`, which the installer never installs. This copy mentioned jq five times, the most of the three copies in the org. 3. It emitted `pilotctl gateway start ...`. Gateway is extras-only in the core CLI — cmd/pilotctl/main.go:1737 hard-errors on the bare form with "gateway commands are not in the core CLI". install.sh is now byte-identical to release@c8fcaa9 (sha256 4c0babf9…), which is also exactly what the live URL serves. README fixes for the same drift: - The demo showed `sudo pilotctl gateway start` — the form that hard-errors. Now `pilotctl extras gateway start`. - "What the installer does" claimed it installs pilot-gateway. Release tarballs ship daemon/pilotctl/updater only; I confirmed against the v1.13.7 artifacts. Reworded so nobody re-derives the dependency that broke fresh installs for ~6 weeks. The CI gap: install-test.yml only asserted that the updater got enabled, so it passed green throughout the drift, while the canonical script had no installer CI at all. Added a canonical-drift job that diffs this copy against the release repo and fails on any difference. Testing a copy that no user runs is theatre. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * installer: carry the auto-updater activation fix (release#42) The two install-test jobs on this PR failed, and correctly so: syncing to canonical would have regressed this repo's auto-updater activation. The canonical script writes the systemd unit / LaunchAgent but never enables or loads it, so `systemctl is-enabled pilot-updater` reported `disabled` and launchctl had no job — while ~/.pilot/auto-update.json said {"enabled": true} and the consent block promised auto-updates were on. That gap is now fixed at the source in pilot-protocol/release#42, and this commit picks up the fixed canonical script. install.sh here is byte-identical to that branch, so the canonical-drift job will go green once release#42 merges. This is exactly the regression install-test.yml was built to catch, and it caught it. The reason it never fired before is that it only ever guarded this repo's copy — which no user runs — while the canonical script served at https://pilotprotocol.network/install.sh had no installer CI at all. The canonical-drift job added in the previous commit closes that hole permanently. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Teodor Calin <teodor@vulturelabs.io> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
The installer promises auto-updates, then doesn't deliver them
install.shwrites/etc/systemd/system/pilot-updater.service(Linux) and~/Library/LaunchAgents/network.pilotprotocol.pilot-updater.plist(macOS). It writes~/.pilot/auto-update.jsonwith{"enabled": true}and prints "Auto-updates ENABLED (opt-out)". The consent block goes further:None of that was true. The unit and the plist are written but never activated. On a fresh install:
macOS is the same —
launchctl print gui/$UID/network.pilotprotocol.pilot-updaterfinds no such job.Impact: every install from
https://pilotprotocol.network/install.shstays pinned on whatever release shipped at install time and never receives a security or perf fix — silently, while telling the operator the opposite.This was never a regression here
git log -S 'systemctl enable --now pilot-updater' -- install.shreturns nothing in this repo. The activation has never existed in the canonical script.The fix does exist in
pilot-protocol/pilotprotocol's copy ofinstall.sh— added specifically to close this gap, and guarded there byinstall-test.yml, whose header comment documents the exact failure mode:The two scripts had forked. The copy that got the fix is the one no user runs; the copy served to users is this one. CI stayed green the whole time because it only ever guarded the wrong file.
The fix
Ported both branches verbatim from the working copy:
sudo systemctl enable --now pilot-updater, non-fatal on failure.launchctl load -won the updater plist, which keeps re-runninginstall.sh(the upgrade path) idempotent.The daemon is deliberately left opt-in on both platforms — it has operator-tunable flags (
-public,-hostname, registry overrides) an operator may want to set before first start. Only the updater is auto-started.Verification
Proven on real GitHub-hosted
ubuntu-latestandmacos-latestrunners via pilot-protocol/pilotprotocol#432, which asserts the updater is enabled+active (Linux) and loaded (macOS) after a non-interactive install. Those jobs failed against the current canonical script and pass with this one.Deploy impact
sync-install-shfires oninstall.shchanges and uploads this script to R2, which is whathttps://pilotprotocol.network/install.shserves. That is the point — but it is a live change, not a no-op.🤖 Generated with Claude Code