Skip to content

spec: declare grantedPermissions on EnvironmentArtifactSchema (record of manifest id → PluginPermissions) — the artifact-contract half of #14034 / #11333 Phase 1, which must land BEFORE cloud can write it (plain z.object strips undeclared keys at the artifact door) #14865

Description

@baozhoutao

Filed by the repo:cloud execution seat (seat post #6026, session session_017Wu48ZSjk7HJjyJmGNBtUk, R35) as the framework half that #14034's measurement (comment on that card, 2026-09-03) showed must land first. Triage: domain:* and type are yours — expected domain:spec; Clause-② yes (a new key on a published contract face) ⇒ CONTRACT_REVIEW_TIER. Dedup: no open objectstack card declares a granted-permissions field on the environment artifact (granted across packages/spec/src returns zero permission declarations at pin 655b106c; the requested set is manifest.zod.ts:290-291).

Ruling this executes

#11333 option A (2026-08-30 「同意」) and the #13457 batch ruling (2026-09-01 「同意」): the consented four-class permission set {services, hooks, network, fs} rides the plugin artifact contract; cloud (owner of the install-consent flow) writes it at consent-compile time, objectstack's loader consumes it from the environment-local carrier at materialize time (ADR-0003 / cloud ADR-0007: sys_package_installation is never read on a runtime path).

Measured on cloud 2f469233 × pin 655b106c (details on #14034)

  • The consent flow writes the granted set to exactly one place: sys_package_installation.granted_permissions (cloud routes/package-install.ts:640-652, written :667/:678), preserving absent vs empty (:651-653 emits it only when consent-bearing).
  • The compiled environment artifact envelope (cloud routes/cloud.ts:747-757: schemaVersion, environmentId, commitId, checksum, metadata, functions, manifest, builtAt, builtWith, runtime) does not carry it; the marketplace-install merge loop reads the install row but consumes only enabled / package_version_id / with_sample_data.
  • The env-local carrier (env-artifact-cache.ts:88-102) stores JSON.stringify(artifact) verbatim ⇒ once the envelope carries the key, the carrier carries it for free — the ruling's carrier assumption holds.
  • The consumer at the pin wants a per-plugin map: registerGrantedPermissions(pluginName, PluginPermissions) (packages/core/src/security/plugin-permission-enforcer.ts:102-111), value shape PluginPermissionsSchema (packages/spec/src/kernel/manifest.zod.ts:32-44, .strict()).
  • Pin-lag / silent-stripping: EnvironmentArtifactSchema (packages/spec/src/system/environment-artifact.zod.ts:83) and ManifestSchema (manifest.zod.ts:158) are plain z.object, so an undeclared key written by cloud is stripped at objectstack's artifact door (packages/metadata/src/plugin.ts:900-925, parse at :908/:915). A cloud-first landing would fail silently. ⇒ spec first, pin bump, cloud second.

The ask (spec half)

Declare one top-level envelope key on EnvironmentArtifactSchema:

grantedPermissions: z.record(z.string(), PluginPermissionsSchema).optional()

Coupling

Refs: #14034 · #11333 · #13457 · #13458 · ADR-0003 / cloud ADR-0007 · ADR-0025.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions