Skip to content

feat(apps): reconcile managed apps from the fleet desired state - #456

Open
Alexgodoroja wants to merge 1 commit into
mainfrom
build/managed-app-reconcile
Open

feat(apps): reconcile managed apps from the fleet desired state#456
Alexgodoroja wants to merge 1 commit into
mainfrom
build/managed-app-reconcile

Conversation

@Alexgodoroja

Copy link
Copy Markdown
Collaborator

The node half of the fleet App Store. Pairs with
pilot-protocol/platform#61 — that PR ships the console and the desired-state
contract; without this one a managed node never acts on a queued install and
the store shows installing indefinitely.

How it works

Nodes converge their installed apps toward a desired-set document the authority
writes into the fleet state mirror. The document arrives as an ordinary signed,
revision-fenced state mutation, so managed app install adds no new command
kind, endpoint, or inbound channel.

The grant boundary

Grants exist only inside a signed bundle, never in the catalogue. So a node
installs an unreviewed app into a staging root the supervisor does not scan:
the binary is present, its manifest can be read and reported, and it cannot
run. Promotion into the live install root happens only once the desired
document accepts every grant the manifest declares — and a catalogue republish
that widens them demotes the app rather than silently keeping the wider set.

Installation

Shells out to the pilotctl beside the daemon. pilotctl owns the only
implementation of the catalogue trust chain — publisher signature, per-platform
bundle pin, sha256 verification, sideload clamping — and a second, subtly
different verifier for a security boundary is the one thing that must not
exist. AppInstaller keeps that swappable if the path is later extracted into
a library.

Inventory

Republished only when it actually changes. The report lives in the state
mirror, so a ticking timestamp would churn the revision the console fences its
mutations on and invalidate an operator's in-flight install.

Verification

gofmt, go build ./... and go test ./internal/enterprisecontrol/ are
clean. Eleven tests cover the reconciler: staging on unreviewed grants,
promotion on acceptance, demotion when grants widen, partial-acceptance
refusal, removal, leaving hand-installed apps alone, install failure, a
document addressed to another node, and the revision-churn guard.

Not yet exercised against a live daemon end to end — deploy/dev-mirror/connect-node.sh
in the platform PR is the path for that.

Nodes converge their installed apps toward a desired-set document the
authority writes into the fleet state mirror. The document arrives as an
ordinary signed, revision-fenced state mutation, so managed app install
adds no new command kind, endpoint, or inbound channel.

The two-root design is the grant boundary. Grants exist only inside a
signed bundle, never in the catalogue, so a node installs an unreviewed
app into a staging root the supervisor does not scan: the binary is
present, its manifest can be read and reported, and it cannot run.
Promotion into the live install root happens only once the desired
document accepts every grant the manifest declares -- and a catalogue
republish that widens them demotes the app rather than silently keeping
the wider capability set.

Installation itself shells out to the pilotctl beside the daemon.
pilotctl owns the only implementation of the catalogue trust chain, and
a second verifier for a security boundary is the one thing that must not
exist; AppInstaller keeps that swappable if the path is later extracted.

The inventory is republished only when it actually changes: it lives in
the state mirror, so a ticking timestamp would churn the revision the
console fences its mutations on and invalidate in-flight installs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
defer cancel()
// --force lets an install replace a wrong-version copy in place; the
// catalogue signature and sha256 gates still run either way.
command := exec.CommandContext(ctx, installer.BinaryPath, "appstore", "install", appID, "--force")
}
ctx, cancel := context.WithTimeout(ctx, installer.timeout())
defer cancel()
command := exec.CommandContext(ctx, installer.BinaryPath, "appstore", "uninstall", appID, "--yes")
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.

2 participants