Repository navigation
Plugin manifest v3: http block, derived requires, gate-aware install refusal - #1732
michielbdejong wants to merge 1 commit into
Conversation
…1712) Manifest schemaVersion 3 adds an optional `http` block (routes, well-known claims, write targets, keys, tokens, listeners, sidecars), validated in every wasm-plugins build, not only behind plugin-routes, so a node without the feature can say precisely why it refuses a release. The block is left out of the serialization when empty; the release ids of every accepted v1/v2 fixture are pinned in release-ids.json. From the block the host computes the gate a release needs (none, read-only, read-write, plus listener and sidecar names) and derives the `requires` list (public-origin, plugin-routes:<level>, operator-listener:<name>, operator-sidecar:<name>, ...), which /plugin-catalog now carries per entry. Installation activation (install and upgrade) and /plugin-release-pin compare the release with the node's gates from #1711 and refuse with the messages of the design's 0.4. The pin endpoint answers 409 with the typed problem host-feature-unavailable; @tomic/lib throws it as HostFeatureUnavailableError. A refused upgrade is a refused commit, so the old release keeps running. @tomic/lib mirrors validation, gate, requires and the refusal; both sides run the shared fixtures http-index.json and http-refusals.json. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Merge-risk triage (2026-09-28). This is a read-only review of this PR's own diff. The rule it applies: nothing that works today may break for existing users or clients, and new behaviour is added alongside the old. The stack is being rebased onto Verdict: SAFE Manifest v3 is additive. 🤖 Generated with Claude Code |
|
Closing as part of the plugin-host stack cleanup (Q-101): not needed by atomic-plugins now. See the inventory at #1726 (comment). The branch is kept, and pin candidates are unaffected. Closed by Claude (VPS coordinator) on behalf of Michiel. |
Closes #1712. Stacked on #1726 (#1711); base is
claude/plugin-routes-gates.Design: atomic-plugins
docs/design/server-plugin-routes.md(ontola/atomic-plugins#108), sections 0.1, 0.4, 1, 2.2, D9, D12.What
schemaVersion: 3with an optionalhttpblock (mount,routes,wellKnown,writeTargets,keys,tokens,listeners,sidecars,reason). It lives in a new file,server/src/plugins/manifest_http.rs, mirrored bybrowser/lib/src/plugin-manifest-http.ts. TheManifeststruct only gains onehttpfield, so this should merge easily with Plugins: let a plugin accept a file and declare its destination (#1653) #1691 (accepts/destination) and atomic-proxy: URLs for plugins, resolved to the delegated connection on the configured proxy (#1700, answer 4) #1725 (proxy).wasm-plugins), not behindplugin-routes, so the refusal can be precise.httpblock is left out of the serialization.testdata/plugin-manifest/release-ids.jsonpins the release id of every accepted v1/v2 fixture. The ids were computed on the base branch before this change.{param}, a trailing{*rest}, no regex, no./...callerrequiresauth: atomic. Ondrive-prefix, onlyanonymousis allowed unlessauth: atomic.bearerrequirestokens.writesmust name write targets.enqueuesmust name declared write operations.[a-z0-9-]), key algorithms.listenersonly withworld: server-extension.httpblock in a v2 manifest is rejected.Manifest::gate()givesnone | read-only | read-write, the listener and sidecar names, and each surface with its level (design 0.1).requires(Manifest::requires()):host-credentials,wasm-sandbox,persistent-host,public-origin,plugin-routes:<level>,operator-listener:<name>,operator-sidecar:<name>. It is sorted, and each/plugin-catalogentry now carries it.config.plugin_routesfrom Gate plugin public surfaces at build time and at runtime (AS-01) #1726 before grants and before anything is materialized. This covers install and upgrade (a change ofrelease/releaseId/status). A refused upgrade is a refused commit, so the old release keeps running./plugin-release-pinanswers409 application/problem+jsonwith{ type: "host-feature-unavailable", feature, needed, compiled, level, surfaces, listeners, sidecars, status, title, detail }.@tomic/lib'spinPluginReleasethrows that as aHostFeatureUnavailableError. The lib also exportscheckHostFeatures(manifest.http, hostFeatures.pluginRoutes),httpGate,derivedRequiresandhostFeatureMessagefor Catalog and install review: gated plugins and public endpoints #1713. The messages match 0.4 word for word on both sides.Tests
http-index.json: 8 accepted cases, with the gate andrequireseach needs, and 32 rejected cases.http-refusals.json: 12 node/manifest cases, one or more per message: not compiled, level too low (bothoffandread-only), listener missing, sidecar missing, plus the allowed cases.accepted_fixtures_keep_their_release_ids.httpinstalls. An upgrade to the gated release is refused, and the storedreleaseIdand materialized meta stay unchanged./plugin-release-pinreturns 409 with the typed problem. A v3 manifest withouthttppins. The catalog showsrequires.cargo test -p atomic-server --lib: 292 passed, both with and without--features plugin-routes.cargo clippy --all-targets -D warnings: clean both ways.browser/libvitest: 1005 passed.browser/liblint and typecheck, anddata-browsertypecheck: clean.Design points I had to interpret
/plugin-release-pin, and the lib can compute it client-side fromhostFeatures.surfaceslists only what exceeds the node's level. Atread-only, it names only the read-write surfaces. For a missing listener or sidecar, it names only those.http.sidecars: [{name, reason}].{*rest}matches one or more segments. Overlap is checked per shared method, like the(host, method, pattern)registry key.GET/HEAD,anonymous,auth: none, and has no body, writes or enqueues. Any other route, and any write target, key, token, delivery, listener or sidecar, is read-write.persistent-hostfrom cron or query triggers is not derived, because triggers aren't in the manifest.wasm-sandboxis derived fromwasip2/1orentrypoints.run.rsa-sha256 | ed25519.timeoutMsis capped at 30000 and inlinemaxBodyBytesat 1 MiB (design 2.8).wasm-pluginsgate is kept.manifest.rswas already behindwasm-plugins, and a build without it can't install plugins at all.unknown-version.jsonmoved fromschemaVersion: 3to4, since 3 is now valid. The same applies to oneapp-package.test.tscase.No UI changes (#1713 is out of scope).
🤖 Generated with Claude Code