@sema-agent/settings-schema 14.6.0 → 14.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +475 -0
- package/README.md +89 -5
- package/conformance/VERSION +1 -1
- package/conformance/cases/c84-sandbox-git-block-carried-verbatim/expected.json +153 -0
- package/conformance/cases/c84-sandbox-git-block-carried-verbatim/input.json +23 -0
- package/conformance/cases/c84-sandbox-git-block-carried-verbatim/meta.json +5 -0
- package/conformance/cases/c85-sandbox-git-refused-block-still-carried/expected.json +153 -0
- package/conformance/cases/c85-sandbox-git-refused-block-still-carried/input.json +23 -0
- package/conformance/cases/c85-sandbox-git-refused-block-still-carried/meta.json +5 -0
- package/dist/api/auth-bridge.d.ts +16 -2
- package/dist/api/auth-bridge.js +59 -24
- package/dist/api/credentials.d.ts +281 -0
- package/dist/api/credentials.js +149 -0
- package/dist/api/scopes.js +5 -1
- package/dist/atomic-write.d.ts +15 -0
- package/dist/atomic-write.js +109 -5
- package/dist/catalog-manages.d.ts +15 -0
- package/dist/catalog-manages.js +60 -0
- package/dist/center-refs.d.ts +68 -0
- package/dist/center-refs.js +70 -0
- package/dist/config-d-file.js +33 -15
- package/dist/config-dir-migration.js +1 -0
- package/dist/config-fns.js +147 -81
- package/dist/config-root-documents.d.ts +106 -0
- package/dist/config-root-documents.js +93 -0
- package/dist/config-root-node.d.ts +117 -0
- package/dist/config-root-node.js +260 -0
- package/dist/conformance/canonical.js +4 -4
- package/dist/conformance/contract-adapter.js +26 -1
- package/dist/conformance/corpus.js +4 -4
- package/dist/conformance/derive.js +2 -2
- package/dist/conformance/run.d.ts +3 -0
- package/dist/conformance/run.js +53 -12
- package/dist/conformance/temp-roots.d.ts +31 -0
- package/dist/conformance/temp-roots.js +184 -0
- package/dist/cross-session.d.ts +4 -4
- package/dist/declared-capture.d.ts +41 -0
- package/dist/declared-capture.js +344 -0
- package/dist/default-model.d.ts +1 -1
- package/dist/default-model.js +5 -4
- package/dist/env-model-table.d.ts +163 -0
- package/dist/env-model-table.js +257 -0
- package/dist/file-edit.js +11 -15
- package/dist/fleet.d.ts +36 -7
- package/dist/fleet.js +83 -32
- package/dist/hash.d.ts +0 -9
- package/dist/hash.js +64 -7
- package/dist/hooks-config.js +18 -3
- package/dist/http-hook-allowlists.d.ts +22 -9
- package/dist/http-hook-allowlists.js +169 -91
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/mcp-request-entry.d.ts +67 -13
- package/dist/mcp-request-entry.js +132 -93
- package/dist/mcp-server-name.d.ts +17 -0
- package/dist/mcp-server-name.js +19 -0
- package/dist/migration-receipt.d.ts +10 -6
- package/dist/migration-receipt.js +5 -3
- package/dist/node.d.ts +1 -0
- package/dist/node.js +2 -0
- package/dist/one-time-migrations.d.ts +4 -2
- package/dist/one-time-migrations.js +236 -96
- package/dist/own-key-read.d.ts +247 -0
- package/dist/own-key-read.js +449 -0
- package/dist/plain-json-object.d.ts +11 -1
- package/dist/plain-json-object.js +37 -1
- package/dist/remote-exec.js +9 -1
- package/dist/retired-form.d.ts +19 -0
- package/dist/retired-form.js +24 -1
- package/dist/safety-merge-spec.d.ts +6 -6
- package/dist/safety-merge-spec.js +50 -37
- package/dist/sandbox-git.d.ts +64 -0
- package/dist/sandbox-git.js +89 -0
- package/dist/scheduler-store.js +19 -11
- package/dist/settings-units.js +14 -12
- package/dist/tier-words.d.ts +33 -0
- package/dist/tier-words.js +43 -0
- package/dist/types.d.ts +12 -2
- package/dist/types.js +35 -6
- package/dist/url-shape.d.ts +16 -0
- package/dist/url-shape.js +18 -0
- package/dist/web-admin-credential.js +5 -2
- package/dist/web-admin-session-url.js +2 -2
- package/dist/worker-row-verdict.d.ts +11 -0
- package/dist/worker-row-verdict.js +58 -2
- package/dist/workers-rows.js +38 -10
- package/dist/zod-unwrap.d.ts +6 -0
- package/dist/zod-unwrap.js +14 -7
- package/docs/API-SURFACE.md +100 -5
- package/docs/CONSUMER-INTEGRATION.md +250 -2
- package/llms-full.txt +26 -4
- package/llms.txt +11 -4
- package/package.json +2 -2
- package/dist/definition-copy.d.ts +0 -23
- package/dist/definition-copy.js +0 -52
package/README.md
CHANGED
|
@@ -47,6 +47,37 @@ therefore shows only `zod`. Node built-ins only (`node:crypto`, `node:fs`).
|
|
|
47
47
|
> zod 3, nested inside every zod-4 host). The move to zod 4 did not change what any schema accepts or produces —
|
|
48
48
|
> the conformance corpus reads byte-identically on both — only the wording of validation errors (see CHANGELOG 11.0.0).
|
|
49
49
|
|
|
50
|
+
## 升级指引:14.7.x → 14.8.0(加性:配置根上的两份根级文档 · env 模型表的一份读法与写者的计划助手 · 一次性迁移 SS-M11)
|
|
51
|
+
|
|
52
|
+
- **新面**:配置根(持有 `config.d` 的那个目录)上多两份**根级文档** —— `config.env-models.json`(`{"envModels":"replaced"|"kept"}`:一份管模型面的
|
|
53
|
+
`config.d/models.json` 能不能顶替 env 模型表 `MODEL_ID` / `MODEL_CHEAP_ID`;运行记录在场之后缺席 ≡ `kept`)与 `config.migration.SS-M11.json`(一次性迁移 SS-M11 的运行记录,按 id 一份)。
|
|
54
|
+
读只经 `readEnvModelsDecision` / `readMigrationMarker`;决定文件的写只经 `writeEnvModelsDecision`(迁移铸确认也经它),运行记录由迁移独占创建(都在 `./node`);名字、闭集答案表与读数身份 `envModelsDecisionTag` 在包根。
|
|
55
|
+
这道闸的两样东西由本包出、供引擎读:组拒码常量 `CATALOG_REPLACES_ENV_MODELS_CODE`(`config.catalog_replaces_env_models`)与上面的决定文档;码的意思是 —— env 表已声明、决定不是 `replaced` 时,管模型面的目录被整组拒、env 模型照服。
|
|
56
|
+
强制在引擎一侧:今天引擎(`@sema-agent/core` 7.58.0)不拒这个组。
|
|
57
|
+
- **写者要做的**(改写 `config.d/models.json` 的每一条路):写之前读 `readEnvModelsDecision(root)`,调 `planEnvModelTableWrite({ env, target, decision, candidate, intent? })`;
|
|
58
|
+
`confirm` ⇒ 把回执给用户看(句子可用 `envModelTableWriteRefusal`),确认后带回 `confirmKey` 再调;`write_with_decision` ⇒ 先
|
|
59
|
+
`writeEnvModelsDecision(root, "replaced", { write: "overwrite" })`、再写 models.json(两次写在你对 models.json 的同一把锁里);`fix_first` ⇒ 先改文档。env 表未声明时计划照写、不写确认。
|
|
60
|
+
**不要自己写这份文件**:写函数给的模式与 `config.d` 域文档同一条规则(新建 0o644 经进程 umask,已有的文件沿用它原来的模式);自写在 umask 002 下落 0664,引擎的目录信任判官会拒。
|
|
61
|
+
撤销 = 删掉决定文件或写 `kept`(运行记录在场、创建者的迁移结局落定之后两者同义;落定之前只有写 `kept` 安全,见下一条)。撤销在下一次起服生效:运行中的引擎保留它在服的那一代目录,env 模型要到下一次起服才回来。
|
|
62
|
+
- **存量**:引擎首次以本地配置起服时跑一次 SS-M11(`migrateEnvModelsDecision`):运行记录缺席才跑;**先读**决定与 models.json,再以「不存在才建」写运行记录 ——
|
|
63
|
+
建成的那个进程是本根唯一的铸造者(别的副本见记录已在即停、永不铸),只在「先读到的决定缺席 ∧ 目录管模型面」时以「不存在才建」铸 `replaced`(env 表声没声明都一样 —— 保的是升级前目录整表顶替的读法)
|
|
64
|
+
(结局 `minted` / `exists` / `failed`,只有「已在」算 `exists`)。根缺席 ⇒ 逐级以恰是 0o700 的目录建根、只放运行记录;不建 `config.d`。记录建不成 / 确认建不成 / models.json 读不出(只有 `ENOENT` 算没有)⇒ 不铸(按 kept),
|
|
65
|
+
结局带 errno(若有)与手工恢复句;读不懂的运行记录按「已跑」处置。**披露**:进程恰在写完运行记录、铸确认之前中断,或铸失败、或那一刻 models.json 读不出 ⇒ 此后无人再铸,升级前由目录服务的根换成 env 模型服务 ——
|
|
66
|
+
出路是手写决定文件。撤销的终局按运行记录的创建者算:只有创建者能写确认;以运行记录在场为前提,创建者写完 / 判定不写 / 失败(结局 `minted` / `exists` / `skipped` / `models_unreadable` / `failed`,调用方各记一行)之后,删掉决定文件就是撤销且是终局(删掉运行记录再起服 ⇒ 迁移重跑,决定缺席即重铸);其它副本读到记录在场 = 已跑、跳过(或得 `EEXIST` 答 `not_minter`),它们的返回不构成终局;创建者当选到结局落定之间的窗口在挂起 / I/O 卡顿下无上界,窗口内唯一安全的撤销是写 `kept`(独占创建护住它;删掉的会被创建者照它先读的 D0 铸回);创建者在写确认之前崩溃 ⇒ 永久 `kept`(fail-closed,披露)。
|
|
67
|
+
- **MCP 条目**:`McpServerSpec` 多两只可选键,都解析透明、原样承运。`sessionMetaKey`(引擎在发给这台服务器的请求 `_meta` 里放发送腿会话 id 的那把键;唯一判官是引擎包根导出的 `judgeSessionMetaKey` —— 写声明的一方要早判就调它;存量里已写着这把键的条目(此前被剥掉并报 `unknown-keys-dropped`),本版原样交给引擎:值合引擎判官的,引擎从此按这把键放会话 id(运维当初写下的意图被兑现,行为随升级而变);值不合的(含 `null`),交给引擎后在准备门整条被拒、点名这把键:删掉这把键,或改成引擎收的键名)。
|
|
68
|
+
`startupTimeoutMs`(这一台服务器自己的启动界,整数毫秒,握手与首次工具列举各带它;缺席读进程级的缺省界):解析透明,**格式与界由引擎判** —— 唯一判官是引擎包根导出的 `mcpStartupTimeoutMs`,在拨号时判:不合 ⇒ 拒这一台服务器的拨号(其余服务器照挂),本包不判、任何 JSON 值原样过 —— 写声明的一方要早判就调它;存量里已写着这把键的条目(此前被剥掉并报 `unknown-keys-dropped`),本版原样交给引擎:值合引擎判官的,引擎从此按这把键给这一台服务器的启动设界(运维当初写下的意图被兑现,行为随升级而变);值不合的(含 `null`),交给引擎后会在拨号时被拒:删掉这把键,或改成引擎收的整数毫秒。
|
|
69
|
+
MCP 服务器名的 `remote-` 前缀保留给设备执行端提供的远程 MCP 服务器(`REMOTE_MCP_SERVER_NAME_PREFIX`):请求面 MCP 服务器名的判官是 `isUsableMcpServerName` / `describeBadMcpServerName`(= 线上名判官再加这一臂;线上名判官 `isUsableWireName` / `describeBadWireName` 管 A2A 对端名);配置面 `McpServerSpec.name` 受理这个前缀(拒是校验收紧,按发版纪律归 major)—— 部署声明 MCP 服务器时请自己不用这个前缀。
|
|
70
|
+
- **中心引用解析口**:`resolveCenterRefs(refs, lookup)` 读中心名册里 MCP server 的 `envRefs` / `headerRefs`(变量名,不是值):**每个成员一句判定**(`resolved` / `missing_name` / `not_a_string` / `read_threw`,
|
|
71
|
+
闭集常量 `CENTER_REF_FAULTS`)、**整台一个结论**(`resolved` / `unresolved` / `refs_unreadable`,`CENTER_REFS_VERDICTS`);只判不决定挂不挂。`lookup` 由调用方给 —— 本包不读 `process.env`,没给 ⇒ `TypeError`。
|
|
72
|
+
引用表经公开的记录读器 `readPlainStringRecord` 读(传输读器读 `env` / `headers` 用的就是这一只;原因词表 `PLAIN_STRING_RECORD_FAULTS` 三词:`not_plain_record` / `member_not_string` / `member_name_reserved`,与传输读器 `lost[].fault` 同一张表)。两方读法见 `docs/CONSUMER-INTEGRATION.md` §14。
|
|
73
|
+
- **已知边界(zod 4.4 臂)**:`Object.prototype` 上有可枚举键(值不是带 `_zod` 成员的对象)时才第一次被读的判别联合,在 zod 4.4 上永久读不出,撤掉污染也不恢复 —— 租约应答读口 `readServingLeaseResponse` 与包里另外 11 处 `z.discriminatedUnion`(如 `McpServerSpec` 的传输)同(读数:zod 4.4 线,键 `value` / `zzz`;zod 4.6 上撤掉污染即恢复;v14.6.0 同,非本包造成,zod 4.4 自己的惰性物化缺陷)。
|
|
74
|
+
`Object.prototype` 被污染本身是嵌入方进程被攻破,本包不替它兜底;失败方向是读不出(租约读口读不出 = 引擎按中心不可达处置,不读成 `granted` 也不读成 `denied`)。出路:zod 升到 4.6 或更高,或加载本包之后、嵌入方的代码跑之前让每一个要用的判别联合都被一次干净的读走到(租约读口读一次干净应答即可;`McpConfig` 要读一条合法条目才暖到传输联合,空配置走不到)。
|
|
75
|
+
- **整根只读的部署**(配置根是 ConfigMap / 只读挂载):迁移写不了运行记录、不会替你铸 —— **升级引擎之前**把 `config.env-models.json` 放进同一份 ConfigMap 的配置根
|
|
76
|
+
(要目录继续优先就写 `{"envModels":"replaced"}`)。
|
|
77
|
+
- **SS-M11 的窗**:起算点 = 引擎开始强制这道闸的那一天(上文:今天引擎不拒这个组,窗还没有起算),到期日 = 起算日 + 30 天;本包登记表里 SS-M11 那一行按本包的发布日计,
|
|
78
|
+
管的是本包何时能删迁移码。
|
|
79
|
+
- **回滚**:回滚到已核的各版旧引擎都安全 —— 两份根级文档在 `config.d` 之外,被检视的配置读者不把它们当域文档。
|
|
80
|
+
|
|
50
81
|
## 升级指引:13.x → 14.0.0(BREAKING:prompts 域单轴 = 中心 artifact;请求面 MCP 条目判官入包)
|
|
51
82
|
|
|
52
83
|
- **prompts**:域里只装引擎当期 schema 的中心 artifact。存储 `PromptsConfig = { artifact?, workers? }`(每份是 artifact 的身份 `{ payload, artifactDigest }`);
|
|
@@ -356,8 +387,12 @@ therefore shows only `zod`. Node built-ins only (`node:crypto`, `node:fs`).
|
|
|
356
387
|
`VerifierConfig` / `VerifiedIdentity` / `AuthBridgeErrorCode` + pure decision fns
|
|
357
388
|
(`shouldForceJwksRefresh`, `jwksWithinOfflineGrace`, `classifyTimeClaims`, `toVerifiedIdentity`).
|
|
358
389
|
The normative rules R1–R10 in its header are the service verifier's implementation spec.
|
|
359
|
-
`VerifiedIdentity` carries an optional `scope` (the token's active tenant scope — see `api/scopes`)
|
|
360
|
-
(issued-at, integer seconds, verbatim — absent claim ⇒ no key; not a non-negative integer ⇒ the token is refused)
|
|
390
|
+
`VerifiedIdentity` carries an optional `scope` (the token's active tenant scope — see `api/scopes`), an optional `iat`
|
|
391
|
+
(issued-at, integer seconds, verbatim — absent claim ⇒ no key; not a non-negative integer ⇒ the token is refused) and an optional
|
|
392
|
+
`principalInc` (14.7.0: the principal's incarnation id, read from the token claim `PRINCIPAL_INC_CLAIM` = `sub_inc` — a non-empty string
|
|
393
|
+
verbatim; absent claim ⇒ no key, never a placeholder; present but not a non-empty string ⇒ the token is refused. A deleted-and-re-created
|
|
394
|
+
user keeps the name and gets a new incarnation, so `sub` alone names a slot, not a person; `PrincipalIncarnation` is the one schema every
|
|
395
|
+
incarnation field reads).
|
|
361
396
|
- **web-admin credential file + connection env names** (`web-admin-credential.ts`, main barrel; S-698): the file behind
|
|
362
397
|
`SEMA_WEB_ADMIN_TOKEN_FILE` is one of two KINDS — a bare line (one static token — the same kind of file as today:
|
|
363
398
|
its first line, printable ASCII with no control / non-ASCII characters; user JWT vs worker pull token stays the reader's `looksLikeJwt` call) or a
|
|
@@ -378,6 +413,44 @@ therefore shows only `zod`. Node built-ins only (`node:crypto`, `node:fs`).
|
|
|
378
413
|
no default) → `env` / `file` / `absent`, the one pick-exactly-one rule for every credential: an empty string is unset, BOTH set (non-empty) throws (the refusal
|
|
379
414
|
names the two keys, never a value), the file path comes back verbatim (resolving a relative one against a cwd
|
|
380
415
|
is the reader's job).
|
|
416
|
+
- **Sandbox git credential plane** (14.7.0; `sandbox-git.ts` + `api/credentials.ts`, main barrel only — no subpath): a sandbox's git uses the
|
|
417
|
+
MEMBER's own credential, fetched by the data plane from web-admin's credential port and used only through a proxy in that member's own pod. This
|
|
418
|
+
package carries the shapes and word tables, no IO. (1) `isSandboxGitOrigin` — a string equal to its own WHATWG origin with scheme http / https (the
|
|
419
|
+
canonical form is that one equation's consequence: lowercase host, no default port, no trailing slash, no userinfo / path / query / fragment;
|
|
420
|
+
an origin cannot express a sub-path deployment) — and the `sandboxGit` effective-config block `SandboxGitConfig`
|
|
421
|
+
(`{ origins: [{ origin, connectUrl }] }`, ≤ `SANDBOX_GIT_MAX_ORIGINS` = 16, distinct, empty list legal; an http origin is READABLE in the block — its
|
|
422
|
+
refusal is a per-origin answer word, not a block refusal). `sandboxGit` is a non-domain top-level key of the `/effective` payload: optional, no
|
|
423
|
+
default, carried verbatim by `readEffectiveWire` (`EffectiveWire.sandboxGit` is `unknown`) and judged by the consumer with `SandboxGitConfig`.
|
|
424
|
+
(2) The credential port's wire for `kind: "sandbox_git"`: `SandboxGitCredentialRequest` (claim a handle), `CredentialHandleAnswer` /
|
|
425
|
+
`CredentialResolveRequest` (kind-independent), `SandboxGitCredentialAnswer` with one `SandboxGitOriginAnswer` per origin (discriminated by
|
|
426
|
+
`state`; `secret` only on the `active` arm — other arms strip it), `SandboxGitCredentialRefusal` (`{ error, permanence, message }`). Three word tables,
|
|
427
|
+
each spelled once: `SANDBOX_GIT_ORIGIN_VERDICTS` (the 11-row per-origin judgment order; `SANDBOX_GIT_ORIGIN_STATES` / `SANDBOX_GIT_ORIGIN_REASONS`
|
|
428
|
+
and the answer arms' reason domains derive from it), `SANDBOX_GIT_REFUSALS` (10 refusal words → HTTP statuses + `permanence`;
|
|
429
|
+
`SANDBOX_GIT_REFUSAL_ERRORS` derives from it), `SANDBOX_GIT_PERMANENCE` (`terminal` / `transient`). Both sides' reading is in
|
|
430
|
+
`docs/CONSUMER-INTEGRATION.md` §12.
|
|
431
|
+
- **Path-base judge and the fleet advertise address** (14.7.0; `url-shape.ts` main barrel, and `fleet.ts` main barrel + `./fleet`): a seat whose value is a
|
|
432
|
+
"base that other code appends request paths to" — the center base URLs, the address a fleet member advertises to the control plane, the web link base of
|
|
433
|
+
`webAdminSessionUrl` — has one rule: no credential, no query, no fragment; a path prefix is fine. `PATH_BASE_URL_FORBIDDEN_PARTS` is the ONE closed, frozen,
|
|
434
|
+
ordered set of the URL parts such a seat forbids (`userinfo` · `query` · `fragment`; a POLICY set, independent of the judge's vocabulary `HTTP_URL_PARTS` — which says what parts a URL has — so a new vocabulary word
|
|
435
|
+
has to be ruled on here explicitly); `pathBaseUrlFlaws(url)` is `httpUrlFlaws(url, that set)` — `[]` = acceptable, otherwise the judge's rule words, never a piece of the
|
|
436
|
+
value. `webAdminSessionUrl` reads it too. `FLEET_ADVERTISE_ADDRESS_ENV` (`"FLEET_ADVERTISE_ADDRESS"`, beside `FLEET_GENERATION_ENV` / `FLEET_SCOPE_ENV`) is the env name both the
|
|
437
|
+
writer (the orchestrator that renders the value) and the reader (the engine at boot) import. Judge the value AFTER placeholders are expanded: `http://$(POD_IP):8090` passes,
|
|
438
|
+
`http://[$(POD_IP)]:8090` does not (a bracketed host must parse as IPv6). Placeholder expansion, reachability and the absent / empty-string reading are not this package's;
|
|
439
|
+
the env name is not a reserved worker env name. Both sides' reading is in `docs/CONSUMER-INTEGRATION.md` §13.
|
|
440
|
+
- **Config-root documents and the env model table** (14.8.0; names / types / judges / plan in the main barrel, readers / writer / migration in `./node`): the config
|
|
441
|
+
root (the directory holding `config.d`) carries two root-level documents outside `config.d` — `config.env-models.json` (`ENV_MODELS_DOCUMENT`: whether a catalog
|
|
442
|
+
that manages the model plane may replace the env model table, `replaced` | `kept`; absent ≡ `kept` once the run marker exists) and `config.migration.SS-M11.json`
|
|
443
|
+
(the per-id run marker of one-time migration SS-M11); `CONFIG_ROOT_DOCUMENTS` is the list an engine's directory trust judge walks. ONE reader each —
|
|
444
|
+
`readEnvModelsDecision(root)` → `absent | replaced{digest} | kept{digest} | unreadable{bad_json|bad_shape, digest} | unreadable{io, errno}` (the engine gate, the
|
|
445
|
+
one-time migration and the writer's plan answer from it; `envModelsDecisionTag` is the one tagged identity of a reading — `absent` / `d:<digest>` / `io:<errno>` —
|
|
446
|
+
for warning dedup and the etag) and `readMigrationMarker(root, id)` → `absent | present | unreadable` — and ONE writer of the decision,
|
|
447
|
+
`writeEnvModelsDecision(root, "replaced"|"kept", { write: "overwrite" | "createIfAbsent" })` (the package's atomic writer, the same mode rule as a `config.d` domain document: a new file is 0o644 through the process umask, an existing file keeps its own mode;
|
|
448
|
+
the migration mints through it too, while the run marker is created only by the migration). One-time migration SS-M11 = `migrateEnvModelsDecision({ root, env })` (read first, then elect by creating the marker exclusively: the creator is
|
|
449
|
+
the only minter; a `config.d/models.json` that cannot be read — anything but `ENOENT` — mints nothing and is reported with its errno when the error carries one). The judge `catalogManagesModelPlane(doc)` (raw half: at least one entry not explicitly disabled — the same `enabled` reading as a dropped entry's
|
|
450
|
+
identity) and `enabledEntryCount(doc)`. The env model table read once — `readEnvModelSlots` / `envModelTableDeclared` (`MODEL_ID` present and non-empty) /
|
|
451
|
+
`buildEnvModelBaseline` — and the writer's plan `planEnvModelTableWrite({ env, target, decision, candidate, intent? })` (receipt by upstream id with landings
|
|
452
|
+
`{name, id}`, `confirmKey` = the receipt's sorted JSON) with its one sentence `envModelTableWriteRefusal(plan)`. Group-refusal codes
|
|
453
|
+
`CATALOG_REPLACES_ENV_MODELS_CODE` / `SUMMARIZE_MODEL_MISSING_CODE` (tail of `MODEL_PLANE_REFUSAL_CODES`).
|
|
381
454
|
- **Session web link** (`web-admin-session-url.ts`, main barrel): `webAdminSessionUrl(webBaseUrl, { worker, sessionId })` — the ONE
|
|
382
455
|
template for a web-admin session link, `<webBaseUrl>/chat?agent=<worker>&session=<sessionId>`: both parts required (a link with only
|
|
383
456
|
the session id lands on whichever worker the page picks and may not find it), each part `encodeURIComponent`-escaped, the base a
|
|
@@ -496,14 +569,14 @@ therefore shows only `zod`. Node built-ins only (`node:crypto`, `node:fs`).
|
|
|
496
569
|
`urlAllowPatternMatches` (⊆ CC `g9`, four upstream holes closed) / `urlDenyPatternMatches` (⊇ `g9` ∪ whole-string glob);
|
|
497
570
|
divergences in `docs/CC-DIVERGENCE.md`.
|
|
498
571
|
- **HTTP hook allowlists** (`http-hook-allowlists.ts`, 13.0.0): category `SAFETY_MERGE_SPEC.httpHookAllowlists`; `readHttpHookAllowlistLayer(source, settings)`,
|
|
499
|
-
`httpHookUrlVerdict(url, layers, workspace)` (every admitted layer's list must pass — layers intersect) and `httpHookEnvVarsAllowed(hookVars, layers, workspace)`;
|
|
572
|
+
`httpHookUrlVerdict(url, layers, workspace)` (every admitted layer's list must pass — layers intersect) and `httpHookEnvVarsAllowed(hookVars, layers, workspace)`, where every layer in `layers` is read by the reader's own reading (a reader-produced `read.layer`, a hand-built plain layer and a `JSON.parse`d layer from this realm with the same content get the same answer; a layer whose `source` cannot be read — missing, inherited, inconsistent or not a string — is a malformed layer that blocks everything (`rule: "malformed"`, not named in `sources`; a readable source with an unreadable list is `malformed` too and is named; `empty` means a list that really is `[]`), and a readable source word outside the closed set throws `RangeError`; lists, `hookVars` and `layers` must be plain arrays — the prototype is this realm's `Array.prototype` (an array from another realm — a `vm` context, another iframe — is refused; round-trip it through JSON first), `constructor` / `filter` / `flatMap` / `Symbol.iterator` read as this realm's built-ins, and the own keys are only indices and `length`; anything else, subclasses included, is read as malformed);
|
|
500
573
|
a layer is admitted ⇔ `layerAdmission(source, workspace, "httpHookAllowlists") === "admitted"` (user / flag / managed; project / local are below the floor).
|
|
501
574
|
- **MCP server list verdict** (`mcp-server-policy.ts`, 13.0.0): `mcpServerPolicyVerdict` → `McpPolicyBlock{name, list, rule, target,
|
|
502
575
|
written, sources}` — judged on the expanded target, echoed only as written; the managed layer (whose `allowManagedMcpServersOnly` counts)
|
|
503
576
|
is the source whose tier in `SETTINGS_SOURCE_CONTRACT` is `managed`; a tighten-only source contributes its deny list only (allow lists
|
|
504
577
|
concatenate across layers, so reading its allow list would widen the others').
|
|
505
578
|
- **Request-plane MCP entry judges** (`mcp-request-entry.ts`, 14.0.0): `isUsableWireName` / `describeBadWireName` (the engine's wire-name bound
|
|
506
|
-
`TOOL_WIRE_NAME_MAX_CHARS`, mirrored; a name with leading or trailing whitespace is a bad name too — never trimmed, never normalised; inner whitespace stays legal), `isContentOriginWord` / `describeBadContentOrigin`, `readMcpIdentityHeaders`, `readRequestMcpTransport`
|
|
579
|
+
`TOOL_WIRE_NAME_MAX_CHARS`, mirrored; a name with leading or trailing whitespace is a bad name too — never trimmed, never normalised; inner whitespace stays legal), `isUsableMcpServerName` / `describeBadMcpServerName` (14.8.0; the request plane's judge of an MCP server's name: the wire-name judge plus one arm — the `remote-` prefix, `REMOTE_MCP_SERVER_NAME_PREFIX`, is reserved for the remote MCP servers a device executor offers; the config plane's `McpServerSpec.name` accepts it — refusing would be a validation tightening, which the release discipline files under a major; A2A peer names keep `isUsableWireName`), `isContentOriginWord` / `describeBadContentOrigin`, `readMcpIdentityHeaders`, `readRequestMcpTransport`
|
|
507
580
|
(hard faults `ok:false` naming the key path; soft faults dropped by the reader and listed in `lost`; `env` / `headers` are read as plain-data string records — a `Map`, a class instance or an own `__proto__` member is not one, the delivered record is a copy, and a `lost` entry for a record carries machine-readable `member` / `fault`; the two describers strip control and bidi characters and cut at 64 characters, the service's echo form) and the value-shape describers — one reading for
|
|
508
581
|
the service's request legs and the shell's pre-send check. A `toolFaces` declaration stays parse-transparent here: its shape is the engine ingest side's to judge, and none of the tool-face vocabularies is mirrored in this package.
|
|
509
582
|
- **One-line echo strip set** (`control-and-bidi.ts`, main barrel): `CONTROL_AND_BIDI_CHARS` (C0 + DEL + the 12 `Bidi_Control=Yes` code points,
|
|
@@ -580,7 +653,7 @@ therefore shows only `zod`. Node built-ins only (`node:crypto`, `node:fs`).
|
|
|
580
653
|
carries verbatim WITHOUT checking their shape, each row naming who does check it (the engine's ingest side or
|
|
581
654
|
a consumer's enforcement point). Today: `models[].compat` (1.10.0), `models[].headers` (6.4.0; the header-name
|
|
582
655
|
allowlist × lane is sema-server's, not restated here), `mcp.servers[].source` (1.7.2), `mcp.servers[].toolFaces`
|
|
583
|
-
(1.9.0), `mcp.servers[].toolAxes` / `authForm` / `entryKind` (12.2.0; connector-entry declarations — `toolAxes` has exactly one shape judge, the engine's root export `readMcpToolAxes`; the other two keys' semantics are the consumer's),
|
|
656
|
+
(1.9.0), `mcp.servers[].toolAxes` / `authForm` / `entryKind` (12.2.0; connector-entry declarations — `toolAxes` has exactly one shape judge, the engine's root export `readMcpToolAxes`; the other two keys' semantics are the consumer's), `mcp.servers[].sessionMetaKey` (14.8.0; the `_meta` key the engine sends the leg's session id under — one judge, the engine's root export `judgeSessionMetaKey`), `mcp.servers[].startupTimeoutMs` (one server's own startup bound — one judge, the engine's root export `mcpStartupTimeoutMs`),
|
|
584
657
|
and an entitlement override's `value`. `models[].extraBody` is NOT one of them — it has its own shape
|
|
585
658
|
gate (a plain JSON object); what it leaves open is the key vocabulary inside. `compat` / `headers` / `extraBody`
|
|
586
659
|
still clear the one free-form safety judgment (secret-shaped key/value, accessor, cycle, non-JSON, too deep ⇒ rejected).
|
|
@@ -633,9 +706,20 @@ local `.env`). This package never carries a credential.
|
|
|
633
706
|
models.json rosters.json skills.json mcp.json scenarios.json plugins.json
|
|
634
707
|
collab.json runtime.json governance.json execution.json prompts.json limits.json
|
|
635
708
|
remote-exec.json # NOT a domain (the exec backend; read by loadRemoteExec)
|
|
709
|
+
config.env-models.json # the env-model-table decision {"envModels":"replaced"|"kept"} (readEnvModelsDecision / writeEnvModelsDecision)
|
|
710
|
+
config.migration.SS-M11.json # the run marker of one-time migration SS-M11 {"migration":"SS-M11"} (readMigrationMarker; written only by the migration)
|
|
636
711
|
.env # secret VALUES only (KEY=value); gitignored; never read into any *.json
|
|
637
712
|
```
|
|
638
713
|
|
|
714
|
+
**The root belongs to the client (TOC).** An engine writes exactly two files in it — the two root-level documents above, both outside `config.d` (the sampled
|
|
715
|
+
file stores read only `config.d`, and no inspected config reader treats these files as domain documents, so they are inert to old and new engines alike); a client
|
|
716
|
+
keeps both in its own registry of persistent files (hygiene, export and reset must not drop or flag them). Bundles (`exportBundle` / `readBundle`) carry only the
|
|
717
|
+
portable `config.d` domains: these two are deployment decisions and do not travel. Laid down offline (before any engine starts on the root: an image, a
|
|
718
|
+
ConfigMap, a restore), the two documents and the domain documents may be written in any order; at runtime the order is fixed — a writer writes the
|
|
719
|
+
decision before `config.d/models.json` (both under its lock on `models.json`), and the one-time migration creates its run marker before it mints the
|
|
720
|
+
decision. When the root itself is absent, the engine's one-time migration creates it (and any missing parent) owner-only (0o700), owned by the engine's
|
|
721
|
+
user: a client that runs under a different user should create the root itself before the engine first starts.
|
|
722
|
+
|
|
639
723
|
Fleet-only domains (`workers`, `hosts`, `systems`) are omitted on a single machine — they resolve to schema
|
|
640
724
|
defaults. Each `config.d/<domain>.json` is exactly that domain's zod object.
|
|
641
725
|
|
package/conformance/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
14.
|
|
1
|
+
14.8.0
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
{
|
|
2
|
+
"domains": {
|
|
3
|
+
"outcome": "effective",
|
|
4
|
+
"effective": {
|
|
5
|
+
"collab": {
|
|
6
|
+
"templates": []
|
|
7
|
+
},
|
|
8
|
+
"entitlement": {
|
|
9
|
+
"budgetGroups": [],
|
|
10
|
+
"groupBindings": [],
|
|
11
|
+
"subjects": [],
|
|
12
|
+
"tiers": []
|
|
13
|
+
},
|
|
14
|
+
"execution": {
|
|
15
|
+
"allowedLanes": [
|
|
16
|
+
"host"
|
|
17
|
+
],
|
|
18
|
+
"required": false
|
|
19
|
+
},
|
|
20
|
+
"governance": {},
|
|
21
|
+
"hosts": {
|
|
22
|
+
"hosts": []
|
|
23
|
+
},
|
|
24
|
+
"limits": {},
|
|
25
|
+
"mcp": {
|
|
26
|
+
"servers": []
|
|
27
|
+
},
|
|
28
|
+
"models": {
|
|
29
|
+
"atMention": {
|
|
30
|
+
"mode": "all"
|
|
31
|
+
},
|
|
32
|
+
"models": [],
|
|
33
|
+
"roles": {},
|
|
34
|
+
"tierGroups": []
|
|
35
|
+
},
|
|
36
|
+
"plugins": {
|
|
37
|
+
"plugins": []
|
|
38
|
+
},
|
|
39
|
+
"projects": {
|
|
40
|
+
"projects": {}
|
|
41
|
+
},
|
|
42
|
+
"prompts": {},
|
|
43
|
+
"rosters": {
|
|
44
|
+
"rosters": []
|
|
45
|
+
},
|
|
46
|
+
"scenarios": {
|
|
47
|
+
"scenarios": []
|
|
48
|
+
},
|
|
49
|
+
"skills": {
|
|
50
|
+
"skills": []
|
|
51
|
+
},
|
|
52
|
+
"systems": {
|
|
53
|
+
"systems": []
|
|
54
|
+
},
|
|
55
|
+
"workers": {
|
|
56
|
+
"workers": []
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"warnings": [],
|
|
60
|
+
"metadata": {
|
|
61
|
+
"version": 7,
|
|
62
|
+
"updatedAt": "2026-10-23T00:00:00.000Z"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"strictWrite": {
|
|
66
|
+
"outcome": "effective",
|
|
67
|
+
"effective": {},
|
|
68
|
+
"warnings": [],
|
|
69
|
+
"metadata": {
|
|
70
|
+
"version": 7,
|
|
71
|
+
"updatedAt": "2026-10-23T00:00:00.000Z"
|
|
72
|
+
},
|
|
73
|
+
"why": "受理空集:本条打的是 wire 腿(提交的域文档集为空)。"
|
|
74
|
+
},
|
|
75
|
+
"wire": {
|
|
76
|
+
"outcome": "effective",
|
|
77
|
+
"effective": {
|
|
78
|
+
"collab": {
|
|
79
|
+
"templates": []
|
|
80
|
+
},
|
|
81
|
+
"entitlement": {
|
|
82
|
+
"budgetGroups": [],
|
|
83
|
+
"groupBindings": [],
|
|
84
|
+
"subjects": [],
|
|
85
|
+
"tiers": []
|
|
86
|
+
},
|
|
87
|
+
"execution": {
|
|
88
|
+
"allowedLanes": [
|
|
89
|
+
"host"
|
|
90
|
+
],
|
|
91
|
+
"required": false
|
|
92
|
+
},
|
|
93
|
+
"governance": {},
|
|
94
|
+
"hosts": {
|
|
95
|
+
"hosts": []
|
|
96
|
+
},
|
|
97
|
+
"limits": {},
|
|
98
|
+
"mcp": {
|
|
99
|
+
"servers": []
|
|
100
|
+
},
|
|
101
|
+
"models": {
|
|
102
|
+
"atMention": {
|
|
103
|
+
"mode": "all"
|
|
104
|
+
},
|
|
105
|
+
"models": [],
|
|
106
|
+
"roles": {},
|
|
107
|
+
"tierGroups": []
|
|
108
|
+
},
|
|
109
|
+
"plugins": {
|
|
110
|
+
"plugins": []
|
|
111
|
+
},
|
|
112
|
+
"projects": {
|
|
113
|
+
"projects": {}
|
|
114
|
+
},
|
|
115
|
+
"rosters": {
|
|
116
|
+
"rosters": []
|
|
117
|
+
},
|
|
118
|
+
"sandboxGit": {
|
|
119
|
+
"origins": [
|
|
120
|
+
{
|
|
121
|
+
"connectUrl": "https://console.example.com/connect/git",
|
|
122
|
+
"origin": "https://git.example.com:9999"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"connectUrl": "https://console.example.com/connect/git",
|
|
126
|
+
"origin": "http://git.legacy.example:8080"
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
},
|
|
130
|
+
"scenarios": {
|
|
131
|
+
"scenarios": []
|
|
132
|
+
},
|
|
133
|
+
"skills": {
|
|
134
|
+
"skills": []
|
|
135
|
+
},
|
|
136
|
+
"systems": {
|
|
137
|
+
"systems": []
|
|
138
|
+
},
|
|
139
|
+
"workers": {
|
|
140
|
+
"workers": []
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
"warnings": [],
|
|
144
|
+
"metadata": {
|
|
145
|
+
"version": 7,
|
|
146
|
+
"updatedAt": "2026-10-23T00:00:00.000Z"
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
"localRoot": {
|
|
150
|
+
"derive": true,
|
|
151
|
+
"localVersion": 2617555914511015
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"version": 7,
|
|
4
|
+
"updatedAt": "2026-10-23T00:00:00.000Z"
|
|
5
|
+
},
|
|
6
|
+
"domains": {},
|
|
7
|
+
"wirePayload": {
|
|
8
|
+
"version": 7,
|
|
9
|
+
"updatedAt": "2026-10-23T00:00:00.000Z",
|
|
10
|
+
"sandboxGit": {
|
|
11
|
+
"origins": [
|
|
12
|
+
{
|
|
13
|
+
"origin": "https://git.example.com:9999",
|
|
14
|
+
"connectUrl": "https://console.example.com/connect/git"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"origin": "http://git.legacy.example:8080",
|
|
18
|
+
"connectUrl": "https://console.example.com/connect/git"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
{
|
|
2
|
+
"contributor": "contract",
|
|
3
|
+
"claim": "`sandboxGit` 是 /effective 载荷上的**非域**顶层键(web-admin 按部署登记册投影,不是存储域):wire 腿按 open-world 把它**逐字承运**(含块里的 http origin —— 它的拒因是逐 origin 应答里的一个词,不是块的校验拒),不出告警;其余三腿的入参里没有这个块,读出的 effective 里也不铸它(可选、无默认)。把它当域去校验、或当未知键丢掉的读腿,会让取值平面静默失去 origin 表。",
|
|
4
|
+
"firstRedOn": []
|
|
5
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
{
|
|
2
|
+
"domains": {
|
|
3
|
+
"outcome": "effective",
|
|
4
|
+
"effective": {
|
|
5
|
+
"collab": {
|
|
6
|
+
"templates": []
|
|
7
|
+
},
|
|
8
|
+
"entitlement": {
|
|
9
|
+
"budgetGroups": [],
|
|
10
|
+
"groupBindings": [],
|
|
11
|
+
"subjects": [],
|
|
12
|
+
"tiers": []
|
|
13
|
+
},
|
|
14
|
+
"execution": {
|
|
15
|
+
"allowedLanes": [
|
|
16
|
+
"host"
|
|
17
|
+
],
|
|
18
|
+
"required": false
|
|
19
|
+
},
|
|
20
|
+
"governance": {},
|
|
21
|
+
"hosts": {
|
|
22
|
+
"hosts": []
|
|
23
|
+
},
|
|
24
|
+
"limits": {},
|
|
25
|
+
"mcp": {
|
|
26
|
+
"servers": []
|
|
27
|
+
},
|
|
28
|
+
"models": {
|
|
29
|
+
"atMention": {
|
|
30
|
+
"mode": "all"
|
|
31
|
+
},
|
|
32
|
+
"models": [],
|
|
33
|
+
"roles": {},
|
|
34
|
+
"tierGroups": []
|
|
35
|
+
},
|
|
36
|
+
"plugins": {
|
|
37
|
+
"plugins": []
|
|
38
|
+
},
|
|
39
|
+
"projects": {
|
|
40
|
+
"projects": {}
|
|
41
|
+
},
|
|
42
|
+
"prompts": {},
|
|
43
|
+
"rosters": {
|
|
44
|
+
"rosters": []
|
|
45
|
+
},
|
|
46
|
+
"scenarios": {
|
|
47
|
+
"scenarios": []
|
|
48
|
+
},
|
|
49
|
+
"skills": {
|
|
50
|
+
"skills": []
|
|
51
|
+
},
|
|
52
|
+
"systems": {
|
|
53
|
+
"systems": []
|
|
54
|
+
},
|
|
55
|
+
"workers": {
|
|
56
|
+
"workers": []
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"warnings": [],
|
|
60
|
+
"metadata": {
|
|
61
|
+
"version": 7,
|
|
62
|
+
"updatedAt": "2026-10-23T00:00:00.000Z"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"strictWrite": {
|
|
66
|
+
"outcome": "effective",
|
|
67
|
+
"effective": {},
|
|
68
|
+
"warnings": [],
|
|
69
|
+
"metadata": {
|
|
70
|
+
"version": 7,
|
|
71
|
+
"updatedAt": "2026-10-23T00:00:00.000Z"
|
|
72
|
+
},
|
|
73
|
+
"why": "受理空集:本条打的是 wire 腿(提交的域文档集为空)。"
|
|
74
|
+
},
|
|
75
|
+
"wire": {
|
|
76
|
+
"outcome": "effective",
|
|
77
|
+
"effective": {
|
|
78
|
+
"collab": {
|
|
79
|
+
"templates": []
|
|
80
|
+
},
|
|
81
|
+
"entitlement": {
|
|
82
|
+
"budgetGroups": [],
|
|
83
|
+
"groupBindings": [],
|
|
84
|
+
"subjects": [],
|
|
85
|
+
"tiers": []
|
|
86
|
+
},
|
|
87
|
+
"execution": {
|
|
88
|
+
"allowedLanes": [
|
|
89
|
+
"host"
|
|
90
|
+
],
|
|
91
|
+
"required": false
|
|
92
|
+
},
|
|
93
|
+
"governance": {},
|
|
94
|
+
"hosts": {
|
|
95
|
+
"hosts": []
|
|
96
|
+
},
|
|
97
|
+
"limits": {},
|
|
98
|
+
"mcp": {
|
|
99
|
+
"servers": []
|
|
100
|
+
},
|
|
101
|
+
"models": {
|
|
102
|
+
"atMention": {
|
|
103
|
+
"mode": "all"
|
|
104
|
+
},
|
|
105
|
+
"models": [],
|
|
106
|
+
"roles": {},
|
|
107
|
+
"tierGroups": []
|
|
108
|
+
},
|
|
109
|
+
"plugins": {
|
|
110
|
+
"plugins": []
|
|
111
|
+
},
|
|
112
|
+
"projects": {
|
|
113
|
+
"projects": {}
|
|
114
|
+
},
|
|
115
|
+
"rosters": {
|
|
116
|
+
"rosters": []
|
|
117
|
+
},
|
|
118
|
+
"sandboxGit": {
|
|
119
|
+
"origins": [
|
|
120
|
+
{
|
|
121
|
+
"connectUrl": "http://console.example.com/connect/git",
|
|
122
|
+
"origin": "https://Git.Example.com:443/"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"connectUrl": "https://console.example.com/connect/git",
|
|
126
|
+
"origin": "https://Git.Example.com:443/"
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
},
|
|
130
|
+
"scenarios": {
|
|
131
|
+
"scenarios": []
|
|
132
|
+
},
|
|
133
|
+
"skills": {
|
|
134
|
+
"skills": []
|
|
135
|
+
},
|
|
136
|
+
"systems": {
|
|
137
|
+
"systems": []
|
|
138
|
+
},
|
|
139
|
+
"workers": {
|
|
140
|
+
"workers": []
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
"warnings": [],
|
|
144
|
+
"metadata": {
|
|
145
|
+
"version": 7,
|
|
146
|
+
"updatedAt": "2026-10-23T00:00:00.000Z"
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
"localRoot": {
|
|
150
|
+
"derive": true,
|
|
151
|
+
"localVersion": 2617555914511015
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"version": 7,
|
|
4
|
+
"updatedAt": "2026-10-23T00:00:00.000Z"
|
|
5
|
+
},
|
|
6
|
+
"domains": {},
|
|
7
|
+
"wirePayload": {
|
|
8
|
+
"version": 7,
|
|
9
|
+
"updatedAt": "2026-10-23T00:00:00.000Z",
|
|
10
|
+
"sandboxGit": {
|
|
11
|
+
"origins": [
|
|
12
|
+
{
|
|
13
|
+
"origin": "https://Git.Example.com:443/",
|
|
14
|
+
"connectUrl": "http://console.example.com/connect/git"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"origin": "https://Git.Example.com:443/",
|
|
18
|
+
"connectUrl": "https://console.example.com/connect/git"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -54,10 +54,14 @@
|
|
|
54
54
|
* signature checks). (Decision function: `jwksWithinOfflineGrace`.)
|
|
55
55
|
*
|
|
56
56
|
* R9 OUTPUT. Success → `VerifiedIdentity { principal: sub, role: token.role, grantId: token.jti,
|
|
57
|
-
* scope: token.scope, iat: token.iat }` (`scope` = the multi-tenant active scope claim, `api/scopes`; read it with
|
|
57
|
+
* scope: token.scope, iat: token.iat, principalInc: token.sub_inc }` (`scope` = the multi-tenant active scope claim, `api/scopes`; read it with
|
|
58
58
|
* `readTokenScope` — an absent claim is refused, not read as "global"; `iat` = issued-at, integer seconds, carried
|
|
59
59
|
* verbatim — a reader that learns a principal was deleted at T cuts off its tokens issued before T; absent claim ⇒ no
|
|
60
|
-
* `iat` key; present but not a non-negative integer ⇒ the token is `malformed`
|
|
60
|
+
* `iat` key; present but not a non-negative integer ⇒ the token is `malformed`; `principalInc` = the incarnation claim
|
|
61
|
+
* {@link PRINCIPAL_INC_CLAIM} carried verbatim — the same name deleted and re-created is ANOTHER person, and this is what
|
|
62
|
+
* tells the two apart; absent claim ⇒ no `principalInc` key — never a placeholder: what a reader does with a token that names
|
|
63
|
+
* no incarnation is that reader's decision, not a refusal here; present but not a non-empty string ⇒ the token is `malformed`,
|
|
64
|
+
* the same law as `sub`).
|
|
61
65
|
* `role` is web-admin RBAC role claim (string, informational for the worker's own authz mapping);
|
|
62
66
|
* `grantId` is the jti when web-admin stamps one (absent today — optional forever). Failure → one
|
|
63
67
|
* of AUTH_BRIDGE_ERROR_CODES; the HTTP surface answers 401 with a GENERIC message (no error detail
|
|
@@ -87,6 +91,14 @@ export declare const DEFAULT_OFFLINE_GRACE_SEC: number;
|
|
|
87
91
|
* split one identity into two. Anyone validating or minting a principal (a token `sub`, a membership row, an outbound
|
|
88
92
|
* principal header) checks against THIS value. */
|
|
89
93
|
export declare const PRINCIPAL_MAX_LENGTH = 190;
|
|
94
|
+
/** R9 — the token claim that carries the principal's INCARNATION: a deleted-and-re-created user keeps the name (`sub`) and gets a new
|
|
95
|
+
* incarnation, so `sub` alone names a slot, not a person. One spelling for whoever mints the claim and whoever reads it
|
|
96
|
+
* ({@link toVerifiedIdentity} reads it by this constant), the way `SCOPE_CLAIM` (`api/scopes`) is spelled once for the scope claim. */
|
|
97
|
+
export declare const PRINCIPAL_INC_CLAIM = "sub_inc";
|
|
98
|
+
/** One incarnation id: a non-empty string, opaque (never trimmed, folded or parsed). The token claim, {@link VerifiedIdentity.principalInc},
|
|
99
|
+
* and every wire field that names an incarnation (the credential port's request and answer) are THIS schema — one rule, read by object. */
|
|
100
|
+
export declare const PrincipalIncarnation: z.ZodString;
|
|
101
|
+
export type PrincipalIncarnation = z.infer<typeof PrincipalIncarnation>;
|
|
90
102
|
/** One RSA public JWK as web-admin publishes it (public members ONLY — a `d`/`p`/`q` here is a web-admin
|
|
91
103
|
* bug, but the verifier MUST still never use/echo private members). */
|
|
92
104
|
export declare const WebAdminJwk: z.ZodObject<{
|
|
@@ -140,6 +152,7 @@ export declare const VerifiedIdentity: z.ZodObject<{
|
|
|
140
152
|
grantId: z.ZodOptional<z.ZodString>;
|
|
141
153
|
scope: z.ZodOptional<z.ZodString>;
|
|
142
154
|
iat: z.ZodOptional<z.ZodNumber>;
|
|
155
|
+
principalInc: z.ZodOptional<z.ZodString>;
|
|
143
156
|
}, z.core.$strip>;
|
|
144
157
|
export type VerifiedIdentity = z.infer<typeof VerifiedIdentity>;
|
|
145
158
|
/** The verifier's total result — exactly one of ok/error. */
|
|
@@ -195,6 +208,7 @@ export declare function toVerifiedIdentity(payload: {
|
|
|
195
208
|
aud?: unknown;
|
|
196
209
|
scope?: unknown;
|
|
197
210
|
iat?: unknown;
|
|
211
|
+
sub_inc?: unknown;
|
|
198
212
|
}, cfg?: Pick<VerifierConfig, "audience">): AuthBridgeResult;
|
|
199
213
|
/** Cheap pre-filter: does this bearer LOOK like a JWT (three non-empty dot-separated segments)? Use it to
|
|
200
214
|
* route between the existing static-token branch and this verifier — NOT as any kind of validation. */
|