Skip to content

fix(os-update): version floor + data-migration rollback guard - #863

Merged
VijitSingh97 merged 1 commit into
develop-v2from
fix/851-856-update-safety
Aug 3, 2026
Merged

fix(os-update): version floor + data-migration rollback guard#863
VijitSingh97 merged 1 commit into
develop-v2from
fix/851-856-update-safety

Conversation

@VijitSingh97

@VijitSingh97 VijitSingh97 commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

What

Two related update-safety gaps, one shared manifest mechanism.

#856 — signed downgrade. pithead os-update had no version floor: any
correctly-signed older bundle installed, health-passed, and committed — a
signed downgrade that re-opens the holes the newer image fixed, under our own
valid signature, so no signature check catches it.

#851 — migration-deadlock rule. The data_migration / minimum_os_version
manifest compatibility fields were unimplemented (dual-distribution-plan.md
risk #6). A forward-only /data (lmdb) migration left no record, so a later
rollback could strand the migrated chain data on an OS that can no longer read it.

Change

The bundle manifest's [meta.pithead] section grows the channel contract,
validated up front in mkbundle.sh (before the multi-minute image build):

field meaning
version the OS version this bundle carries (from VERSION)
data_migration true if this release runs a forward-only /data migration
minimum_os_version lowest OS version that can still read /data once it has migrated

os-update reads them back and:

Deliberate A/B rollback (rauc status mark-bad booted) is unaffected — it boots
the spare slot already present, it does not re-install an old bundle.

Scoped follow-up (explicit)

Recording and enforcing the floor is done and tested. Executing the
forward-only migration, and withholding chain services (monerod/tari) until the
slot commits — the automatic-fallback half of the deadlock rule — remain scoped
follow-up, documented in appliance-release.md and appliance-wizard.md. Until
that boot-path change lands, the guard here prevents the manual rollback path
from stranding data; the automatic-fallback ordering is bench/tier-4 work
(tests/os/run.sh --phase update). The plan's db_schema field lands with that
runner — nothing reads it yet.

Tests

tests/stack/run.sh: os_bundle_meta parsing of all three fields; the downgrade
refusal + --allow-downgrade override; the below-floor hard refusal (no
override); the floor being recorded on a data_migration install and not on a
plain one; mkbundle.sh's fail-fast metadata validation. Full suite: 1996
passed, 0 failed. make lint green (shellcheck, shfmt, docs-voice, operator-strings).

Closes #856
Refs #851 — the safety guard + manifest fields are done and tested here; the
migration runner and the automatic-fallback boot ordering are scoped follow-up,
so this deliberately does not auto-close #851.

🤖 Generated with Claude Code

A correctly-signed OS bundle is not automatically a safe one. Two gaps:

- os-update had no version floor: a signed OLDER bundle installed,
  health-passed, and committed — a signed downgrade that re-opens the
  holes the newer image fixed, under our own valid signature.
- The migration-deadlock rule was unimplemented: no manifest
  compatibility fields, so a forward-only /data migration left no
  record and a later rollback could strand the migrated chain data.

Stamp version + data_migration + minimum_os_version into the bundle
manifest's [meta.pithead] section (validated up front in mkbundle.sh,
before the multi-minute image build). os-update reads them back and:

- installs without --allow-downgrade ONLY a clean X.Y.Z release at or
  newer than the running OS. Both guards fail CLOSED: an older bundle, a
  pre-release/-prep version, garbage, or an absent stamp is not proof of
  safety, so it refuses rather than silently skipping the check.
- records a /data migration floor when a data_migration bundle installs,
  and refuses any later rollback below it outright (no override). A
  corrupt floor file, or a bundle whose version can't be parsed, also
  refuses — a floor that exists is authoritative.

semver_newer only understands clean X.Y.Z, so os_semver_ok gates every
comparison; a version it can't parse never reaches the buggy path.

Executing the forward-only migration itself, and withholding chain
services until the slot commits (the automatic-fallback half of the
deadlock rule), remain scoped follow-up — documented in
appliance-release.md and appliance-wizard.md. The manual-rollback guard
is real, tested, and armed now.

Closes #856
Refs #851

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@VijitSingh97
VijitSingh97 force-pushed the fix/851-856-update-safety branch from 94533ac to 59a163a Compare August 3, 2026 01:46
@VijitSingh97

Copy link
Copy Markdown
Collaborator Author

Adversarial-review round (force-pushed, amended into the single commit):

  1. semver_newer pre-release bypass → fail-closed. semver_newer's awk parses 1.17.0-prep field 0-prep as 0, so a pre-release bundle compared equal to a release and slipped the guard. os_semver_ok already rejects -prep/garbage; the fix is that the downgrade and floor guards now refuse when os_semver_ok is false (older, pre-release, garbage, or absent stamp) rather than silently skipping — --allow-downgrade required to install one on purpose. The version-compare path only ever sees clean X.Y.Z.
  2. Corrupt floor file → fail-closed. A .os-data-floor that exists but holds garbage made os_semver_ok $floor false and short-circuited the whole guard, letting the downgrade through. Now a floor file that exists is authoritative: an unparseable floor, or a bundle not provably at/above it, refuses outright (no --allow-downgrade override).
  3. Trailer. Closes #851Refs #851 (commit body + PR) — the migration runner / chain-service-withholding half is scoped follow-up, so Appliance: migration-deadlock rule (data_migration flag) and manifest compatibility fields not implemented #851 stays open.

Tests added: a -prep bundle exercises the fail-closed refusal (+ --allow-downgrade override); a corrupt floor file exercises the fail-closed refusal. Full suite 2004 passed / 0 failed; make lint green. Did not touch the IPv6/watchdog files.

@VijitSingh97
VijitSingh97 merged commit 031e237 into develop-v2 Aug 3, 2026
16 checks passed
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.

1 participant