@dsh-enhanced/assistant-policy 0.1.7 → 0.1.14

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/README.md CHANGED
@@ -127,9 +127,9 @@ DSH 原生 `user-approval` 仍只负责 open turn 内的即时询问;本插件
127
127
  - 文件系统:创建一个 SQLite 数据库及 WAL/SHM 辅助文件;新目录权限 `0700`,主数据库 `0600`。数据库使用 WAL、`busy_timeout`、外键、FULL synchronous 和前向 schema 版本。
128
128
  - 网络:插件不直接打开 socket;启用隔离自动 reviewer 时会调用宿主 `dsh-llm`,所选 provider route 可能使用网络。provider 的认证与传输仍由宿主拥有,本插件不直接读取其凭据。
129
129
  - 子进程:无。
130
- - 凭据:不读取或保存凭据值。审计按敏感 key/value、shell `command`、授权头、token、password、path/cwd 做脱敏,资源 id 仅保存 SHA-256。自动 reviewer 输入只要需要任何 secret 脱敏就失去自动放行资格并转人工。
130
+ - 凭据:运行时的 Policy service 不读取或保存凭据值。审计按敏感 key/value、shell `command`、授权头、token、password、path/cwd 做脱敏,资源 id 仅保存 SHA-256。自动 reviewer 输入只要需要任何 secret 脱敏就失去自动放行资格并转人工。包另附带两个安装期 CLI(`dsh-permission-setup`、`dsh-model-setup`),仅供安装器/部署者手动调用,不在 Cordis 运行时加载:`dsh-permission-setup` 只原子改写 `settings.yaml` 的 `permission.defaultPreset`;`dsh-model-setup` 原子写入 `agent-default-model`(自定义网关另写 `llm-pi-ai.providers.<route>`),并在显式 `--store-key` 时把仅从环境变量读取的 API Key 原子写入 `$DSH_HOME/.credentials.yaml`(`0600`,目录 `0700`),密钥绝不作为命令行参数、缺失时 fail-closed。`dsh-model-setup` 还支持 agent route(如 `traex-agent`):这类 route 不涉及 API Key,`--enable-in-profile <profile>` 只在该 profile 的 `cordis.patch.yml` 原子改写对应 provider 行的 `enabled`(保留其它行/注释/`!!js`),非 YAML 序列的 patch 会 fail-closed 而非被覆盖。
131
131
  - 浏览器:无。
132
- - 安装脚本:无。
132
+ - 安装脚本:无 npm 生命周期脚本;上述 CLI 仅在被显式调用时运行。
133
133
 
134
134
  审批展示预算由包根的 frozen `APPROVAL_DISPLAY_BUDGET` 统一公开:Delivery 默认文本上限 64 KiB,summary 最多 120 UTF-8 bytes,diff 最多 60 KiB,预留 4 KiB 给渠道渲染。原始 diff 只在 routed dispatch 仍为 pending 时存在;成功 enqueue、批准、拒绝或过期都会在同一状态事务中清除,v2→v3 迁移也会清除无路由、已 enqueue 和终态记录的遗留原文。业务域仍不得把 credential、token 或其他秘密写进 diff。`proposalMaintenanceIntervalMs` 默认每 15 秒有界地过期 stale proposal,设为 `0` 可关闭,最大值为 `2147483647`。审计是 append-only;当前版本不会自动删除记录,部署者应根据自己的隐私与合规周期备份或轮换整个数据库。SQLite 提供单机多连接并发安全,不承诺多主机共享文件系统上的一致性。
135
135
 
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { runModelSetup } from '../lib/model-setup.js'
4
+
5
+ void runModelSetup().catch(error => {
6
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`)
7
+ process.exitCode = 1
8
+ })
package/lib/index.d.ts CHANGED
@@ -13,7 +13,7 @@ export type { NativeFullReviewerReconciliation, PolicyBudgetConfig } from './ser
13
13
  export { APPROVAL_DISPLAY_BUDGET } from './ledger.js';
14
14
  export { ApprovalSettlementConflict, validateApprovalSettlement } from './settlement.js';
15
15
  export type { ApprovalSettlementConflictReason, ApprovalSettlementExpectation, ValidatedApprovalSettlement, } from './settlement.js';
16
- export type { ApprovalDecisionInput, ApprovalDispatchResult, ApprovalDispatchCursor, ApprovalDispatchRoute, ApprovalDispatchSnapshot, ApprovalDispatchState, ApprovalProposalInput, ApprovalProposalLookupInput, ApprovalProposalRecoveryInput, ApprovalProposalRecoveryResult, ApprovalProposalResult, ApprovalProposalStatus, AuditEvent, BudgetReservationResult, ApprovalProposalSnapshot, EmergencyStopState, } from './ledger.js';
16
+ export type { ApprovalDecisionInput, ApprovalDispatchResult, ApprovalDispatchCursor, ApprovalDispatchRoute, ApprovalDispatchRouteV1, ApprovalDispatchRouteV2, ApprovalDispatchSnapshot, ApprovalDispatchState, ApprovalProposalInput, ApprovalProposalLookupInput, ApprovalProposalRecoveryInput, ApprovalProposalRecoveryResult, ApprovalProposalResult, ApprovalProposalStatus, AuditEvent, BudgetReservationResult, ApprovalProposalSnapshot, EmergencyStopState, } from './ledger.js';
17
17
  export * from './types.js';
18
18
  export declare function apply(ctx: Context, config: import('./service.js').Config): void;
19
19
  export default AssistantPolicyService;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,eAAO,MAAM,IAAI,kCAAkC,CAAA;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,OAAO,EAAE,CAAA;AAClD,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,wBAAwB,CAAA;AAC/B,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAC9D,OAAO,EAAE,wCAAwC,EAAE,MAAM,iCAAiC,CAAA;AAC1F,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AACzD,YAAY,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAC9E,OAAO,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACnF,YAAY,EAAE,gCAAgC,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AACxF,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AACrD,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAA;AACxF,YAAY,EACV,gCAAgC,EAChC,6BAA6B,EAC7B,2BAA2B,GAC5B,MAAM,iBAAiB,CAAA;AACxB,YAAY,EACV,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EACrB,qBAAqB,EACrB,2BAA2B,EAC3B,6BAA6B,EAC7B,8BAA8B,EAC9B,sBAAsB,EACtB,sBAAsB,EACtB,UAAU,EACV,uBAAuB,EACvB,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,aAAa,CAAA;AACpB,cAAc,YAAY,CAAA;AAE1B,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,cAAc,EAAE,MAAM,GAAG,IAAI,CAE/E;AAED,eAAe,sBAAsB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,eAAO,MAAM,IAAI,kCAAkC,CAAA;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,OAAO,EAAE,CAAA;AAClD,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,wBAAwB,CAAA;AAC/B,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAC9D,OAAO,EAAE,wCAAwC,EAAE,MAAM,iCAAiC,CAAA;AAC1F,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AACzD,YAAY,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAC9E,OAAO,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACnF,YAAY,EAAE,gCAAgC,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AACxF,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AACrD,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAA;AACxF,YAAY,EACV,gCAAgC,EAChC,6BAA6B,EAC7B,2BAA2B,GAC5B,MAAM,iBAAiB,CAAA;AACxB,YAAY,EACV,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,EACrB,uBAAuB,EACvB,uBAAuB,EACvB,wBAAwB,EACxB,qBAAqB,EACrB,qBAAqB,EACrB,2BAA2B,EAC3B,6BAA6B,EAC7B,8BAA8B,EAC9B,sBAAsB,EACtB,sBAAsB,EACtB,UAAU,EACV,uBAAuB,EACvB,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,aAAa,CAAA;AACpB,cAAc,YAAY,CAAA;AAE1B,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,cAAc,EAAE,MAAM,GAAG,IAAI,CAE/E;AAED,eAAe,sBAAsB,CAAA"}
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,MAAM,CAAC,MAAM,IAAI,GAAG,+BAA+B,CAAA;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,OAAO,EAAE,CAAA;AAClD,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EAAE,wCAAwC,EAAE,MAAM,iCAAiC,CAAA;AAC1F,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAEzD,OAAO,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AAEnF,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AACrD,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAA;AAwBxF,cAAc,YAAY,CAAA;AAE1B,MAAM,UAAU,KAAK,CAAC,GAAY,EAAE,MAAqC;IACvE,IAAI,sBAAsB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;AACzC,CAAC;AAED,eAAe,sBAAsB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,MAAM,CAAC,MAAM,IAAI,GAAG,+BAA+B,CAAA;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,OAAO,EAAE,CAAA;AAClD,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EAAE,wCAAwC,EAAE,MAAM,iCAAiC,CAAA;AAC1F,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAEzD,OAAO,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AAEnF,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AACrD,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAA;AA0BxF,cAAc,YAAY,CAAA;AAE1B,MAAM,UAAU,KAAK,CAAC,GAAY,EAAE,MAAqC;IACvE,IAAI,sBAAsB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;AACzC,CAAC;AAED,eAAe,sBAAsB,CAAA"}
package/lib/ledger.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type PolicyLedgerErrorCode = 'budget-exhausted' | 'idempotency-conflict' | 'invalid-input' | 'invalid-path' | 'invalid-state' | 'not-found' | 'schema-too-new' | 'unauthorized' | 'version-conflict';
1
+ export type PolicyLedgerErrorCode = 'budget-exhausted' | 'idempotency-conflict' | 'invalid-input' | 'invalid-path' | 'invalid-state' | 'legacy-unverifiable' | 'not-found' | 'schema-too-new' | 'unauthorized' | 'version-conflict';
2
2
  export declare class PolicyLedgerError extends Error {
3
3
  readonly code: PolicyLedgerErrorCode;
4
4
  constructor(code: PolicyLedgerErrorCode, message: string);
@@ -48,7 +48,7 @@ export interface ApprovalProposalInput {
48
48
  diff: string;
49
49
  summary: string;
50
50
  ttlMs: number;
51
- dispatch?: Readonly<ApprovalDispatchRoute>;
51
+ dispatch?: Readonly<ApprovalDispatchRouteV2>;
52
52
  }
53
53
  /**
54
54
  * Durable-domain creation input with an absolute deadline. Unlike `ttlMs`, the
@@ -67,14 +67,28 @@ export interface ApprovalProposalRecoveryInput {
67
67
  diff: string;
68
68
  summary: string;
69
69
  notAfter: number;
70
- dispatch?: Readonly<ApprovalDispatchRoute>;
70
+ dispatch?: Readonly<ApprovalDispatchRouteV2>;
71
71
  }
72
- export interface ApprovalDispatchRoute {
72
+ export interface ApprovalDispatchRouteV1 {
73
+ /** Omitted by pre-v2 callers and persisted explicitly as route version 1. */
74
+ routeVersion?: 1;
73
75
  sourceId: string;
74
76
  bindingId: string;
75
77
  workspace: string;
76
78
  principal: string;
77
79
  }
80
+ export interface ApprovalDispatchRouteV2 {
81
+ routeVersion: 2;
82
+ sourceId: string;
83
+ bindingId: string;
84
+ bindingVersion: number;
85
+ bindingGeneration: number;
86
+ workspace: string;
87
+ principal: string;
88
+ principalRecordId: string;
89
+ principalVersion: number;
90
+ }
91
+ export type ApprovalDispatchRoute = ApprovalDispatchRouteV1 | ApprovalDispatchRouteV2;
78
92
  export type ApprovalProposalStatus = 'approved' | 'expired' | 'pending' | 'rejected';
79
93
  export interface ApprovalProposalResult {
80
94
  proposalId: string;
@@ -140,7 +154,7 @@ export interface ApprovalProposalLookupInput {
140
154
  id: string;
141
155
  };
142
156
  }
143
- export type ApprovalDispatchState = 'enqueued' | 'pending';
157
+ export type ApprovalDispatchState = 'enqueued' | 'pending' | 'quarantined';
144
158
  /** Stable high-water mark for a pending-dispatch scan. */
145
159
  export interface ApprovalDispatchCursor {
146
160
  createdAt: number;
@@ -150,12 +164,8 @@ export interface ApprovalDispatchCursor {
150
164
  * Immutable, policy-derived payload and route for one durable approval delivery.
151
165
  * Callers can choose when to enqueue it, but cannot substitute display content.
152
166
  */
153
- export interface ApprovalDispatchSnapshot {
167
+ interface ApprovalDispatchSnapshotBase {
154
168
  proposalId: string;
155
- sourceId: string;
156
- bindingId: string;
157
- workspace: string;
158
- principal: string;
159
169
  requester: string;
160
170
  action: string;
161
171
  resource: {
@@ -173,6 +183,9 @@ export interface ApprovalDispatchSnapshot {
173
183
  enqueuedAt: number | undefined;
174
184
  version: number;
175
185
  }
186
+ export type ApprovalDispatchSnapshot = Readonly<ApprovalDispatchSnapshotBase & ((ApprovalDispatchRouteV1 & {
187
+ routeVersion: 1;
188
+ }) | ApprovalDispatchRouteV2)>;
176
189
  export interface ApprovalDispatchResult {
177
190
  proposalId: string;
178
191
  state: 'enqueued';
@@ -250,4 +263,5 @@ export declare class PolicyLedger {
250
263
  lastAuditSequence: number;
251
264
  };
252
265
  }
266
+ export {};
253
267
  //# sourceMappingURL=ledger.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../src/ledger.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../src/ledger.ts"],"names":[],"mappings":"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"}
package/lib/ledger.js CHANGED
@@ -22,6 +22,7 @@ const textLimits = Object.freeze({
22
22
  action: 256,
23
23
  resourceId: 4_096,
24
24
  workspace: 4_096,
25
+ principalRecordId: 500,
25
26
  summary: APPROVAL_DISPLAY_BUDGET.maxSummaryBytes,
26
27
  diff: APPROVAL_DISPLAY_BUDGET.maxDiffBytes,
27
28
  reason: 2_048,
@@ -37,23 +38,103 @@ function requireBoundedText(value, field, maxBytes, options = {}) {
37
38
  throw new PolicyLedgerError('invalid-input', `${field} must not exceed ${maxBytes} UTF-8 bytes`);
38
39
  }
39
40
  }
41
+ function requirePositiveSafeInteger(value, field) {
42
+ if (!Number.isSafeInteger(value) || value <= 0) {
43
+ throw new PolicyLedgerError('invalid-input', `${field} must be a positive safe integer`);
44
+ }
45
+ }
46
+ function approvalDispatchRouteVersion(route) {
47
+ return route.routeVersion ?? 1;
48
+ }
49
+ function storedApprovalDispatchRoute(row) {
50
+ const common = {
51
+ sourceId: row.source_id,
52
+ bindingId: row.binding_id,
53
+ workspace: row.workspace,
54
+ principal: row.dispatch_principal,
55
+ };
56
+ if (row.route_version === 1) {
57
+ if (row.binding_version !== null || row.binding_generation !== null
58
+ || row.principal_record_id !== null || row.principal_version !== null) {
59
+ throw new PolicyLedgerError('invalid-state', 'legacy approval dispatch contains v2 authority fields');
60
+ }
61
+ return Object.freeze({ routeVersion: 1, ...common });
62
+ }
63
+ const bindingVersion = row.binding_version;
64
+ const bindingGeneration = row.binding_generation;
65
+ const principalRecordId = row.principal_record_id;
66
+ const principalVersion = row.principal_version;
67
+ if (row.route_version !== 2
68
+ || !Number.isSafeInteger(bindingVersion) || bindingVersion === null || bindingVersion <= 0
69
+ || !Number.isSafeInteger(bindingGeneration) || bindingGeneration === null || bindingGeneration <= 0
70
+ || typeof principalRecordId !== 'string'
71
+ || principalRecordId.trim() === ''
72
+ || Buffer.byteLength(principalRecordId, 'utf8') > textLimits.principalRecordId
73
+ || !Number.isSafeInteger(principalVersion) || principalVersion === null || principalVersion <= 0) {
74
+ throw new PolicyLedgerError('invalid-state', 'approval dispatch v2 authority fields are invalid');
75
+ }
76
+ return Object.freeze({
77
+ routeVersion: 2,
78
+ ...common,
79
+ bindingVersion,
80
+ bindingGeneration,
81
+ principalRecordId,
82
+ principalVersion,
83
+ });
84
+ }
85
+ function sameApprovalDispatchRoute(stored, input) {
86
+ const routeVersion = approvalDispatchRouteVersion(input);
87
+ if (stored.routeVersion !== routeVersion
88
+ || stored.sourceId !== input.sourceId
89
+ || stored.bindingId !== input.bindingId
90
+ || stored.workspace !== input.workspace
91
+ || stored.principal !== input.principal)
92
+ return false;
93
+ return routeVersion === 1 || (stored.routeVersion === 2 && input.routeVersion === 2
94
+ && stored.bindingVersion === input.bindingVersion
95
+ && stored.bindingGeneration === input.bindingGeneration
96
+ && stored.principalRecordId === input.principalRecordId
97
+ && stored.principalVersion === input.principalVersion);
98
+ }
40
99
  function dispatchPayloadHash(input) {
41
- const canonicalPayload = JSON.stringify([
42
- input.proposalId,
43
- input.route.sourceId,
44
- input.route.bindingId,
45
- input.route.workspace,
46
- input.route.principal,
47
- input.requester,
48
- input.action,
49
- input.resourceKind,
50
- input.resourceId,
51
- input.summary,
52
- input.diff,
53
- input.diffHash,
54
- input.expiresAt,
55
- input.proposalVersion,
56
- ]);
100
+ const canonicalPayload = JSON.stringify(input.route.routeVersion === 2
101
+ ? [
102
+ 'approval-dispatch-payload-v2',
103
+ input.proposalId,
104
+ input.route.sourceId,
105
+ input.route.bindingId,
106
+ input.route.bindingVersion,
107
+ input.route.bindingGeneration,
108
+ input.route.workspace,
109
+ input.route.principal,
110
+ input.route.principalRecordId,
111
+ input.route.principalVersion,
112
+ input.requester,
113
+ input.action,
114
+ input.resourceKind,
115
+ input.resourceId,
116
+ input.summary,
117
+ input.diff,
118
+ input.diffHash,
119
+ input.expiresAt,
120
+ input.proposalVersion,
121
+ ]
122
+ : [
123
+ input.proposalId,
124
+ input.route.sourceId,
125
+ input.route.bindingId,
126
+ input.route.workspace,
127
+ input.route.principal,
128
+ input.requester,
129
+ input.action,
130
+ input.resourceKind,
131
+ input.resourceId,
132
+ input.summary,
133
+ input.diff,
134
+ input.diffHash,
135
+ input.expiresAt,
136
+ input.proposalVersion,
137
+ ]);
57
138
  return createHash('sha256').update(canonicalPayload).digest('hex');
58
139
  }
59
140
  /**
@@ -62,23 +143,45 @@ function dispatchPayloadHash(input) {
62
143
  */
63
144
  function approvalRecoveryIntentHash(input) {
64
145
  const dispatch = input.dispatch;
65
- const canonicalIntent = JSON.stringify([
66
- 'approval-recovery-intent-v1',
67
- input.idempotencyKey,
68
- input.notAfter,
69
- input.requester,
70
- input.principal,
71
- input.action,
72
- input.resource.kind,
73
- input.resource.id,
74
- input.diff,
75
- input.summary,
76
- dispatch === undefined ? 0 : 1,
77
- dispatch?.sourceId ?? null,
78
- dispatch?.bindingId ?? null,
79
- dispatch?.workspace ?? null,
80
- dispatch?.principal ?? null,
81
- ]);
146
+ const canonicalIntent = JSON.stringify(dispatch?.routeVersion === 2
147
+ ? [
148
+ 'approval-recovery-intent-v2',
149
+ input.idempotencyKey,
150
+ input.notAfter,
151
+ input.requester,
152
+ input.principal,
153
+ input.action,
154
+ input.resource.kind,
155
+ input.resource.id,
156
+ input.diff,
157
+ input.summary,
158
+ 1,
159
+ dispatch.sourceId,
160
+ dispatch.bindingId,
161
+ dispatch.bindingVersion,
162
+ dispatch.bindingGeneration,
163
+ dispatch.workspace,
164
+ dispatch.principal,
165
+ dispatch.principalRecordId,
166
+ dispatch.principalVersion,
167
+ ]
168
+ : [
169
+ 'approval-recovery-intent-v1',
170
+ input.idempotencyKey,
171
+ input.notAfter,
172
+ input.requester,
173
+ input.principal,
174
+ input.action,
175
+ input.resource.kind,
176
+ input.resource.id,
177
+ input.diff,
178
+ input.summary,
179
+ dispatch === undefined ? 0 : 1,
180
+ dispatch?.sourceId ?? null,
181
+ dispatch?.bindingId ?? null,
182
+ dispatch?.workspace ?? null,
183
+ dispatch?.principal ?? null,
184
+ ]);
82
185
  return createHash('sha256').update(canonicalIntent).digest('hex');
83
186
  }
84
187
  function requirePositive(value, field) {
@@ -319,10 +422,25 @@ export class PolicyLedger {
319
422
  requireBoundedText(input.summary, 'summary', textLimits.summary);
320
423
  requireBoundedText(input.diff, 'diff', textLimits.diff, { allowEmpty: true });
321
424
  if (input.dispatch !== undefined) {
425
+ const routeVersion = input.dispatch.routeVersion;
426
+ if (routeVersion !== 2) {
427
+ throw new PolicyLedgerError('invalid-input', 'new approval dispatch routes must use routeVersion 2');
428
+ }
322
429
  requireBoundedText(input.dispatch.sourceId, 'dispatch.sourceId', textLimits.id);
323
430
  requireBoundedText(input.dispatch.bindingId, 'dispatch.bindingId', textLimits.id);
324
431
  requireBoundedText(input.dispatch.workspace, 'dispatch.workspace', textLimits.workspace);
325
432
  requireBoundedText(input.dispatch.principal, 'dispatch.principal', textLimits.id);
433
+ const route = input.dispatch;
434
+ requirePositiveSafeInteger(route.bindingVersion, 'dispatch.bindingVersion');
435
+ requirePositiveSafeInteger(route.bindingGeneration, 'dispatch.bindingGeneration');
436
+ if (typeof route.principalRecordId !== 'string') {
437
+ throw new PolicyLedgerError('invalid-input', 'dispatch.principalRecordId must be text');
438
+ }
439
+ requireBoundedText(route.principalRecordId, 'dispatch.principalRecordId', textLimits.principalRecordId);
440
+ if (route.principalRecordId.normalize('NFC').trim() !== route.principalRecordId) {
441
+ throw new PolicyLedgerError('invalid-input', 'dispatch.principalRecordId must be normalized and trimmed');
442
+ }
443
+ requirePositiveSafeInteger(route.principalVersion, 'dispatch.principalVersion');
326
444
  if (!isAbsolute(input.dispatch.workspace)) {
327
445
  throw new PolicyLedgerError('invalid-path', 'dispatch.workspace must be absolute');
328
446
  }
@@ -342,16 +460,17 @@ export class PolicyLedger {
342
460
  }
343
461
  #assertExistingProposalMatches(existing, input, diffHash, expiryMatches) {
344
462
  const existingDispatch = this.#database.prepare(`
345
- SELECT source_id, binding_id, workspace, principal, state, payload_hash, proposal_version
463
+ SELECT route_version, source_id, binding_id, binding_version, binding_generation,
464
+ workspace, principal AS dispatch_principal, principal_record_id, principal_version,
465
+ state, payload_hash, proposal_version
346
466
  FROM approval_dispatches WHERE proposal_id = ?
347
467
  `).get(existing.id);
468
+ const storedRoute = existingDispatch === undefined
469
+ ? undefined
470
+ : storedApprovalDispatchRoute(existingDispatch);
348
471
  const sameDispatch = input.dispatch === undefined
349
472
  ? existingDispatch === undefined
350
- : existingDispatch !== undefined
351
- && existingDispatch.source_id === input.dispatch.sourceId
352
- && existingDispatch.binding_id === input.dispatch.bindingId
353
- && existingDispatch.workspace === input.dispatch.workspace
354
- && existingDispatch.principal === input.dispatch.principal;
473
+ : storedRoute !== undefined && sameApprovalDispatchRoute(storedRoute, input.dispatch);
355
474
  const sameDiff = input.dispatch === undefined
356
475
  ? existingDispatch === undefined && existing.diff_hash === diffHash
357
476
  : existingDispatch !== undefined
@@ -407,10 +526,11 @@ export class PolicyLedger {
407
526
  });
408
527
  this.#database.prepare(`
409
528
  INSERT INTO approval_dispatches(
410
- proposal_id, source_id, binding_id, workspace, principal,
529
+ proposal_id, route_version, source_id, binding_id, binding_version,
530
+ binding_generation, workspace, principal, principal_record_id, principal_version,
411
531
  state, payload_hash, created_at, proposal_version
412
- ) VALUES (?, ?, ?, ?, ?, 'pending', ?, ?, 1)
413
- `).run(id, input.dispatch.sourceId, input.dispatch.bindingId, input.dispatch.workspace, input.dispatch.principal, payloadHash, createdAt);
532
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending', ?, ?, 1)
533
+ `).run(id, approvalDispatchRouteVersion(input.dispatch), input.dispatch.sourceId, input.dispatch.bindingId, input.dispatch.routeVersion === 2 ? input.dispatch.bindingVersion : null, input.dispatch.routeVersion === 2 ? input.dispatch.bindingGeneration : null, input.dispatch.workspace, input.dispatch.principal, input.dispatch.routeVersion === 2 ? input.dispatch.principalRecordId : null, input.dispatch.routeVersion === 2 ? input.dispatch.principalVersion : null, payloadHash, createdAt);
414
534
  }
415
535
  return {
416
536
  proposalId: id,
@@ -632,8 +752,10 @@ export class PolicyLedger {
632
752
  const cursorCreatedAt = after?.createdAt ?? null;
633
753
  const cursorProposalId = after?.proposalId ?? '';
634
754
  const rows = this.#database.prepare(`
635
- SELECT d.proposal_id, d.source_id, d.binding_id, d.workspace,
636
- d.principal AS dispatch_principal, d.state, d.payload_hash,
755
+ SELECT d.proposal_id, d.route_version, d.source_id, d.binding_id,
756
+ d.binding_version, d.binding_generation, d.workspace,
757
+ d.principal AS dispatch_principal, d.principal_record_id,
758
+ d.principal_version, d.state, d.payload_hash,
637
759
  d.created_at AS dispatch_created_at, d.enqueued_at,
638
760
  d.version AS dispatch_version, d.proposal_version, p.requester,
639
761
  p.principal AS proposal_principal, p.action, p.resource_kind,
@@ -641,7 +763,8 @@ export class PolicyLedger {
641
763
  p.expires_at, p.version AS current_proposal_version
642
764
  FROM approval_dispatches d
643
765
  JOIN approval_proposals p ON p.id = d.proposal_id
644
- WHERE d.state = 'pending' AND p.status = 'pending' AND p.expires_at > ?
766
+ WHERE d.state = 'pending' AND d.route_version = 2
767
+ AND p.status = 'pending' AND p.expires_at > ?
645
768
  AND (? IS NULL OR d.created_at > ? OR (d.created_at = ? AND d.proposal_id > ?))
646
769
  ORDER BY d.created_at ASC, d.proposal_id ASC
647
770
  LIMIT ?
@@ -655,6 +778,10 @@ export class PolicyLedger {
655
778
  }
656
779
  return this.#transaction(() => {
657
780
  const dispatch = this.#dispatch(proposalId);
781
+ const route = storedApprovalDispatchRoute(dispatch);
782
+ if (route.routeVersion !== 2) {
783
+ throw new PolicyLedgerError('legacy-unverifiable', 'legacy approval dispatch cannot be enqueued or replayed');
784
+ }
658
785
  if (dispatch.state === 'enqueued') {
659
786
  return this.#dispatchResult(dispatch, true);
660
787
  }
@@ -727,8 +854,10 @@ export class PolicyLedger {
727
854
  }
728
855
  #dispatch(proposalId) {
729
856
  const dispatch = this.#database.prepare(`
730
- SELECT d.proposal_id, d.source_id, d.binding_id, d.workspace,
731
- d.principal AS dispatch_principal, d.state, d.payload_hash,
857
+ SELECT d.proposal_id, d.route_version, d.source_id, d.binding_id,
858
+ d.binding_version, d.binding_generation, d.workspace,
859
+ d.principal AS dispatch_principal, d.principal_record_id,
860
+ d.principal_version, d.state, d.payload_hash,
732
861
  d.created_at AS dispatch_created_at, d.enqueued_at,
733
862
  d.version AS dispatch_version, d.proposal_version, p.requester,
734
863
  p.principal AS proposal_principal, p.action, p.resource_kind,
@@ -753,14 +882,10 @@ export class PolicyLedger {
753
882
  if (row.current_proposal_version !== row.proposal_version) {
754
883
  throw new PolicyLedgerError('invalid-state', 'approval dispatch proposal version changed');
755
884
  }
885
+ const route = storedApprovalDispatchRoute(row);
756
886
  const expectedPayloadHash = dispatchPayloadHash({
757
887
  proposalId: row.proposal_id,
758
- route: {
759
- sourceId: row.source_id,
760
- bindingId: row.binding_id,
761
- workspace: row.workspace,
762
- principal: row.dispatch_principal,
763
- },
888
+ route,
764
889
  requester: row.requester,
765
890
  action: row.action,
766
891
  resourceKind: row.resource_kind,
@@ -776,9 +901,7 @@ export class PolicyLedger {
776
901
  }
777
902
  return Object.freeze({
778
903
  proposalId: row.proposal_id,
779
- sourceId: row.source_id,
780
- bindingId: row.binding_id,
781
- workspace: row.workspace,
904
+ ...route,
782
905
  principal: row.proposal_principal,
783
906
  requester: row.requester,
784
907
  action: row.action,