Skip to content

feat(installer): publish binary and assets as one atomic bundle - #268

Open
Alan-TheGentleman wants to merge 4 commits into
feat/release-artifact-syncfrom
feat/release-assets-install
Open

feat(installer): publish binary and assets as one atomic bundle#268
Alan-TheGentleman wants to merge 4 commits into
feat/release-artifact-syncfrom
feat/release-assets-install

Conversation

@Alan-TheGentleman

Copy link
Copy Markdown
Collaborator

Third slice of the consumer chain, stacked on #267. POSIX install path; Windows split provenance is the next slice.

The half-published bundle becomes unobservable, not just unlikely

recoverInterruptedPublication already takes the bundle-validity predicate as a parameter and is already called with existingSignedBundleMatches. Extending that one predicate to cover the assets tree makes a binary-without-assets bundle invalid by construction. No second publish operation, no new crash window.

Two tests prove it rather than describe it: an assets-less existing bundle is rejected for reuse and repaired by a fresh full install, and a crash simulated through a fake rename seam refuses to silently restore a backup missing its assets, failing closed and asking for intervention instead.

Path-set equality before digesting, again

Same reason as #266: a digest walk only ever revisits declared entries, so it cannot notice an added file. resolveGentleAiAssets asserts exact path-set equality against the manifest first, then per-file lstat, confinement and mode checks, then a whole-set sameFile TOCTOU recheck.

The integrity manifest is extended, never loosened

Five new keys join the existing exact key-count and string-equality discipline in isCanonicalManifest. Every asset file gets its own regular-non-symlink check, not just the binary. Symlink rejection, path confinement and TOCTOU detection are untouched.

Threat cases covered: an extra file in the installed tree, a symlinked asset, an asset with mode 0755, an asset named install.sh landing non-executable, TOCTOU replacement mid-verify across the whole file set plus the manifest, and a forged assets digest still rejected by resolveGentleAiBinary.

Where the pinned assets digest comes from

The lock, not a second hardcoded table. Reintroducing a hand-maintained table here would recreate exactly the drift that #262 removed for the binary version.

Tests

63/63 focused. Full suite 1083 pass, 3 fail — the known receipt-driven-development-disabled failures, verified identical on clean main before this chain started. pnpm run check:transaction-runner clean, runtime regenerated rather than hand-edited.

pnpm run test:harness fails in this sandbox, confirmed by stashing to reproduce identically on the unmodified parent branch. Pre-existing environment condition, same root cause.

Rollback

Revert the three source files and two test files to the parent tip and delete the new fixture helper. No file from the earlier or later slices is touched.

…le atomically

Extend the installer's binary-only bundle into one atomic binary+assets
bundle (design D3/D4): resolveGentleAiAssetsArchive reads the pinned
archive identity from the canonical release lock instead of a second
hand-maintained digest table, installAssets stages the signed assets
archive into <staging>/assets via the existing bounded release-artifact
extractor, and assetsBundleMatches extends the one existing bundle-validity
predicate recoverInterruptedPublication already takes as a parameter -- so
a binary-without-assets bundle is invalid by construction, with no new
publish operation and interrupted-publication recovery covered for free.
…leAiAssets

Add the assets provenance keys (assetsAsset, assetsArchiveSha256,
assetsTreeSha256, contractMajor, layoutVersion) to the runtime integrity
manifest without weakening isCanonicalManifest's exact-key-count/
string-equality discipline, so a missing or forged assets field fails
binary resolution exactly like a missing or forged binary field always
has. Export resolveGentleAiAssets(packageRoot, platform) for lazy
snapshot readers: exact path-set equality against the manifest entries
first (an added file a tree digest alone could never catch), then
per-file lstat/confinement/mode checks, then a whole-set sameFile TOCTOU
recheck across the manifest and every entry file.
@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0e55b573-d2e3-4479-afae-1fdb3224fc39

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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