You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(appstore): pilotctl install --local for sideloaded apps (#240)
* feat(appstore): pilotctl install --local for sideloaded apps
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.
* docs(appstore): show --local in install help
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.
* fix(ci): cli-reference-check captures stderr and tolerates --help exit 2
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.
* deps: pin app-store to post-merge main SHA
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.
---------
Co-authored-by: Teodor Calin <teodor@vulturelabs.io>
"the argument must be either a catalogue ID (`pilotctl appstore catalogue` to list) or a path to a bundle dir containing manifest.json",
998
1015
"%v", err)
999
1016
}
1017
+
ifsource==installSourceLocal&&!allowLocal {
1018
+
fatalHint("invalid_argument",
1019
+
"local sideloads carry no catalogue signature; pass --local to confirm you trust this bundle's source. The supervisor will clamp the manifest to a small allow-list (fs.read/fs.write under $APP, audit.log). No net.dial, key.sign, ipc.call to other apps, or daemon hooks.",
1020
+
"refusing to install local path %q without --local", target)
1021
+
}
1000
1022
1001
1023
// 1. Validate the bundle — same shape as verify. Reusing the
1002
1024
// surface manifest.Parse + sha256File makes the trust check
"sideloaded apps may only declare audit.log, fs.read $APP/*, fs.write $APP/*. Remove the offending grant or use the catalogue install path for a reviewed app.",
return"", fmt.Errorf("not a catalogue ID or a bundle dir: %q (try `pilotctl appstore catalogue` to list installable apps)", target)
179
+
return"", installSourceLocal, fmt.Errorf("not a catalogue ID or a bundle dir: %q (try `pilotctl appstore catalogue` to list installable apps)", target)
160
180
}
161
181
162
182
// fetchAndUnpackBundle downloads the catalogue entry's tarball,
0 commit comments