Skip to content

Commit c1078a5

Browse files
hotlongclaude
andauthored
docs(adr-0025): the granted permission set is REGISTERED at load and enforces nothing — §3.7 said the opposite (#17147) (#17756)
## ⛔ Governed surface — stays DRAFT for maintainer merge `docs/adr/**` is a governed surface (Prime Directive #14). This PR is **not** queued, **not** armed for auto-merge, and is **not** flipped out of draft. It is split out of **#17753** precisely so that PR's code / spec / test / generated-docs half can land the ordinary way. ⛔ #17147 stays OPEN — this PR is not its closer either. See #17753 for why the card stays open. > ⚠️ Worded this way deliberately. The earlier phrasing put a closing keyword immediately before the number, and GitHub's reference parser matches the keyword plus the number and ignores the negation around it — so on #17753's merge the card was auto-closed as COMPLETED (reopened since; see that PR's body). ## ⚠️ CI state before you merge — one red remains, and it is advisory | check | state | why | |---|---|---| | `Check Changeset` | **cleared** | This PR releases nothing (one file under `docs/adr/`), so the gate's own prescription applies: the `skip-changeset` label, applied live. ⛔ An empty-frontmatter changeset is explicitly not a third option (#5471). | | `Part-of PR must not also close its card` | **red, and staying red** | RULE 2 only: the single commit on this branch ends `Refs #17147`, and that rule forbids **any** card-relation trailer in a commit message. RULE 3 (the body half) is clear — the body carries no closing keyword, verified with the gate's own regex. | The Part-of red is **not** a required context (absent from the required-context registry; its workflow subscribes to no `merge_group` event), `Refs` lands as a reference and moves **no** card, and the gate's own log states that the repair is ⛔ never a history rewrite. So it is a red to read, not to act on. Nothing else is outstanding. ## The two corrections **§3.7 Permissions** carried the parenthetical *"(service/hook/file/network already enforced)"*. Measured on `9bd4344e4`: | | | |---|---| | persisted consent record + re-consent on a widening upgrade | ✅ live, §3.8 as written | | artifact carriage + `AppPlugin.init()` → `registerGrantedPermissions` | ✅ live (#13457) | | anything that **queries** the registry | ❌ `enforceServiceAccess` / `enforceHookTrigger` reachable only via `SecurePluginContext` (zero production construction sites); `enforceFileRead` / `enforceFileWrite` / `enforceNetworkRequest` called by **nothing at all** | The bullet now states that split and names §3.5 step 7's per-plugin context as the **materialize seam** ruling `5486840233` assigns to this ADR's own install-flow design work — tracked as #17147, deliberately not built here. **The Status line** is stale in the *other* direction: the 2026-07-16 audit says install-time consent is unimplemented, and it has since landed for package installs. Replaced with a 2026-09-12 audit that separates what landed (consent, carriage, registration) from what did not — no `os plugin install`, no `.osplugin` loader, and no runtime path on which a distributed plugin's code executes; an environment artifact carries `sys_package_version.manifest_json` and never the blob. ## Related - **#17753** — the framework half: four shipped sentences corrected, generated docs regenerated, and `granted-permissions-not-enforced.pin.test.ts`, which pins the measurement and **goes red the day the seam lands**. That pin's failure message names this note; delete the §3.7 block in the same PR. - **objectstack-ai/objectui#9235** — the console consent panel, which told the installer the same thing. - **#13458** — Phase 2 stays `Blocked-by: #17147`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent b59b749 commit c1078a5

1 file changed

Lines changed: 29 additions & 4 deletions

File tree

‎docs/adr/0025-plugin-package-distribution.md‎

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ADR-0025: Plugin Package Distribution (Code + Dependencies)
22

3-
**Status**: Proposed — partially implemented (2026-07-16 audit): the `.osplugin` artifact format and `os plugin build`/`sign`/`publish` CLI landed; the install flow (§3.5), `sys_plugin`/`sys_plugin_version`/`sys_plugin_installation` registry (§3.8), and install-time consent remain unimplemented.
3+
**Status**: Proposed — partially implemented (2026-09-12 audit, superseding 2026-07-16): the `.osplugin` artifact format and `os plugin build`/`sign`/`publish` CLI landed. **Install-time permission consent landed for PACKAGE installs** — the console disclosure panel, `sys_package_installation.granted_permissions`, re-consent on a widening upgrade, `EnvironmentArtifactSchema.grantedPermissions`, and `PluginPermissionEnforcer.registerGrantedPermissions` at load — but it is **registered-only and enforces nothing** (§3.7, #17147). The code-plugin half of the install flow (§3.5 steps 4–7: download / verify / materialize / load) and the `sys_plugin`/`sys_plugin_version`/`sys_plugin_installation` registry (§3.8) remain unimplemented: measured on `9bd4344e4` there is no `os plugin install` command, no `.osplugin` loader, and no runtime path on which a distributed plugin's code executes — an environment artifact carries `sys_package_version.manifest_json` and never the blob.
44
**Deciders**: ObjectStack Protocol Architects
55
**Builds on**: [ADR-0003](./0003-package-as-first-class-citizen.md) (package + versioned releases), [ADR-0004](./0004-cloud-multi-kernel.md) (cloud multi-kernel), [ADR-0010](./0010-metadata-protection-model.md) (L1/L2/L3 protection), [ADR-0016](./0016-studio-package-authoring-and-publish.md) (package authoring & publish, local export/import)
66
**Consumers**: `@objectstack/core` (kernel, plugin-loader, security), `@objectstack/runtime` (sandbox, marketplace install), `@objectstack/cli`, `@objectstack/spec/system` (ObjectStackManifest), `@objectstack/spec/cloud`, `../objectui` (Studio)
@@ -277,9 +277,34 @@ enforces this at publish time (an unverified publisher cannot ship `runtime:
277277
counter-signs on approval. Host ships trusted root keys; verify the chain at
278278
install (§3.5 step 4) **and** at load (§3.5 step 7).
279279
- **Permissions.** New manifest `permissions` block → install-time consent →
280-
granted set → `PluginPermissionEnforcer` (service/hook/file/network already
281-
enforced). Principle of least privilege; all denials logged (existing
282-
behavior).
280+
granted set → `PluginPermissionEnforcer`. Principle of least privilege; all
281+
denials logged.
282+
283+
> **Landed as far as REGISTRATION, and no further (2026-09-12, #17147).** The
284+
> parenthetical here used to read *"(service/hook/file/network already
285+
> enforced)"*. It was never true of the granted set, and two of the four
286+
> classes have no enforcement surface at all. Measured on `9bd4344e4`:
287+
>
288+
> - the consent record is persisted by the control plane
289+
> (`sys_package_installation.granted_permissions`) and re-consent is forced
290+
> on a widening upgrade — **live**, §3.8 as written;
291+
> - it reaches the runtime on `EnvironmentArtifactSchema.grantedPermissions`
292+
> and `AppPlugin.init()` hands each entry to
293+
> `PluginPermissionEnforcer.registerGrantedPermissions` — **live** (#13457);
294+
> - **nothing queries that registry.** `enforceServiceAccess` and
295+
> `enforceHookTrigger` are reachable only through `SecurePluginContext`,
296+
> which has zero production construction sites; `enforceFileRead`,
297+
> `enforceFileWrite` and `enforceNetworkRequest` are called by nothing at
298+
> all, `SecurePluginContext` included.
299+
>
300+
> ⇒ the granted set records what was consented to and **refuses no
301+
> operation**. Per-plugin context construction — §3.5 step 7's *"wraps
302+
> `PluginContext` with the enforcer scoped to the granted set"* — is the
303+
> materialize seam that maintainer ruling `5486840233` assigns to this ADR's
304+
> install-flow design work and forbids improvising elsewhere; it is tracked as
305+
> **#17147** and is not built. The measurement is pinned in
306+
> `packages/core/src/security/granted-permissions-not-enforced.pin.test.ts`,
307+
> which goes red the day it is — delete this note in that PR.
283308
- **Config.** RETIRED 2026-08-27 (#11982, ADR-0049 enforce-or-remove;
284309
maintainer ruling, decision-inbox batch 5). `PluginConfigValidator` /
285310
`createPluginConfigValidator` and `PluginMetadata.configSchema` were removed:

0 commit comments

Comments
 (0)