fix(metadata-protocol): allowRuntimeCreate:false 在每一种 kernel 上都生效 —— PUT /meta 不再创建注册表声明为 code-only 的 job / agent (#5086) - #5263
Merged
Conversation
…PUT /meta no longer creates code-only job/agent items (#5086) The ADR-0005 two-tier authorization gate sat behind `environmentId !== undefined`. `environmentId` is a row-scoping key, not an authorization signal: a host config with instantiated plugins skips the standalone stack and boots `new ObjectQLPlugin()` with no environmentId, so the showcase — and every self-hosted app server shaped like it — ran with the gate disengaged. A `job` whose `handler` names no compiled function persisted and answered 200. A type declaring BOTH `allowRuntimeCreate: false` and `allowOrgOverride: false` has no runtime write channel at all, in any topology. That verdict is now taken from the registry declaration alone, before persistence, for draft and publish saves alike, and returns the catalogued NOT_CREATABLE / NOT_OVERRIDABLE 403 naming the type, the flags, the source file pattern to declare it in and the OS_METADATA_WRITABLE escape hatch. The overlay whitelist keeps its single-kernel carve-out (ADR-0005 §"Whitelist enforcement" predates `allowRuntimeCreate` and speaks only of that list), and `deleteMetaItem` stays permissive so a code-only row that predates this refusal can still be removed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pbu27iNUfQCHeuS551Rqo7
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #5086
结论先说:门是有的,只是挂在了一个错误的条件上
saveMetaItem里 ADR-0005 的两级授权门(not_overridable/not_creatable)一直存在,也一直是对的 —— 但它整个包在if (this.environmentId !== undefined)里:environmentId是行作用域键(给sys_metadata打environment_id),不是授权信号。凡是没有它的 kernel,这道门整个不跑。而这并不是什么冷门拓扑:objectstack.config.ts里带了已实例化插件(showcase 就是)⇒isHostConfig为真 ⇒shouldBootWithLibrary为假 ⇒createStandaloneStack那条分支被跳过;packages/cli/src/commands/serve.ts的自动注册:new ObjectQLPlugin(),不带 environmentId;PUT /api/v1/meta/*面向的是最终用户 —— 整个 ADR-0005 授权门是关着的。issue 的复现正是在 "a real showcase boot" 上做的(#5145),所以拿到 200。
补一条实测出来的、比推理更有说服力的细节:这个洞恰好只对"没有写通道的类型"张开。
hook这类allowRuntimeCreate: true的类型走 repository 写路径,SysMetadataRepository.assertAllowed()在同一台无 environmentId 的 kernel 上照样 403(实测见下);而job/agent因为两个标志都是 false,useRepoPath为假,落到了那条 legacy raw-engine 分支 —— 那条分支没有任何门。声明得最严的两个类型,恰恰是唯一能穿过去的两个。改了什么
注册表条目同时声明
allowRuntimeCreate: false与allowOrgOverride: false的类型,等于声明"没有任何运行时写通道"。这个判断只取决于类型声明,与部署拓扑无关,所以它被提到environmentId判断之外,在落库前、draft 与 publish 一视同仁地拒绝:PUT /meta/job/*(单 kernel 宿主)200 success403 NOT_CREATABLEPUT /meta/agent/*(单 kernel 宿主)200 success403 NOT_CREATABLE200 success403 NOT_OVERRIDABLE403403(不变)拒绝语句点名类型、点名产生该判决的两个标志、给出该类型自己注册表条目里的
filePatterns作为处方,并说明OS_METADATA_WRITABLE逃生舱 —— 处方从注册表读,新类型被打上标志的当天就有正确提示,这里没有第二份需要同步的表(Prime Directive #8)。落点为什么是
packages/metadata-protocol/src/protocol.ts而不是packages/runtime/src/domains/meta.ts:两个 HTTP 面(rest-server.ts与 runtime dispatcher)都只是转调protocol.saveMetaItem,migrateStoredMetadata/duplicatePackage也从内部重入同一个方法。门放在这里,才是"每个 surface 都必然经过"的那一处。边界:哪些没有改,以及为什么
projectId) keep their existing behaviour (any type writable)" 写的是 overlay 白名单(当时代码里只有OVERLAY_ALLOWED_TYPES = {view, dashboard}),allowRuntimeCreate那时还不存在。所以仍然可运行时创建的类型(object/hook/field/seed/mapping…)在单 kernel 上行为完全不变 —— 这也是本 PR 刻意不去动的部分:把 artifact-backed 的not_overridable一并放开,会让每个自托管部署的"改包内 object"当场 403,那是另一个量级的合同变更,不该搭车。supportsOverlay判不了,所以没有假装判。 spec 自己的 TSDoc 写得很清楚:supportsOverlay描述的是 能力(loader 能否合并 overlay),allowOrgOverride才是 许可,并且明说 "a write targets a type with this flag unset → 403not_overridable"。而且object/flow/action/seed/hook都是supportsOverlay: false却按设计可运行时写 —— 把它当写入门会把这些类型全部拒掉。issue 里提到的"给supportsOverlay: false的行回一句 customization overlay"是文案问题,已另开 保存成功的回执一律自称 "customization overlay",包括注册表声明 supportsOverlay:false 的类型(object / flow / action / seed / hook) #5265。allowOrgOverride的 org 维度按 ADR-0005 PR-10d.7 两级模型不动。 "无 artifact 的新建只需要allowRuntimeCreate" 是被 ADR 记录且被测试钉住的决定,不在本 issue 范围内推翻。deleteMetaItem不同步收紧。 删除一条本不该存在的 code-only 行是修复,必须保持可行 —— 这次 bug 已经在真实环境里造出过这种行。实测确认删除侧没有对应的洞:非 code-only 类型走 repository,assertAllowed()照常拦;code-only 类型的 legacy 分支只会删sys_metadata里的行,删不到 artifact。验证
单测(新增 22 条,数据驱动:code-only 集合从
DEFAULT_METADATA_TYPE_REGISTRY推导,新类型被打标志时缺 probe 会直接红)+ 真实 showcase boot 实测(pnpm dev -- --fresh -p 39517,issue 原样 payload):套件:
回归性:把
protocol.tsstash 掉重跑新测试 →10 failed | 12 passed,恢复后22 passed。packages/objectql/src/overlay-precedence.test.ts里那条 "single-kernel mode — gate bypassed" 的标本从agent换成了hook:它钉的决定是"overlay 白名单在单 kernel 上不生效",agent只是当时挑的最戏剧化的标本;换成hook后那个决定原样保留,并在旁边补了一条 code-only 仍被拒的断言。顺带发现(已单独开 issue,未在本 PR 修)
saveMetaItem的 legacy raw-engine 分支在本 PR 之后已不可达(注释还宣称它为 control-plane 而存在);deleteMetaItem里结构对称的那条仍然可达且必要,两条并排最容易读错。supportsOverlay: false的类型;写路径其实已经算出了 artifact-backed / 新建 / org 三种事实,只是没说。packages/spec/**零改动;未触碰seed-loader.ts、metadata-manager.ts、#5079 的枚举失效面。🤖 Generated with Claude Code
https://claude.ai/code/session_01Pbu27iNUfQCHeuS551Rqo7