Skip to content

installer: don't clobber an existing config.json on a fresh install - #44

Merged
TeoSlayer merged 1 commit into
mainfrom
fix/preserve-existing-config
Jul 29, 2026
Merged

installer: don't clobber an existing config.json on a fresh install#44
TeoSlayer merged 1 commit into
mainfrom
fix/preserve-existing-config

Conversation

@TeoSlayer

Copy link
Copy Markdown
Contributor

The comment above this write already promised the right behaviour —

config.json is written ONLY on a fresh install. A re-run must never clobber registry/beacon/consent settings the operator edited by hand.

— but the guard didn't deliver it.

UPDATING is derived purely from [ -x "$BIN_DIR/pilotctl" ], i.e. whether the binary exists. It says nothing about the config. So a host with a hand-edited ~/.pilot/config.json but no binary took the "fresh install" branch and had its config silently overwritten:

  • binaries removed for a clean reinstall, config deliberately kept
  • config restored from backup before reinstalling
  • config pre-seeded before a first install

Same class as the rest of this installer's recent history: the stated intent was right, the condition tested the wrong thing, and the failure was silent — no warning, no backup, no diff.

Secondary effect

It also made consent settings unreachable before first start. Pre-seeding {"consent":{...}} or {"skill_inject":{"mode":"disabled"}} was erased by this write — and the erase happened before the first pilotctl skills check pass further down. So the opt-outs the installer itself documents could only be applied after the first injection had already run.

Guarding on the file makes them reachable at install time.

Defaults are unchanged

This does not alter skill-injection mode, auto-updates, telemetry, or any other default. A host with no config gets exactly the same config it got before. The only behavioural change is that an existing file is left alone instead of destroyed.

Verification

Fresh debian:12 containers, both directions:

scenario result
pre-seeded config (custom registry, telemetry off, skill_inject disabled) survives byte-for-byte, exit 0
true fresh install, no config standard config written, pilotctl versionv1.13.7

🤖 Generated with Claude Code

The comment above this write already promised the correct behaviour — "a
re-run must never clobber registry/beacon/consent settings the operator
edited by hand" — but the guard did not deliver it.

UPDATING is derived purely from `[ -x "$BIN_DIR/pilotctl" ]`, i.e. whether the
BINARY exists. It says nothing about the config. So a host with a hand-edited
~/.pilot/config.json but no binary took the "fresh install" branch and had its
config silently overwritten:

  - binaries removed for a clean reinstall, config deliberately kept
  - config restored from backup before reinstalling
  - config pre-seeded before a first install

Same class of bug as the rest of this installer's recent history: the stated
intent was right, the condition tested the wrong thing, and the failure was
silent — no warning, no backup, no diff.

It also made consent settings unreachable before first start. Pre-seeding
{"consent":{...}} or {"skill_inject":{"mode":"disabled"}} was erased by this
write, and the erase happened BEFORE the first `pilotctl skills check` pass
further down — so the documented opt-outs could only be applied after the
first injection had already run. Guarding on the file itself makes them
reachable at install time.

DEFAULTS ARE UNCHANGED. This does not alter skill-injection mode,
auto-updates, telemetry, or any other default: a host with no config still
gets exactly the same config it got before. The only behavioural change is
that an existing file is left alone instead of being destroyed.

Verified in fresh debian:12 containers both ways: a pre-seeded config with a
custom registry, telemetry disabled and skill_inject disabled survives the
install byte-for-byte (exit 0); and a true fresh install with no config still
writes the standard one and yields a working `pilotctl version` (v1.13.7).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@TeoSlayer
TeoSlayer merged commit 16aa221 into main Jul 29, 2026
@TeoSlayer
TeoSlayer deleted the fix/preserve-existing-config branch July 29, 2026 11:46
TeoSlayer added a commit to pilot-protocol/website that referenced this pull request Jul 29, 2026
The hard drift check added to sync-install-sh.yml fires on this — as designed.

pilot-protocol/release#44 landed ("don't clobber an existing config.json on a
fresh install"), moving canonical from 906deea1 to c9d11c8c. R2 (the live path)
picked it up automatically via repository_dispatch, so the live URL already
serves c9d11c8c and users were never affected. This shadowed fallback copy is
the one that lagged.

public/install.sh is now byte-identical to release@main (c9d11c8c…).

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 29, 2026
The canonical-drift job went red on main again — working exactly as intended.

pilot-protocol/release#44 landed ("don't clobber an existing config.json on a
fresh install"), moving canonical from 906deea1 to c9d11c8c, while this repo's
copy stayed behind.

The live URL already serves c9d11c8c — R2 syncs from the release repo
automatically — so users were never affected. Only the in-repo copy was stale,
which is precisely the gap the drift job exists to catch.

install.sh is now byte-identical to release@main (c9d11c8c…).

Co-authored-by: Teodor Calin <teodor@vulturelabs.io>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants