feat(appstore): pilotctl install --local for sideloaded apps#240
Merged
Conversation
Pairs with pilot-protocol/app-store#15. Local-path installs now require an explicit --local flag and produce a `.sideloaded` marker in the install dir; the supervisor uses that marker to skip publisher-signature verification and enforce the sideload allow-list. Behaviour changes: - `pilotctl appstore install <path>` → refused - `pilotctl appstore install <path> --local` → sideload - `pilotctl appstore install <id>` (catalogue ID) → unchanged When --local is passed: - manifest.EnforceSideloadPolicy(m) runs before staging; any grant outside the allow-list refuses the install up-front so users get a clear error instead of a silent supervisor-skip after the next rescan - `.sideloaded` is planted in the staging dir as mode 0o400 BEFORE the atomic rename, so there is no window where the dir appears under InstallRoot without the marker - install output prints the honest "manifest gate, not OS sandbox" caveat so users know what `--local` does and does not protect `appstore list` surfaces a `[sideloaded]` badge on the version line and adds the `sideloaded` field to the JSON output. A small internal refactor: resolveInstallTarget now returns an installSource tag (catalogue|local) so the install command can branch on trust regime without re-doing catalogue lookup. Test updates: - validManifestJSON now uses fs.read $APP/data instead of /tmp/data so the helper-built bundles satisfy the sideload policy and can be used in both the catalogue and sideload test paths - install/duplicate/text-mode tests pass --local since they install from a local bundle directory - SIDELOADED warning text is asserted in the text-mode install test so removing the warning would surface as a regression Bumps the app-store dep to the feat/sideload-policy SHA. Will rebase the version pin once that PR merges and a tagged release lands.
Splits the install command into two lines in the appstore help — catalogue install vs sideload — so users discover --local from `pilotctl appstore` rather than via the trial-and-error of a local-path install that gets refused.
Two pre-existing bugs were masking each other in scripts/gen-cli-reference.sh: 1. \`pilotctl --help\` writes its banner to stderr (so that the same text shows for \`pilotctl <bad-flag>\`). The script only redirected stdout, so the help block was always empty. 2. \`pilotctl --help\` exits with code 2 by Go's flag convention. Under \`set -euo pipefail\` that aborted the whole script before the diff step ran. Net effect: the workflow never modified docs/cli-reference.md, so the \`git diff --exit-code\` step trivially passed. Any PR that touches \`cmd/pilotctl/**\` triggered the gen step, hit the same exit-2 abort, and \"passed\" for the wrong reason. PR #237/#238/#239 (catalogue-only bumps) skipped the gate entirely because they don't match the path filter, but the run history on main shows the check has been failing silently on every code change since the workflow was added. Fix: - 2>&1 so stderr lands in the doc - `|| true` so a non-zero exit doesn't abort the script No content change to docs/cli-reference.md — the current committed version already matches what the fixed script produces.
4 tasks
Bumps github.com/pilot-protocol/app-store from the feat/sideload-policy SHA to the post-merge main SHA (#15 squash-merged as 8852c785). No code changes — the manifest API + supervisor scan code that ships in v1.0.1-beta.1.0.20260609061942-8852c785a264 is byte-equivalent to what was on the feat branch.
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.
Summary
Pairs with pilot-protocol/app-store#15. Adds the user-facing path for installing apps that did not come through the signed catalogue.
pilotctl appstore install <path>→ refused with a hint telling the user about--localand what it does/doesn't protect.pilotctl appstore install <path> --local→ installs as a sideload: runsmanifest.EnforceSideloadPolicy(m)at install time, plants.sideloaded(mode0o400) in the staged dir before the atomic rename, prints the honest "manifest gate, not OS sandbox" caveat.pilotctl appstore install <id>(catalogue ID) → unchanged.The internal refactor:
resolveInstallTargetnow returns aninstallSourcetag (catalogue|local) so the install command can branch on trust regime without re-running the catalogue lookup.appstore listshows a[sideloaded]badge on the version line and asideloadedfield in JSON output.Test plan
go test ./cmd/pilotctl -count=1 -short— green--local(they install from local bundle dirs); the text-mode test now asserts theSIDELOADEDwarning is present, so removing that caveat surfaces as a regression--local, refused without (caught byos.Exit(1)); policy violation (addednet.dial) refused at install time with the offending cap namedvalidManifestJSONmove from/tmp/data→\$APP/datadoesn't break any downstream test I missed (full pilotctl suite passes locally)Dep bump
Go.mod pin moves to the SHA on
app-store#15's feat branch. Will rebase the pin to a tagged release once that PR merges.