feat(installer): preserve managed install authority across updates - #288
hyperb1iss wants to merge 13 commits into
Conversation
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueComment |
Managed installation needs immutable releases in its data root and a persistent transaction journal in its state root. Add a split-root store that retains both directory identities and ancestry under one state lock, while preserving the existing single-root installer contract. Validate both roots through bootstrap and every later store operation. Reject substituted ancestry, changed permissions, overlapping roots and foreign locks before active-pointer or journal operations proceed. Co-Authored-By: Nova (GPT-6) <noreply@openai.com>
Permission bits cannot establish per-user installation ownership. Read owner identity from the retained file handle and require the effective user to own every bootstrapped installation directory. Cover replacement-resistant metadata and a foreign-owned directory with otherwise safe permissions before accepting filesystem authority.
Record installation identity and fixed XDG roots without reconstructing paths from later environment changes. Reject invalid contracts and root overlap before filesystem authority can be used for adoption. Validate directory ownership and physical ancestry under the existing install lock, including replacement that preserves a leaf inode. Keep locator publication and activation outside this topology checkpoint.
Persist exact legacy observations and the intended journal digest before creating managed transaction state. Revalidate the original platform and sync retained preparation files before publishing the managed locator. Reuse nonrollback file replacement so an ambiguous directory fsync never restores old authority. Require an explicit durability retry and reject unknown locators, unrelated orphan journals, and changed preparation.
…ation Managed authority handoff must bind the exact intended transaction before writing its journal. Extract the existing platform preparation path so callers can persist that binding before entering normal recovery. Reject pending transactions and foreign locks before preparation. Keep ordinary install behavior and the existing recovery driver unchanged.
Copied releases can share a digest while identifying different executable inodes. Bind the prior role through retained historical ancestry and keep candidate validation tied to the current store during owner proof. Reject copied or replaced prior authority and restore prior snapshot bytes from the original retained unit. Exercise same-ID different-inode proofs and native historical ancestry replacement without changing live services.
Treat the permanent locator as a hint until its identity, journal and retained roots are verified under the recorded state lock. Confirm the locator directory barrier before managed transitions and never bootstrap missing managed state or reacquire the historical lock after election. Recheck legacy hints under the historical lock to preserve old-then-state ordering across concurrent adoption. Refuse writable journals and locator files while allowing ordinary atomic journal replacement during recovery.
Installed trees have immutable modes that differ from extracted release payloads. Reuse the verified staging path with an explicit installed-source mode instead of changing permissions or moving the original release. Validate the source before and after copying, publish only within the new store filesystem, and verify any existing destination before reuse. Cover different-device copying and refusal of altered source or target.
Select historical prior authority from its exact persisted executable path, then validate the original installed tree and executable identity. Keep current-store and synthetic legacy roles on their existing validators. Share immutable binding proof with normal platform validation and validate the complete record after restoring its prior role. Cover copied-inode, foreign-path, manifest-mode and retained-ancestor refusal.
Restore directory write permission only inside the completed synthetic fixture so TempDir can remove immutable installed trees. Inspect entries without following symlinks and keep failed fixtures available for diagnosis.
Retain old then state locks while copying the original release and preparing recorded roots. Persist the exact initial journal before its state copy so interrupted preparation resumes the same transaction. Publish the permanent locator only after fresh prior-platform proof and durability checks. Cold recovery uses the existing coordinator and keeps the original inode and public paths available for rollback.
Elect recorded authority before staging and recover pending journals before attempting a new release. Preserve historical prior bindings only while rollback or interrupted adoption still requires the original unit. Restore recorded prior authority once during cold native construction. Exercise that constructor against retained original and copied releases, and preserve strict candidate validation before installer bootstrap.
a3addfe to
1201eea
Compare
3fcd441 to
8e7e235
Compare
The v0.5.1 release added a validate_only flag to InstallReleaseArgs and made user_skill_files mandatory for candidate manifests after this branch forked. The rebased legacy-authority fixture no longer compiled, and the binding fixture's release was rejected as missing the user skills tree. Both fixtures now match the shape the installer requires. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014466GJcZeEz2om6JDZ3Chm
|
Rebased onto main (the v0.5.1 release commit landed while this was open) and retargeted from the now-closed #286 to main. Two fixtures needed repair after the rebase: the legacy authority test gained the new validate_only flag, and the binding fixture's release now ships the user skills tree the v0.5.1 manifest requires. All 17 CLI test binaries pass locally after that (99 lib tests plus the integration suites). One thing is holding this in draft, and it is the gap the body already names. The new directory policy rejects any group-writable directory (adoption_roots.rs:72 and :81, location.rs:250, store.rs:505) and ensure.rs:123 refuses to normalize a pre-existing 0775 directory. The installer's own directories are fine because durable_ensure_child_directory sets an exact 0755. The problem is the directories Hypercolor creates outside the installer: hypercolor-cli config/mod.rs:126, hypercolor-core config/mod.rs:795, and daemon layout_store.rs:54 all use plain create_dir_all, so on an Ubuntu default umask of 002 they land at 0775. Any user who ran the daemon before running hypercolor install would then fail with InvalidOwner or Unprepared and have no repair path. The positive systemd fixture sidesteps this by chmodding ~/.local to 0700 first, and no test covers umask 002 roots. Two candidate fixes, either of which would let this merge: normalize uid-owned hypercolor leaf directories to 0755 as an explicit migration step before the policy check, and give those three create_dir_all sites an explicit mode so new installs never produce the shape. Leaving the call to Bliss rather than picking a security posture in a rebase pass. ~ via nova ⚡ |
What this changes
Linux installation can adopt managed authority with separate release and state roots, preserve the original service/release identity, and recover through a permanent versioned locator. CLI installation follows the recorded authority before preparing another candidate.
This layer targets
nova/product-experience-engine. The shared engine fixes belong to that preceding layer.Why
Update/recovery must retain one durable authority across interrupted adoption, rollback, and changes to the caller's XDG environment. The old journal location becomes a permanent locator only after the receipt and new journal are durable; an older CLI must refuse that authority instead of starting a competing installation.
Verification
just verifypasses locally (full current-head command pending; focused tests and strict Clippy passed)just denypasses (no dependency version/license change)just ui-testandjust ui-buildpass (not applicable; no UI source changes in this layer)just sdk-lint,just sdk-check, andjust sdk-buildpass (not applicable)just python-verifypasses (not applicable)just compat-checkpasses (not applicable)just docs-buildpasses (not applicable)cd docs && zola checkpasses (not applicable)just e2e-buildpasses with the normal Servo stack (full product artifact qualification is separate)just e2e-build-cpupasses (not applicable to the installer authority proof)just e2epasses against the Servo stack (full runtime journey pending)Notes for reviewers
The actual positive systemd proof uses explicit 0755 roots. Default app directories created under umask 002 are 0775 and correctly fail the current ownership policy. A workable secure default migration path remains unresolved; no permissions were silently changed and no authority check was weakened.
The old CLI can stage candidate files before refusing the versioned locator. The verified invariant is that it cannot replace the active pointers, locator, or service authority.
The generated qualification artifact combines this installer with retained daemon/UI/app inputs. It is not a new product release. Changed-XDG replay, actual cold rollback/recovery, supported uninstall, launcher hardening, and the separate activator remain pending. Keep the PR in draft.