@sema-ai/registry-core 0.10.3
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 +131 -0
- package/README.md +84 -0
- package/dist/api/auth-bridge.d.ts +332 -0
- package/dist/api/auth-bridge.d.ts.map +1 -0
- package/dist/api/auth-bridge.js +211 -0
- package/dist/api/auth-bridge.js.map +1 -0
- package/dist/api/auth.d.ts +258 -0
- package/dist/api/auth.d.ts.map +1 -0
- package/dist/api/auth.js +204 -0
- package/dist/api/auth.js.map +1 -0
- package/dist/api/scopes.d.ts +353 -0
- package/dist/api/scopes.d.ts.map +1 -0
- package/dist/api/scopes.js +229 -0
- package/dist/api/scopes.js.map +1 -0
- package/dist/bundle.d.ts +14 -0
- package/dist/bundle.d.ts.map +1 -0
- package/dist/bundle.js +68 -0
- package/dist/bundle.js.map +1 -0
- package/dist/config-fns.d.ts +236 -0
- package/dist/config-fns.d.ts.map +1 -0
- package/dist/config-fns.js +272 -0
- package/dist/config-fns.js.map +1 -0
- package/dist/cross-domain.d.ts +35 -0
- package/dist/cross-domain.d.ts.map +1 -0
- package/dist/cross-domain.js +119 -0
- package/dist/cross-domain.js.map +1 -0
- package/dist/file-edit.d.ts +37 -0
- package/dist/file-edit.d.ts.map +1 -0
- package/dist/file-edit.js +126 -0
- package/dist/file-edit.js.map +1 -0
- package/dist/file-store.d.ts +39 -0
- package/dist/file-store.d.ts.map +1 -0
- package/dist/file-store.js +142 -0
- package/dist/file-store.js.map +1 -0
- package/dist/fleet.d.ts +479 -0
- package/dist/fleet.d.ts.map +1 -0
- package/dist/fleet.js +303 -0
- package/dist/fleet.js.map +1 -0
- package/dist/hash.d.ts +33 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +60 -0
- package/dist/hash.js.map +1 -0
- package/dist/hooks.d.ts +5478 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +628 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/local-load.d.ts +33 -0
- package/dist/local-load.d.ts.map +1 -0
- package/dist/local-load.js +164 -0
- package/dist/local-load.js.map +1 -0
- package/dist/migrate.d.ts +245 -0
- package/dist/migrate.d.ts.map +1 -0
- package/dist/migrate.js +543 -0
- package/dist/migrate.js.map +1 -0
- package/dist/node.d.ts +12 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +12 -0
- package/dist/node.js.map +1 -0
- package/dist/reader.d.ts +19 -0
- package/dist/reader.d.ts.map +1 -0
- package/dist/reader.js +2 -0
- package/dist/reader.js.map +1 -0
- package/dist/remote-exec.d.ts +275 -0
- package/dist/remote-exec.d.ts.map +1 -0
- package/dist/remote-exec.js +183 -0
- package/dist/remote-exec.js.map +1 -0
- package/dist/resolve-roster.d.ts +29 -0
- package/dist/resolve-roster.d.ts.map +1 -0
- package/dist/resolve-roster.js +105 -0
- package/dist/resolve-roster.js.map +1 -0
- package/dist/safety-merge-spec.d.ts +328 -0
- package/dist/safety-merge-spec.d.ts.map +1 -0
- package/dist/safety-merge-spec.js +65 -0
- package/dist/safety-merge-spec.js.map +1 -0
- package/dist/scheduler-store-node.d.ts +15 -0
- package/dist/scheduler-store-node.d.ts.map +1 -0
- package/dist/scheduler-store-node.js +43 -0
- package/dist/scheduler-store-node.js.map +1 -0
- package/dist/scheduler-store.d.ts +65 -0
- package/dist/scheduler-store.d.ts.map +1 -0
- package/dist/scheduler-store.js +59 -0
- package/dist/scheduler-store.js.map +1 -0
- package/dist/secret-refs.d.ts +34 -0
- package/dist/secret-refs.d.ts.map +1 -0
- package/dist/secret-refs.js +49 -0
- package/dist/secret-refs.js.map +1 -0
- package/dist/sha256.d.ts +17 -0
- package/dist/sha256.d.ts.map +1 -0
- package/dist/sha256.js +115 -0
- package/dist/sha256.js.map +1 -0
- package/dist/skills-manifest.d.ts +13 -0
- package/dist/skills-manifest.d.ts.map +1 -0
- package/dist/skills-manifest.js +55 -0
- package/dist/skills-manifest.js.map +1 -0
- package/dist/types.d.ts +9911 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1727 -0
- package/dist/types.js.map +1 -0
- package/package.json +130 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
(始于 0.8.1;更早版本的变更记录散在 commit message 与 README「发版纪律」注记。)
|
|
4
|
+
|
|
5
|
+
## 0.10.3 — 2026-07-12
|
|
6
|
+
|
|
7
|
+
**新增(additive patch,发版纪律「patch 只装 additive 字段」):`runtimeCaps.allowObservers` — observer
|
|
8
|
+
agents 开闸线收编(板 [661]④/[668]①/[671]②;service 1.175.0 宽读透传 wire 已钉,core 1.270.0/1.273.0
|
|
9
|
+
`=== true` 显式 opt-in)。**
|
|
10
|
+
|
|
11
|
+
- **形状**:`EntitlementRuntimeCaps.allowObservers?: boolean`(optional,零迁移——存量文档不含此键
|
|
12
|
+
=ABSENT 语义,字节不变)。
|
|
13
|
+
- **三态语义(ABSENT ≠ false)**:ABSENT = center 没说话 → wire 上**不下发键**(core 是唯一默认源;
|
|
14
|
+
service toCoreRuntimeCaps 缺键不补 false 的纪律与此对钉——补 false 会伪造「center 说过话」的显式
|
|
15
|
+
判决,污染 env-baseline 合成序);`true` = 显式开 observer agents;`false` = 显式 org 关,**压过
|
|
16
|
+
节点级 env 基线 `EXPERIMENTAL_OBSERVER_AGENTS`**(center 带键必赢)。
|
|
17
|
+
- **⚠️ core 消费极性与其余 allow* 相反**:引擎侧 `=== true` 显式 opt-in(默认 OFF,undefined ships
|
|
18
|
+
dark)——本键是 ENABLE 线不只是 ceiling;但 compose 合成极性不变:`RUNTIME_CAP_STRICT.allowObservers
|
|
19
|
+
= false`(deny 胜,与 core 的 opt-in 检查正交)。
|
|
20
|
+
- 钉测 +5(true/false round-trip / ABSENT 不注默认 / 非 boolean 拒[service fail-closed THROW 依赖
|
|
21
|
+
schema 真值] / STRICT 表极性 / tier 挂载端到端);全量 366 绿(361+5);build 绿。
|
|
22
|
+
|
|
23
|
+
## 0.10.2 — 2026-07-12
|
|
24
|
+
|
|
25
|
+
**修复 + 文案勘误(附B 三单合评审 M-1 / MAJOR-1;PM 拍向=保 C 行为、改 A/B 文案)。**
|
|
26
|
+
|
|
27
|
+
- **M-1 根治:`SESSION_MIRROR_ENGINE_URL` trim 前置**(`z.string().trim().pipe(NON_SECRET_URL)…`,
|
|
28
|
+
trim 在 url/refine 之前)。合评审实证:`" https://…"` 带前导空白时写侧收(new URL 容忍前导空白)
|
|
29
|
+
而壳读侧逐字符比对拒 = 全员镜像**静默 degraded**。0.10.2 起前导/尾随空白在 schema 层归一后再验,
|
|
30
|
+
**存储值即 trim 后值**(所见即所存);trim 只归一空白不放水(trim 后仍非法照拒)。钉测 +2
|
|
31
|
+
(前导空白/尾随空白→归一通过且存储值已 trim;纯空白/trim 后非 loopback http 仍拒)。
|
|
32
|
+
- **MAJOR-1 文案勘误(TSDoc,行为不变=保 C)**:`SessionMirrorPolicy` 对 required:true 治理态的表述
|
|
33
|
+
由「本地 --target 只能改目标不能关镜像」勘正为「**本地覆盖不生效:既不能改目标也不能关闭
|
|
34
|
+
(org 压一切)**」——0.10.1 CHANGELOG/commit 中旧措辞以本条为准(WEB-SETUP-RECON 附B r1 勘误)。
|
|
35
|
+
- 全量 361 绿(359+2);additive patch,零形状变更、零迁移。
|
|
36
|
+
|
|
37
|
+
## 0.10.1 — 2026-07-12
|
|
38
|
+
|
|
39
|
+
**新增(additive patch,发版纪律「patch 只装 additive 字段」):`execution.sessionMirror` — org 默认
|
|
40
|
+
会话镜像目标(WEB-SETUP-RECON 附B,clay 拍两层;附B 单A/单B/单C 并行切分的先行腿)。**
|
|
41
|
+
|
|
42
|
+
- **形状**:`sessionMirror?: { engineUrl: string, required: boolean=false }`,挂在 execution 域。
|
|
43
|
+
整个对象 optional,**缺省 INERT**(无下发=老 center/老壳/0.8–0.10 存量 execution 文档零行为差、
|
|
44
|
+
零迁移——存量文档本就不可能含此键)。
|
|
45
|
+
- **域归属=收编 execution,零新域**(142-S3 草案同款尺量,详见 commit message 与 SessionMirrorPolicy
|
|
46
|
+
JSDoc):同一治理轴(required 与 ExecutionPolicy.required 完全同构=壳锁定/managed-settings 心智)
|
|
47
|
+
+无独立读损语义(损坏退默认=required:true 审计强制静默蒸发=放权,与 execution throw 理由逐字同款
|
|
48
|
+
——tierGroups 收编 models 的同款判据)+无独立写面(纯声明字段,无动作端点,RBAC 同 editor 档)。
|
|
49
|
+
#74 双表态/缺域 fixture/PORTABLE 全部零新增(execution 既有表态罩住)。
|
|
50
|
+
- **engineUrl 写侧传输闸**(`SESSION_MIRROR_ENGINE_URL`):**https 或 loopback http**(127.0.0.1/
|
|
51
|
+
localhost/::1/[::1],与壳 isLoopbackEngineUrl 逐字同表)——非 loopback 明文 http 在 **schema 层就拒**
|
|
52
|
+
(镜像上行带会话内容+Bearer 凭证;写侧 fail-loud 早于读侧 evaluateEngineRouting 执法)。基线复用
|
|
53
|
+
NON_SECRET_URL(禁 userinfo 凭证/禁 query/fragment 夹带 secret-shaped 值)。
|
|
54
|
+
- **两层语义**:required:false(默认)=便利默认值(壳解析序第三腿:本地显式 --target > 本值 >
|
|
55
|
+
connect 回落;开关仍用户 sync on,隐私 opt-in 先例不破);required:true=org 治理强制(镜像默认开
|
|
56
|
+
且不可关,--target 只能改目标——CC managed-settings 锁定语义,S3 沙箱锁同构)。ENFORCEMENT HONESTY
|
|
57
|
+
同域头注:今天 CLIENT-SIDE 执法。
|
|
58
|
+
- **开放演化**:对象不 .strict()(zod 默认 strip 未知键)——v2 加 audit 字段类(保留窗/脱敏面等)
|
|
59
|
+
恒 additive,v2 文档被 v1 消费者读到=未知键剥除,forward-compatible。
|
|
60
|
+
- EffectiveConfig 投影直通(execution 既有腿);钉测 10(零新域/INERT 存量字节稳定/两层 round-trip/
|
|
61
|
+
传输闸四通三拒/NON_SECRET 基线/malformed/投影/corrupt=throw 不降级/v2 strip);全量 359 绿。
|
|
62
|
+
|
|
63
|
+
## 0.10.0 — 2026-07-12
|
|
64
|
+
|
|
65
|
+
**新增(additive,兼容):第 15 域 `projects`(142-S3 项目身份登记簿+无仓 projectId 铸造权威)。**
|
|
66
|
+
草案=sema-cc-parity/142-S3-PROJECTS-DOMAIN-DRAFT.md;口径=黑板 [633](铸造离线自由/使用注册执法)
|
|
67
|
+
+[641]②(core 六答)+[642](收账)。
|
|
68
|
+
|
|
69
|
+
- **形状**:`ProjectsConfig = { projects: Record<projectId, ProjectRegistration> }`;
|
|
70
|
+
`ProjectRegistration = { displayName, gitRemotes[], defaultScopes[], registeredBy, registeredAt, notes? }`。
|
|
71
|
+
复合键 `(tenant, projectId)` 的 tenant 半边走 center **scope 轴**(每 scope 一份域文档),不进文档
|
|
72
|
+
——fork 后同一 projectId 可合法存在于两个 scope,互不相见。
|
|
73
|
+
- **宽读严写([641]②)**:新导出 `PROJECT_ID_REGEX` = generic lowercase 规范 UUID(读面收 v4——
|
|
74
|
+
外部/历史铸造也是合法身份);铸造侧(core marker / center mint 端点)恒产 **uuidv7**。自持镜像
|
|
75
|
+
+dev-only conformance 纪律(core 公开导出后切单源,TODO 在 JSDoc)。
|
|
76
|
+
- **gitRemotes 底线校验**:归一化 `host/path` 形态——非空/无空白/不含 `://`(出现即未归一化,
|
|
77
|
+
fail-loud);`.max(16)`;空数组=无仓合法。**superRefine:租户内一条 remote 只属一个 project**
|
|
78
|
+
(找回阶梯/防重铸要求映射确定;撞了写入即拒)。
|
|
79
|
+
- **defaultScopes 开放值域**([580]④ 纪律):z.string() 不 enum;方向=v2 scope 键串
|
|
80
|
+
(`proj`/`userproj` JSDoc-only,S4 细化),本域只登记不解释。
|
|
81
|
+
- **#74 立规过门**:`DOMAIN_READ_FALLBACK.projects = "throw"`(退默认=身份簿蒸发+claim 读改写面
|
|
82
|
+
clobber 存量登记=数据丢失,比放权更狠);**PORTABLE_DOMAINS 不进**(租户身份数据不可搬——bundle
|
|
83
|
+
导入=凭空复制身份注册,design/142 §1.4 威胁①);缺省 INERT(`{projects:{}}`,老快照/0.9 存量
|
|
84
|
+
读面零变化)。写权限=editor 档(登记簿非注入面,DOMAIN_WRITE_ROLE 注释落裁定)。
|
|
85
|
+
- 钉测 11(注册域/INERT/round-trip/宽读 v4/坏键拒/REGEX 镜像/remote 底线/remote 撞项目/开放值域/
|
|
86
|
+
corrupt=throw 不降级/0.9 字节稳定);全量 349 绿。
|
|
87
|
+
|
|
88
|
+
## 0.8.1 — 2026-07-11
|
|
89
|
+
|
|
90
|
+
**修复(S3 评审 M-1:收紧毁存量,E2 血训模式)。** 0.8.0 给 hosts 域加了 `.max` 收紧
|
|
91
|
+
(`HostEntry.name`/`HostStatus.host` → 63、`hosts` 数组 → 1024),但 store 持久化的是 RAW JSON、
|
|
92
|
+
每次读都经 `parseDomain` 重校验——0.7 时代**合法**的超限存量(64 字符机器名 / 2000 条数组)在 0.8 下
|
|
93
|
+
读取即 THROW,并炸掉整个 scope 的 `getEffective`(连带 /effective、caps、publish 全线)。两层修复:
|
|
94
|
+
|
|
95
|
+
1. **hosts 精确 grandfather(读路径)** — `grandfatherHostsRead`(config-fns):`buildEffective` 解析
|
|
96
|
+
hosts 前对**且仅对** 0.8.0 新增的两条界做 clamp:
|
|
97
|
+
- name 超 63 → **整条跳过**(不截断——截断会伪造一个与任何 ORCH_HOST_ID 都不匹配的新身份,还可能
|
|
98
|
+
撞进重名 refine);
|
|
99
|
+
- 数组超 1024 → `slice(0, 1024)`(先跳过后截断,给合法条目腾位);
|
|
100
|
+
- 每次 clamp 经 `onWarning` 回调发 `hosts-grandfathered` 审计告警;
|
|
101
|
+
- **良构数据恒等返回原引用 → 字节不变**。
|
|
102
|
+
regex/类型/重名等非本次收紧项**不**放行,仍交 zod(这是定向 grandfather,不是宽松解析器)。
|
|
103
|
+
|
|
104
|
+
2. **buildEffective 逐域降级护栏(通用,防未来一切收紧)** — 坏域退 schema 默认 + `domain-defaulted`
|
|
105
|
+
告警,不炸全 scope;但「退默认」逐域判安全性(`DOMAIN_READ_FALLBACK`,`satisfies Record<DomainName,…>`
|
|
106
|
+
强制未来新域显式表态):
|
|
107
|
+
|
|
108
|
+
| 域 | 策略 | 理由 |
|
|
109
|
+
|---|---|---|
|
|
110
|
+
| models / skills / mcp / plugins / scenarios / systems / collab | 退默认 | 加法能力目录,退空=能力消失,fail-closed |
|
|
111
|
+
| hosts | 退默认 | 空注册表=只失自动排布(可用性面);自注册回登为 approved:false=待批,fail-closed;且精确 grandfather 先行 |
|
|
112
|
+
| rosters | **fail-loud** | 丢 roster → resolveEffectiveForWorker 落 global fallback=**全目录**,静默放宽受限 worker 的模型面(放权) |
|
|
113
|
+
| workers | **fail-loud** | 退空 fleet spec → reconciler 会**拆光**全部在管 worker(毁灭性 apply,比读故障更糟) |
|
|
114
|
+
| runtime | **fail-loud** | 丢中心下发的限速/成本天花板=预算面 fail-open |
|
|
115
|
+
| governance | **fail-loud** | 丢 autonomy/commandPolicy/approvalRequire 三闸=放权 |
|
|
116
|
+
| entitlement | **fail-loud** | 丢 org kill-switch/预算天花板/tier 限制=放权 |
|
|
117
|
+
| execution | **fail-loud** | required:true(沙箱强制)翻回 false=放权 |
|
|
118
|
+
|
|
119
|
+
fail-loud 域坏档时消费者按既有姿势 fail-static(守住 last-good),比端出去掉闸的 effective 安全。
|
|
120
|
+
|
|
121
|
+
**API(additive,兼容)**:`buildEffective` 第 4 参可选 `onWarning?: (w: EffectiveReadWarning) => void`;
|
|
122
|
+
`FileConfigStore` 构造第 2 参可选 `{ onWarning }`;新导出 `grandfatherHostsRead` /
|
|
123
|
+
`GrandfatherHostsReadResult` / `EffectiveReadWarning` / `DOMAIN_READ_FALLBACK` /
|
|
124
|
+
`HOST_NAME_MAX` / `HOSTS_ARRAY_MAX`(types)。
|
|
125
|
+
|
|
126
|
+
**写路径不动**:`parseDomain` 保持严格——超限**新写入**在 save 时照旧 fail-loud 拒绝
|
|
127
|
+
(0.8.0「poisoned publish」意图不变);center 各 store 的 setDomain/PUT 走的就是它。
|
|
128
|
+
|
|
129
|
+
**订正(S3 批报告失实处)**:「center 本就兜底 probes」不成立——center hosts/status PUT 并无独立
|
|
130
|
+
probe 上限,schema 即闸;probes 上报属 WRITE 路径,其 fail-loud **保持**(有意设计),本修复只涉
|
|
131
|
+
config-hosts 读路径。
|
package/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# @sema-ai/registry-core
|
|
2
|
+
|
|
3
|
+
Pure config contract — **one source of truth** for the AI-agent config domain, shared by
|
|
4
|
+
[`sema-registry`](http://git.zhenbs.com:9999/AI-Only/sema-registry) (the TOB fleet admin),
|
|
5
|
+
[`sema-server`](http://git.zhenbs.com:9999/AI-Only/sema-server) (the workers), and the future
|
|
6
|
+
TOC desktop/CLI. No two copies of the schema.
|
|
7
|
+
|
|
8
|
+
The package is **pure**: zero `next`/`react`/server imports, zero DB drivers. `zod` is a peer dependency
|
|
9
|
+
(the host supplies the single instance). Node built-ins only (`node:crypto`, `node:fs`).
|
|
10
|
+
|
|
11
|
+
## What's in it
|
|
12
|
+
|
|
13
|
+
- **The zod contract** (`types.ts`): `DOMAIN_SCHEMAS` (15 domains — 0.10.0 adds `projects`, the 142-S3 project identity ledger), `EffectiveConfig`, `EffectiveWire`,
|
|
14
|
+
`ENV_NAME`/`K8S_QUANTITY` boundaries, every domain's entry/config type.
|
|
15
|
+
- **Pure config fns** (`config-fns.ts`): `buildEffective` (deterministic — `updatedAt` is a required param),
|
|
16
|
+
`parseDomain`, `emptyEffective` (epoch-seeded), `statusRowsToPrune`, role helpers (`roleAtLeast`,
|
|
17
|
+
`domainWriteRole`, …), `resolveActiveTiers`(0.9.0 档位组→引擎 `RunnerDeps.tiers` 单表的唯一解析点,
|
|
18
|
+
[599]③;组存 `models.tierGroups`+`models.activeTierGroup`,组切换=换表下发), and the pure data
|
|
19
|
+
shapes (`User`, `PublishedSnapshot`, …).
|
|
20
|
+
- **Roster resolution** (`resolve-roster.ts`): `resolveEffectiveForWorker` → the per-worker wire payload.
|
|
21
|
+
- **Ref integrity** (`cross-domain.ts`): `refIntegrityIssues` + `siblingResolver` (the pure half — fleet
|
|
22
|
+
auto-placement stays in sema-registry).
|
|
23
|
+
- **Hashing** (`hash.ts`): `stableHash` (FNV-1a 64) for ETags / version detection.
|
|
24
|
+
- **Skills manifest** (`skills-manifest.ts`): content-addressing for the lazy-pull skill bodies.
|
|
25
|
+
- **Secret inventory** (`secret-refs.ts`): `collectEnvRefs` — every referenced env-NAME.
|
|
26
|
+
- **The narrow reader** (`reader.ts`): `ConfigReader` (3 methods) — sema-registry's fat `ConfigStore`
|
|
27
|
+
`extends` it; a local file source / test fixture implements it without stubbing ~32 methods.
|
|
28
|
+
- **`remoteExec` contract** (`remote-exec.ts`): the execution-substrate schema mirroring the service's
|
|
29
|
+
actual providers (`e2b` / `k8s` / `ssh` / `adb`), discriminated on `provider`; every secret an env-NAME ref.
|
|
30
|
+
- **Auth wire contract** (`api/auth.ts`, subpath `./api/auth`): the `/api/v1/auth/*` face — RFC 8628
|
|
31
|
+
device flow (code / token poll / approve consent) + rotating refresh + logout, the OAuth error envelope,
|
|
32
|
+
and the pure client helpers `normalizeUserCode` / `nextPollInterval`. Shipped M1 shapes frozen
|
|
33
|
+
(REGISTRY-REBUILD-DESIGN §2). OpenAPI mirror: `docs/openapi-auth.yaml` (auth face only).
|
|
34
|
+
- **Auth bridge** (`api/auth-bridge.ts`, subpath `./api/auth-bridge`): the 鉴权桥 contract (design §5) —
|
|
35
|
+
how a worker verifies a registry-minted user JWT: RS256-only, JWKS from `/api/auth/sso/jwks` with
|
|
36
|
+
kid-miss forced refresh + ≥60s throttle, exp/nbf clock skew (60s), 24h offline grace then fail-closed;
|
|
37
|
+
`VerifierConfig` / `VerifiedIdentity` / `AuthBridgeErrorCode` + pure decision fns
|
|
38
|
+
(`shouldForceJwksRefresh`, `jwksWithinOfflineGrace`, `classifyTimeClaims`, `toVerifiedIdentity`).
|
|
39
|
+
The normative rules R1–R10 in its header are the service verifier's implementation spec.
|
|
40
|
+
`VerifiedIdentity` carries an optional `scope` (the token's active tenant scope — see `api/scopes`).
|
|
41
|
+
- **Multi-tenant scopes** (`api/scopes.ts`, subpath `./api/scopes`): the scope contract — the `Scope`
|
|
42
|
+
entity (`{id,name,createdAt}`, reserved default `"global"` always exists), per-scope `ScopeMember`
|
|
43
|
+
rows (`{scopeId,principal,role}` on the existing viewer<editor<publisher<admin ladder), the optional
|
|
44
|
+
JWT `scope` claim (absent = `"global"`, fully backward compatible; refresh tokens are never
|
|
45
|
+
scope-bound), and the wire shapes for `GET/POST /api/v1/scopes`, `POST /api/v1/auth/scope`
|
|
46
|
+
(scope-switch mint, OAuth-style errors) and `PUT/DELETE /api/v1/scopes/{id}/members`. The DECISION
|
|
47
|
+
SEMANTICS live here as pure functions the registry imports (never re-implements):
|
|
48
|
+
`resolveTokenScope` (mint-time scope binding: 1 membership → it; several incl. global → global;
|
|
49
|
+
several without global → lexicographically first; none → `{scope:"global", role:instanceRole}`),
|
|
50
|
+
`canSwitchScope` (member or instance admin), `effectiveScopeRole` (instance admin is `"admin"` in
|
|
51
|
+
every scope) and `tokenScopeOf` (missing claim = `"global"`). OpenAPI mirror: `docs/openapi-scopes.yaml`.
|
|
52
|
+
- **Local file source** (`file-store.ts`, `file-edit.ts`): `FileConfigStore` reads `config.d/*.json` →
|
|
53
|
+
`EffectiveConfig`; `validateDomain` / `writeDomainFile` / `listRequiredEnvNames` are the TOC's
|
|
54
|
+
read/write/validate helpers.
|
|
55
|
+
|
|
56
|
+
## Secret boundary
|
|
57
|
+
|
|
58
|
+
Every secret field is an **env-NAME**, never a value. The `ENV_NAME` regex (`/^[A-Z_][A-Z0-9_]*$/`) rejects a
|
|
59
|
+
pasted credential. Values are resolved by the consumer from its own env (service env / k8s secret / the TOC
|
|
60
|
+
local `.env`). This package never carries a credential.
|
|
61
|
+
|
|
62
|
+
## Local file layout (TOC)
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
<root>/
|
|
66
|
+
config.d/
|
|
67
|
+
models.json rosters.json skills.json mcp.json scenarios.json
|
|
68
|
+
collab.json runtime.json governance.json execution.json remote-exec.json
|
|
69
|
+
.env # secret VALUES only (KEY=value); gitignored; never read into any *.json
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Fleet-only domains (`workers`, `hosts`, `systems`) are omitted on a single machine — they resolve to schema
|
|
73
|
+
defaults. Each `config.d/<domain>.json` is exactly that domain's zod object.
|
|
74
|
+
|
|
75
|
+
## 发版纪律(semver,E6-fix F3 立规)
|
|
76
|
+
|
|
77
|
+
- **0.x 破坏性变更必 bump minor**(0.7 → 0.8),不许塞 patch——`^0.7.0` 这类脱字号 range 在 0.x 下
|
|
78
|
+
会自动收 patch,把 BREAKING 塞进 patch = 未 pin 精确版的下游 `npm install` 静默拿到破坏。
|
|
79
|
+
- patch 只装:additive 字段、修 bug、文档/测试。commit message 的 `feat!`/`BREAKING` 标记照旧,
|
|
80
|
+
但标记不豁免版号义务。
|
|
81
|
+
- **历史注记(0.7.1,2026-07-10)**:0.7.1 携带了两件预协调 BREAKING(撤 governance effective 双写
|
|
82
|
+
镜像、撤 QuotaLease 0.5 `tokens` 别名 shim)——名义上违反本条。当时下游(center/service 1.150.0)
|
|
83
|
+
均已显式迁移、实际破坏面为零,PM 拍板不重发 0.8.0(纯扰动);本段即为该例外的收账与今后的规矩。
|
|
84
|
+
- 双腿发布:`npm run publish:dual`;报告必须含双腿 shasum。
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `api/auth-bridge` — the 鉴权桥 contract: how a WORKER (sema-server / any data-plane service) verifies a
|
|
3
|
+
* registry-minted USER access JWT and maps it into its existing `x-agent-principal` identity system.
|
|
4
|
+
* (REGISTRY-REBUILD-DESIGN §5, M3 — registry half = JWKS already shipped; service half = ONE verifier
|
|
5
|
+
* middleware implementing the rules below. This file is the schema + normative spec the service copies.)
|
|
6
|
+
*
|
|
7
|
+
* TRUST MODEL (single trust root, zero new worker config): the worker already trusts the registry for its
|
|
8
|
+
* effective config; it extends the SAME trust to the registry's JWKS. A bearer token that is a well-formed
|
|
9
|
+
* JWT and verifies against that JWKS with the checks below IS the user — `sub` feeds x-agent-principal
|
|
10
|
+
* directly (owner semantics / costQuota / entitlement are already principal-keyed). Existing
|
|
11
|
+
* SERVICE_AUTH_TOKENS / wpt_ bearer branches are untouched; this is an ADDITIONAL branch tried when the
|
|
12
|
+
* bearer LOOKS like a JWT (three dot-separated base64url segments).
|
|
13
|
+
*
|
|
14
|
+
* ════════════════════════════════════════════════════════════════════════════════════════════════════════
|
|
15
|
+
* NORMATIVE VERIFICATION RULES — the service verifier MUST implement ALL of these (copy as its spec):
|
|
16
|
+
*
|
|
17
|
+
* R1 ALGORITHM WHITELIST. Accept `alg: RS256` ONLY (AUTH_BRIDGE_ALGS). Reject `none`, every HS* (an
|
|
18
|
+
* HS-signed token verified against a PUBLIC key = classic alg-confusion forgery), the ES and PS families, and any
|
|
19
|
+
* token whose header alg is absent. Pin the list in the JWT library call — never read alg from the
|
|
20
|
+
* token to decide how to verify.
|
|
21
|
+
*
|
|
22
|
+
* R2 KEY SOURCE. Verification keys come from the registry JWKS endpoint `GET {registryBaseUrl}` +
|
|
23
|
+
* REGISTRY_JWKS_PATH ("/api/auth/sso/jwks"; response shape = `Jwks` below, public members only:
|
|
24
|
+
* kty/n/e/kid/use/alg). Match the token header `kid` against cached keys.
|
|
25
|
+
*
|
|
26
|
+
* R3 KID-MISS FORCED REFRESH + THROTTLE. kid found in cache → use the cached key (no fetch on the hot
|
|
27
|
+
* path). kid NOT in cache → force ONE JWKS refetch, then retry the match (this is the key-ROTATION
|
|
28
|
+
* window: a freshly rotated registry would otherwise reject every new token until the cache expired).
|
|
29
|
+
* Forced refreshes are throttled to at most one per `jwksRefreshMinIntervalSec` (default 60s) —
|
|
30
|
+
* otherwise an attacker spraying random-kid tokens turns the verifier into a JWKS-fetch DoS cannon.
|
|
31
|
+
* Within the throttle window an unknown kid fails as `invalid_signature` WITHOUT fetching.
|
|
32
|
+
* (Decision function: `shouldForceJwksRefresh`.)
|
|
33
|
+
*
|
|
34
|
+
* R4 ISSUER PIN. `iss` MUST equal the CONFIGURED issuer exactly (VerifierConfig.issuer = the registry's
|
|
35
|
+
* SSO_ISSUER, trailing slash stripped on both sides). A missing or different iss → `unknown_issuer`.
|
|
36
|
+
* Never derive the expected issuer from the token or from request headers.
|
|
37
|
+
*
|
|
38
|
+
* R5 TIME WINDOW WITH SKEW. `exp` is REQUIRED: reject when nowSec > exp + clockSkewSec. `nbf`, when
|
|
39
|
+
* present: reject when nowSec < nbf - clockSkewSec. clockSkewSec default 60 (DEFAULT_CLOCK_SKEW_SEC).
|
|
40
|
+
* Both failures classify as `expired`. (Decision function: `classifyTimeClaims`.)
|
|
41
|
+
*
|
|
42
|
+
* R6 PRINCIPAL. `sub` MUST be a non-empty string — it becomes `VerifiedIdentity.principal` and feeds the
|
|
43
|
+
* existing x-agent-principal pipeline verbatim. Empty/missing sub → `malformed`.
|
|
44
|
+
*
|
|
45
|
+
* R7 AUDIENCE (optional). When VerifierConfig.audience is set, `aud` must contain it (string equal or
|
|
46
|
+
* array member); mismatch → `invalid_signature`-class rejection is WRONG — classify as `malformed`
|
|
47
|
+
* (the token is valid but not FOR this worker). When audience is unset, `aud` is ignored.
|
|
48
|
+
*
|
|
49
|
+
* R8 OFFLINE GRACE / FAIL-CLOSED. JWKS fetch fails (network/5xx/malformed body): keys already cached
|
|
50
|
+
* remain USABLE for up to `offlineGraceSec` (default 24h, DEFAULT_OFFLINE_GRACE_SEC) measured from the
|
|
51
|
+
* last SUCCESSFUL fetch — a registry blip must not take the whole data plane down. Past the grace, or
|
|
52
|
+
* with no cache at all, verification fails CLOSED as `jwks_unavailable` (never fail-open, never skip
|
|
53
|
+
* signature checks). (Decision function: `jwksWithinOfflineGrace`.)
|
|
54
|
+
*
|
|
55
|
+
* R9 OUTPUT. Success → `VerifiedIdentity { principal: sub, role: token.role, grantId: token.jti,
|
|
56
|
+
* scope: token.scope }` (`scope` = the multi-tenant active scope claim, `api/scopes`; absent = "global").
|
|
57
|
+
* `role` is the registry RBAC role claim (string, informational for the worker's own authz mapping);
|
|
58
|
+
* `grantId` is the jti when the registry stamps one (absent today — optional forever). Failure → one
|
|
59
|
+
* of AUTH_BRIDGE_ERROR_CODES; the HTTP surface answers 401 with a GENERIC message (no error detail
|
|
60
|
+
* disclosure to the caller; the code goes to logs/metrics only).
|
|
61
|
+
*
|
|
62
|
+
* R10 NO CREDENTIAL LOGGING. Never log the token, any signature segment, or JWKS key material. Log
|
|
63
|
+
* kid + error code + iss at most.
|
|
64
|
+
* ════════════════════════════════════════════════════════════════════════════════════════════════════════
|
|
65
|
+
*
|
|
66
|
+
* Pure zod contract + pure decision functions — zero IO, zero crypto here (the service brings its own JWT
|
|
67
|
+
* library; THESE shapes pin down its configuration), browser-safe.
|
|
68
|
+
*/
|
|
69
|
+
import { z } from "zod";
|
|
70
|
+
/** The registry JWKS endpoint, relative to the registry origin (shipped; public by design, RFC 7517 §5). */
|
|
71
|
+
export declare const REGISTRY_JWKS_PATH = "/api/auth/sso/jwks";
|
|
72
|
+
/** R1 — the ONLY acceptable JWS algorithms. */
|
|
73
|
+
export declare const AUTH_BRIDGE_ALGS: readonly ["RS256"];
|
|
74
|
+
export type AuthBridgeAlg = (typeof AUTH_BRIDGE_ALGS)[number];
|
|
75
|
+
/** R5 — default tolerated clock skew (seconds) on exp/nbf. */
|
|
76
|
+
export declare const DEFAULT_CLOCK_SKEW_SEC = 60;
|
|
77
|
+
/** R3 — default minimum seconds between FORCED (kid-miss) JWKS refetches. */
|
|
78
|
+
export declare const DEFAULT_JWKS_REFRESH_MIN_INTERVAL_SEC = 60;
|
|
79
|
+
/** R8 — default seconds a cached JWKS stays usable after fetches start failing (24h), then fail-closed. */
|
|
80
|
+
export declare const DEFAULT_OFFLINE_GRACE_SEC: number;
|
|
81
|
+
/** One RSA public JWK as the registry publishes it (public members ONLY — a `d`/`p`/`q` here is a registry
|
|
82
|
+
* bug, but the verifier MUST still never use/echo private members). */
|
|
83
|
+
export declare const RegistryJwk: z.ZodObject<{
|
|
84
|
+
kty: z.ZodLiteral<"RSA">;
|
|
85
|
+
/** modulus, base64url */
|
|
86
|
+
n: z.ZodString;
|
|
87
|
+
/** exponent, base64url */
|
|
88
|
+
e: z.ZodString;
|
|
89
|
+
/** RFC 7638 thumbprint of the public JWK — the rotation-stable key id tokens carry in their header. */
|
|
90
|
+
kid: z.ZodString;
|
|
91
|
+
use: z.ZodOptional<z.ZodLiteral<"sig">>;
|
|
92
|
+
alg: z.ZodOptional<z.ZodEnum<["RS256"]>>;
|
|
93
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
94
|
+
kty: z.ZodLiteral<"RSA">;
|
|
95
|
+
/** modulus, base64url */
|
|
96
|
+
n: z.ZodString;
|
|
97
|
+
/** exponent, base64url */
|
|
98
|
+
e: z.ZodString;
|
|
99
|
+
/** RFC 7638 thumbprint of the public JWK — the rotation-stable key id tokens carry in their header. */
|
|
100
|
+
kid: z.ZodString;
|
|
101
|
+
use: z.ZodOptional<z.ZodLiteral<"sig">>;
|
|
102
|
+
alg: z.ZodOptional<z.ZodEnum<["RS256"]>>;
|
|
103
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
104
|
+
kty: z.ZodLiteral<"RSA">;
|
|
105
|
+
/** modulus, base64url */
|
|
106
|
+
n: z.ZodString;
|
|
107
|
+
/** exponent, base64url */
|
|
108
|
+
e: z.ZodString;
|
|
109
|
+
/** RFC 7638 thumbprint of the public JWK — the rotation-stable key id tokens carry in their header. */
|
|
110
|
+
kid: z.ZodString;
|
|
111
|
+
use: z.ZodOptional<z.ZodLiteral<"sig">>;
|
|
112
|
+
alg: z.ZodOptional<z.ZodEnum<["RS256"]>>;
|
|
113
|
+
}, z.ZodTypeAny, "passthrough">>;
|
|
114
|
+
export type RegistryJwk = z.infer<typeof RegistryJwk>;
|
|
115
|
+
/** The JWKS document. `{keys: []}` is VALID (registry with SSO disabled) — every token then fails as
|
|
116
|
+
* `invalid_signature` (there is a registry, it just mints nothing). */
|
|
117
|
+
export declare const RegistryJwks: z.ZodObject<{
|
|
118
|
+
keys: z.ZodArray<z.ZodObject<{
|
|
119
|
+
kty: z.ZodLiteral<"RSA">;
|
|
120
|
+
/** modulus, base64url */
|
|
121
|
+
n: z.ZodString;
|
|
122
|
+
/** exponent, base64url */
|
|
123
|
+
e: z.ZodString;
|
|
124
|
+
/** RFC 7638 thumbprint of the public JWK — the rotation-stable key id tokens carry in their header. */
|
|
125
|
+
kid: z.ZodString;
|
|
126
|
+
use: z.ZodOptional<z.ZodLiteral<"sig">>;
|
|
127
|
+
alg: z.ZodOptional<z.ZodEnum<["RS256"]>>;
|
|
128
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
129
|
+
kty: z.ZodLiteral<"RSA">;
|
|
130
|
+
/** modulus, base64url */
|
|
131
|
+
n: z.ZodString;
|
|
132
|
+
/** exponent, base64url */
|
|
133
|
+
e: z.ZodString;
|
|
134
|
+
/** RFC 7638 thumbprint of the public JWK — the rotation-stable key id tokens carry in their header. */
|
|
135
|
+
kid: z.ZodString;
|
|
136
|
+
use: z.ZodOptional<z.ZodLiteral<"sig">>;
|
|
137
|
+
alg: z.ZodOptional<z.ZodEnum<["RS256"]>>;
|
|
138
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
139
|
+
kty: z.ZodLiteral<"RSA">;
|
|
140
|
+
/** modulus, base64url */
|
|
141
|
+
n: z.ZodString;
|
|
142
|
+
/** exponent, base64url */
|
|
143
|
+
e: z.ZodString;
|
|
144
|
+
/** RFC 7638 thumbprint of the public JWK — the rotation-stable key id tokens carry in their header. */
|
|
145
|
+
kid: z.ZodString;
|
|
146
|
+
use: z.ZodOptional<z.ZodLiteral<"sig">>;
|
|
147
|
+
alg: z.ZodOptional<z.ZodEnum<["RS256"]>>;
|
|
148
|
+
}, z.ZodTypeAny, "passthrough">>, "many">;
|
|
149
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
150
|
+
keys: z.ZodArray<z.ZodObject<{
|
|
151
|
+
kty: z.ZodLiteral<"RSA">;
|
|
152
|
+
/** modulus, base64url */
|
|
153
|
+
n: z.ZodString;
|
|
154
|
+
/** exponent, base64url */
|
|
155
|
+
e: z.ZodString;
|
|
156
|
+
/** RFC 7638 thumbprint of the public JWK — the rotation-stable key id tokens carry in their header. */
|
|
157
|
+
kid: z.ZodString;
|
|
158
|
+
use: z.ZodOptional<z.ZodLiteral<"sig">>;
|
|
159
|
+
alg: z.ZodOptional<z.ZodEnum<["RS256"]>>;
|
|
160
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
161
|
+
kty: z.ZodLiteral<"RSA">;
|
|
162
|
+
/** modulus, base64url */
|
|
163
|
+
n: z.ZodString;
|
|
164
|
+
/** exponent, base64url */
|
|
165
|
+
e: z.ZodString;
|
|
166
|
+
/** RFC 7638 thumbprint of the public JWK — the rotation-stable key id tokens carry in their header. */
|
|
167
|
+
kid: z.ZodString;
|
|
168
|
+
use: z.ZodOptional<z.ZodLiteral<"sig">>;
|
|
169
|
+
alg: z.ZodOptional<z.ZodEnum<["RS256"]>>;
|
|
170
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
171
|
+
kty: z.ZodLiteral<"RSA">;
|
|
172
|
+
/** modulus, base64url */
|
|
173
|
+
n: z.ZodString;
|
|
174
|
+
/** exponent, base64url */
|
|
175
|
+
e: z.ZodString;
|
|
176
|
+
/** RFC 7638 thumbprint of the public JWK — the rotation-stable key id tokens carry in their header. */
|
|
177
|
+
kid: z.ZodString;
|
|
178
|
+
use: z.ZodOptional<z.ZodLiteral<"sig">>;
|
|
179
|
+
alg: z.ZodOptional<z.ZodEnum<["RS256"]>>;
|
|
180
|
+
}, z.ZodTypeAny, "passthrough">>, "many">;
|
|
181
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
182
|
+
keys: z.ZodArray<z.ZodObject<{
|
|
183
|
+
kty: z.ZodLiteral<"RSA">;
|
|
184
|
+
/** modulus, base64url */
|
|
185
|
+
n: z.ZodString;
|
|
186
|
+
/** exponent, base64url */
|
|
187
|
+
e: z.ZodString;
|
|
188
|
+
/** RFC 7638 thumbprint of the public JWK — the rotation-stable key id tokens carry in their header. */
|
|
189
|
+
kid: z.ZodString;
|
|
190
|
+
use: z.ZodOptional<z.ZodLiteral<"sig">>;
|
|
191
|
+
alg: z.ZodOptional<z.ZodEnum<["RS256"]>>;
|
|
192
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
193
|
+
kty: z.ZodLiteral<"RSA">;
|
|
194
|
+
/** modulus, base64url */
|
|
195
|
+
n: z.ZodString;
|
|
196
|
+
/** exponent, base64url */
|
|
197
|
+
e: z.ZodString;
|
|
198
|
+
/** RFC 7638 thumbprint of the public JWK — the rotation-stable key id tokens carry in their header. */
|
|
199
|
+
kid: z.ZodString;
|
|
200
|
+
use: z.ZodOptional<z.ZodLiteral<"sig">>;
|
|
201
|
+
alg: z.ZodOptional<z.ZodEnum<["RS256"]>>;
|
|
202
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
203
|
+
kty: z.ZodLiteral<"RSA">;
|
|
204
|
+
/** modulus, base64url */
|
|
205
|
+
n: z.ZodString;
|
|
206
|
+
/** exponent, base64url */
|
|
207
|
+
e: z.ZodString;
|
|
208
|
+
/** RFC 7638 thumbprint of the public JWK — the rotation-stable key id tokens carry in their header. */
|
|
209
|
+
kid: z.ZodString;
|
|
210
|
+
use: z.ZodOptional<z.ZodLiteral<"sig">>;
|
|
211
|
+
alg: z.ZodOptional<z.ZodEnum<["RS256"]>>;
|
|
212
|
+
}, z.ZodTypeAny, "passthrough">>, "many">;
|
|
213
|
+
}, z.ZodTypeAny, "passthrough">>;
|
|
214
|
+
export type RegistryJwks = z.infer<typeof RegistryJwks>;
|
|
215
|
+
/** Everything the service verifier is allowed to be configured with. In the zero-new-config deployment the
|
|
216
|
+
* worker derives `jwksUrl` from the registry base URL it already has (config pull) + REGISTRY_JWKS_PATH,
|
|
217
|
+
* and `issuer` from the same registry identity — but both stay explicit here so a split deployment
|
|
218
|
+
* (issuer behind a proxy ≠ fetch URL) is expressible. */
|
|
219
|
+
export declare const VerifierConfig: z.ZodObject<{
|
|
220
|
+
/** R4 — the EXACT expected `iss` (the registry's SSO_ISSUER). Compare with trailing slash stripped. */
|
|
221
|
+
issuer: z.ZodString;
|
|
222
|
+
/** R2 — absolute JWKS URL. Default: issuer (or the worker's registry base URL) + REGISTRY_JWKS_PATH. */
|
|
223
|
+
jwksUrl: z.ZodOptional<z.ZodString>;
|
|
224
|
+
/** R7 — when set, `aud` must contain this value; unset = aud ignored. */
|
|
225
|
+
audience: z.ZodOptional<z.ZodString>;
|
|
226
|
+
/** R5 — seconds of exp/nbf tolerance. Default DEFAULT_CLOCK_SKEW_SEC (60). */
|
|
227
|
+
clockSkewSec: z.ZodDefault<z.ZodNumber>;
|
|
228
|
+
/** R3 — min seconds between forced kid-miss refetches. Default 60. */
|
|
229
|
+
jwksRefreshMinIntervalSec: z.ZodDefault<z.ZodNumber>;
|
|
230
|
+
/** R8 — seconds a stale cache survives fetch failures before fail-closed. Default 86400 (24h). */
|
|
231
|
+
offlineGraceSec: z.ZodDefault<z.ZodNumber>;
|
|
232
|
+
}, "strip", z.ZodTypeAny, {
|
|
233
|
+
issuer: string;
|
|
234
|
+
clockSkewSec: number;
|
|
235
|
+
jwksRefreshMinIntervalSec: number;
|
|
236
|
+
offlineGraceSec: number;
|
|
237
|
+
jwksUrl?: string | undefined;
|
|
238
|
+
audience?: string | undefined;
|
|
239
|
+
}, {
|
|
240
|
+
issuer: string;
|
|
241
|
+
jwksUrl?: string | undefined;
|
|
242
|
+
audience?: string | undefined;
|
|
243
|
+
clockSkewSec?: number | undefined;
|
|
244
|
+
jwksRefreshMinIntervalSec?: number | undefined;
|
|
245
|
+
offlineGraceSec?: number | undefined;
|
|
246
|
+
}>;
|
|
247
|
+
export type VerifierConfig = z.infer<typeof VerifierConfig>;
|
|
248
|
+
/** The pre-parse (all-optional-defaults) input shape, for consumers assembling config by hand. */
|
|
249
|
+
export type VerifierConfigInput = z.input<typeof VerifierConfig>;
|
|
250
|
+
/** R9 failure classification — for logs/metrics/tests; the HTTP surface stays a generic 401. */
|
|
251
|
+
export declare const AUTH_BRIDGE_ERROR_CODES: readonly ["expired", "invalid_signature", "unknown_issuer", "jwks_unavailable", "malformed"];
|
|
252
|
+
export type AuthBridgeErrorCode = (typeof AUTH_BRIDGE_ERROR_CODES)[number];
|
|
253
|
+
/** R9 success output — what the verifier middleware hands the existing principal pipeline. */
|
|
254
|
+
export declare const VerifiedIdentity: z.ZodObject<{
|
|
255
|
+
/** = token `sub`. Feeds x-agent-principal / owner semantics / costQuota / entitlement keying verbatim. */
|
|
256
|
+
principal: z.ZodString;
|
|
257
|
+
/** = token `role` claim (registry RBAC: admin/editor/viewer/…) — informational; the worker maps it to
|
|
258
|
+
* its own authz, it does NOT replace worker-side entitlement checks. */
|
|
259
|
+
role: z.ZodOptional<z.ZodString>;
|
|
260
|
+
/** = token `jti` when present (the auth_grants grant id — audit correlation). Absent today; optional forever. */
|
|
261
|
+
grantId: z.ZodOptional<z.ZodString>;
|
|
262
|
+
/** = token `scope` claim when present (the active tenant scope, `api/scopes`) — transparently forwarded
|
|
263
|
+
* to the worker's principal pipeline. ABSENT means the pre-scope/default token: semantically "global"
|
|
264
|
+
* (use `tokenScopeOf` from api/scopes to normalize). Optional forever — backward compatible. */
|
|
265
|
+
scope: z.ZodOptional<z.ZodString>;
|
|
266
|
+
}, "strip", z.ZodTypeAny, {
|
|
267
|
+
principal: string;
|
|
268
|
+
role?: string | undefined;
|
|
269
|
+
scope?: string | undefined;
|
|
270
|
+
grantId?: string | undefined;
|
|
271
|
+
}, {
|
|
272
|
+
principal: string;
|
|
273
|
+
role?: string | undefined;
|
|
274
|
+
scope?: string | undefined;
|
|
275
|
+
grantId?: string | undefined;
|
|
276
|
+
}>;
|
|
277
|
+
export type VerifiedIdentity = z.infer<typeof VerifiedIdentity>;
|
|
278
|
+
/** The verifier's total result — exactly one of ok/error. */
|
|
279
|
+
export type AuthBridgeResult = {
|
|
280
|
+
ok: true;
|
|
281
|
+
identity: VerifiedIdentity;
|
|
282
|
+
} | {
|
|
283
|
+
ok: false;
|
|
284
|
+
error: AuthBridgeErrorCode;
|
|
285
|
+
};
|
|
286
|
+
/** What the R3 cache logic needs to know — all times epoch ms supplied by the caller (pure, no clock). */
|
|
287
|
+
export interface JwksCacheSnapshot {
|
|
288
|
+
/** kids currently in the cache (empty = never fetched / fetched empty). */
|
|
289
|
+
cachedKids: readonly string[];
|
|
290
|
+
/** epoch ms of the last fetch ATTEMPT (success or failure); undefined = never attempted. */
|
|
291
|
+
lastFetchAttemptMs?: number;
|
|
292
|
+
/** epoch ms of the last SUCCESSFUL fetch; undefined = never succeeded. */
|
|
293
|
+
lastFetchSuccessMs?: number;
|
|
294
|
+
nowMs: number;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* R3 — should a token bearing `kid` trigger a forced JWKS refetch?
|
|
298
|
+
* true ⇔ the kid is not cached AND we are outside the refresh throttle window.
|
|
299
|
+
* false ⇒ verify against the cache as-is (cached kid hit), or fail `invalid_signature` (unknown kid,
|
|
300
|
+
* throttled) without fetching.
|
|
301
|
+
*/
|
|
302
|
+
export declare function shouldForceJwksRefresh(kid: string, cache: JwksCacheSnapshot, cfg: Pick<VerifierConfig, "jwksRefreshMinIntervalSec">): boolean;
|
|
303
|
+
/**
|
|
304
|
+
* R8 — with the LATEST fetch having failed, may the cached keys still be used?
|
|
305
|
+
* true ⇔ there WAS a successful fetch and it is at most offlineGraceSec old.
|
|
306
|
+
* false ⇒ fail closed: `jwks_unavailable`.
|
|
307
|
+
*/
|
|
308
|
+
export declare function jwksWithinOfflineGrace(cache: Pick<JwksCacheSnapshot, "lastFetchSuccessMs" | "nowMs">, cfg: Pick<VerifierConfig, "offlineGraceSec">): boolean;
|
|
309
|
+
/**
|
|
310
|
+
* R5 — classify the token's time window. `exp` REQUIRED (a JWT without exp is not acceptable here —
|
|
311
|
+
* registry tokens always carry one; classify a missing exp as "expired" so it can never live forever).
|
|
312
|
+
* All values in SECONDS (JWT NumericDate).
|
|
313
|
+
*/
|
|
314
|
+
export declare function classifyTimeClaims(claims: {
|
|
315
|
+
exp?: number;
|
|
316
|
+
nbf?: number;
|
|
317
|
+
}, nowSec: number, clockSkewSec: number): "ok" | "expired";
|
|
318
|
+
/**
|
|
319
|
+
* R6/R7/R9 — map a signature-verified payload to the identity, or reject. Pure: call AFTER the JWT
|
|
320
|
+
* library has verified signature (R1/R2/R3), issuer (R4) and time (R5).
|
|
321
|
+
*/
|
|
322
|
+
export declare function toVerifiedIdentity(payload: {
|
|
323
|
+
sub?: unknown;
|
|
324
|
+
role?: unknown;
|
|
325
|
+
jti?: unknown;
|
|
326
|
+
aud?: unknown;
|
|
327
|
+
scope?: unknown;
|
|
328
|
+
}, cfg?: Pick<VerifierConfig, "audience">): AuthBridgeResult;
|
|
329
|
+
/** Cheap pre-filter: does this bearer LOOK like a JWT (three non-empty dot-separated segments)? Use it to
|
|
330
|
+
* route between the existing static-token branch and this verifier — NOT as any kind of validation. */
|
|
331
|
+
export declare function looksLikeJwt(bearer: string): boolean;
|
|
332
|
+
//# sourceMappingURL=auth-bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-bridge.d.ts","sourceRoot":"","sources":["../../src/api/auth-bridge.ts"],"names":[],"mappings":"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"}
|