@the-ai-company/cbio-node-runtime 1.62.0 → 1.62.2
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 +8 -15
- package/dist/clients/agent/client.js +1 -4
- package/dist/clients/agent/client.js.map +1 -1
- package/dist/clients/owner/client.d.ts +0 -1
- package/dist/clients/owner/client.js +5 -23
- package/dist/clients/owner/client.js.map +1 -1
- package/dist/clients/owner/contracts.d.ts +2 -3
- package/dist/vault-core/contracts.d.ts +18 -30
- package/dist/vault-core/contracts.js.map +1 -1
- package/dist/vault-core/core.d.ts +1 -1
- package/dist/vault-core/core.js +80 -187
- package/dist/vault-core/core.js.map +1 -1
- package/dist/vault-core/index.d.ts +1 -1
- package/dist/vault-core/index.js.map +1 -1
- package/dist/vault-core/read-policy.d.ts +2 -0
- package/dist/vault-core/read-policy.js +40 -0
- package/dist/vault-core/read-policy.js.map +1 -0
- package/dist/vault-core/tool-metadata.js +1 -1
- package/dist/vault-core/tool-metadata.js.map +1 -1
- package/dist/vault-ingress/index.d.ts +2 -8
- package/dist/vault-ingress/index.js +12 -68
- package/dist/vault-ingress/index.js.map +1 -1
- package/dist/vault-ingress/remote-transport.js +1 -4
- package/dist/vault-ingress/remote-transport.js.map +1 -1
- package/docs/REFERENCE.md +3 -6
- package/docs/api/README.md +2 -2
- package/docs/api/classes/IdentityError.md +1 -1
- package/docs/api/classes/OwnerClientError.md +1 -1
- package/docs/api/classes/VaultCore.md +1 -17
- package/docs/api/classes/VaultCoreError.md +1 -1
- package/docs/api/enumerations/IdentityErrorCode.md +1 -1
- package/docs/api/enumerations/OwnerClientErrorCode.md +1 -1
- package/docs/api/functions/createAgentClient.md +1 -1
- package/docs/api/functions/createIdentity.md +1 -1
- package/docs/api/functions/createOwnerHttpFlowBoundary.md +1 -1
- package/docs/api/functions/createOwnerSession.md +1 -1
- package/docs/api/functions/createPersistentVaultCoreDependencies.md +1 -1
- package/docs/api/functions/createStandardAcquireBoundary.md +1 -1
- package/docs/api/functions/createStandardDispatchBoundary.md +1 -1
- package/docs/api/functions/createVault.md +1 -1
- package/docs/api/functions/createVaultClient.md +1 -1
- package/docs/api/functions/createVaultCore.md +1 -1
- package/docs/api/functions/createVaultCoreDependencies.md +1 -1
- package/docs/api/functions/createVaultService.md +1 -1
- package/docs/api/functions/createWorkspaceStorage.md +1 -1
- package/docs/api/functions/deriveIdentityId.md +1 -1
- package/docs/api/functions/deriveVaultWorkingKeyFromPassword.md +1 -1
- package/docs/api/functions/getDefaultWorkspaceDir.md +1 -1
- package/docs/api/functions/handleVaultAgentControlHttp.md +1 -1
- package/docs/api/functions/handleVaultHttpDispatch.md +1 -1
- package/docs/api/functions/initializeVaultCustody.md +1 -1
- package/docs/api/functions/listVaults.md +1 -1
- package/docs/api/functions/readVaultProfile.md +1 -1
- package/docs/api/functions/recoverVault.md +1 -1
- package/docs/api/functions/recoverVaultWorkingKey.md +1 -1
- package/docs/api/functions/restoreIdentity.md +1 -1
- package/docs/api/functions/updateVaultMetadata.md +1 -1
- package/docs/api/functions/wrapVaultCoreAsVaultService.md +1 -1
- package/docs/api/functions/writeVaultProfile.md +1 -1
- package/docs/api/interfaces/AgentClient.md +1 -1
- package/docs/api/interfaces/AgentDispatchIntent.md +1 -1
- package/docs/api/interfaces/AgentDispatchTransport.md +1 -1
- package/docs/api/interfaces/AgentIdentity.md +1 -1
- package/docs/api/interfaces/AgentSigner.md +1 -1
- package/docs/api/interfaces/AgentSubmitCapabilityRequestInput.md +1 -1
- package/docs/api/interfaces/CbioRuntime.md +1 -1
- package/docs/api/interfaces/CreateAgentClientOptions.md +1 -1
- package/docs/api/interfaces/CreateIdentityOptions.md +1 -1
- package/docs/api/interfaces/CreateOwnerSessionOptions.md +1 -1
- package/docs/api/interfaces/CreatePersistentVaultCoreDependenciesOptions.md +1 -1
- package/docs/api/interfaces/CreateVaultClientOptions.md +1 -1
- package/docs/api/interfaces/CreateVaultOptions.md +1 -1
- package/docs/api/interfaces/CreatedVault.md +1 -1
- package/docs/api/interfaces/DefaultPolicyEngineOptions.md +1 -1
- package/docs/api/interfaces/IStorageProvider.md +1 -1
- package/docs/api/interfaces/InitializeVaultCustodyOptions.md +1 -1
- package/docs/api/interfaces/InitializedVaultCustody.md +1 -1
- package/docs/api/interfaces/OwnerAgentProvisionResult.md +1 -1
- package/docs/api/interfaces/OwnerCreateSecretInput.md +1 -1
- package/docs/api/interfaces/OwnerRemoveSecretInput.md +1 -1
- package/docs/api/interfaces/OwnerSensitiveActionConfirmation.md +1 -1
- package/docs/api/interfaces/OwnerSensitiveActionContext.md +1 -1
- package/docs/api/interfaces/OwnerSession.md +1 -1
- package/docs/api/interfaces/OwnerUpdateSecretInput.md +1 -1
- package/docs/api/interfaces/RecoverVaultOptions.md +1 -1
- package/docs/api/interfaces/RecoveredVault.md +1 -1
- package/docs/api/interfaces/RestoreIdentityOptions.md +1 -1
- package/docs/api/interfaces/Signer.md +1 -1
- package/docs/api/interfaces/VaultApproveCapabilityRequestInput.md +1 -1
- package/docs/api/interfaces/VaultApproveDispatchInput.md +1 -1
- package/docs/api/interfaces/VaultAuditQueryInput.md +1 -1
- package/docs/api/interfaces/VaultClient.md +1 -17
- package/docs/api/interfaces/VaultCoreDependenciesOptions.md +1 -1
- package/docs/api/interfaces/VaultCreateAgentInput.md +1 -1
- package/docs/api/interfaces/VaultExportSecretInput.md +1 -1
- package/docs/api/interfaces/VaultGrantCapabilityInput.md +1 -1
- package/docs/api/interfaces/VaultGrantCapabilityRequest.md +1 -1
- package/docs/api/interfaces/VaultIdentity.md +1 -1
- package/docs/api/interfaces/VaultImportAgentInput.md +1 -1
- package/docs/api/interfaces/VaultIssueSessionTokenInput.md +1 -1
- package/docs/api/interfaces/VaultListAgentsInput.md +1 -1
- package/docs/api/interfaces/VaultListCapabilitiesInput.md +1 -1
- package/docs/api/interfaces/VaultListSecretsInput.md +1 -1
- package/docs/api/interfaces/VaultMetadata.md +1 -1
- package/docs/api/interfaces/VaultObject.md +1 -1
- package/docs/api/interfaces/VaultProfile.md +1 -1
- package/docs/api/interfaces/VaultReadAgentPrivateKeyInput.md +1 -1
- package/docs/api/interfaces/VaultReadSecretPlaintextInput.md +1 -1
- package/docs/api/interfaces/VaultRegisterFlowInput.md +1 -1
- package/docs/api/interfaces/VaultRevokeCapabilityInput.md +1 -1
- package/docs/api/interfaces/VaultRevokeSessionTokenInput.md +1 -1
- package/docs/api/interfaces/VaultSigner.md +1 -1
- package/docs/api/interfaces/VaultSubmitCapabilityRequestInput.md +1 -1
- package/docs/api/interfaces/VaultUpdateAgentInput.md +1 -1
- package/docs/api/type-aliases/AgentCapabilityEnvelope.md +1 -1
- package/docs/api/type-aliases/AgentVisibleSecretRecord.md +1 -1
- package/docs/api/type-aliases/CbioRuntimeModule.md +1 -1
- package/docs/api/type-aliases/OwnerGrantCapabilityInput.md +1 -1
- package/docs/api/variables/DEFAULT_VAULT_KEY_CUSTODY_BLOB_KEY.md +1 -1
- package/docs/zh/README.md +5 -7
- package/package.json +1 -1
package/dist/vault-core/core.js
CHANGED
|
@@ -3,67 +3,8 @@ import { VaultCoreError } from "./errors.js";
|
|
|
3
3
|
import { verifySignature } from "../protocol/crypto.js";
|
|
4
4
|
import { getAgentToolbox } from "./tool-metadata.js";
|
|
5
5
|
import { InMemoryRequestRecordRegistry } from "./defaults.js";
|
|
6
|
+
import { applyResponseReadPolicy } from "./read-policy.js";
|
|
6
7
|
const VAULT_MASTER_ID = "vault-master";
|
|
7
|
-
function redactResponseShapeValue(value) {
|
|
8
|
-
if (value === null || value === undefined) {
|
|
9
|
-
return null;
|
|
10
|
-
}
|
|
11
|
-
if (Array.isArray(value)) {
|
|
12
|
-
return value.map((entry) => redactResponseShapeValue(entry));
|
|
13
|
-
}
|
|
14
|
-
if (typeof value === "object") {
|
|
15
|
-
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, redactResponseShapeValue(entry)]));
|
|
16
|
-
}
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
function applyResponseReadPolicy(body, policy) {
|
|
20
|
-
if (body === undefined)
|
|
21
|
-
return body;
|
|
22
|
-
if (policy.mode === "full")
|
|
23
|
-
return body;
|
|
24
|
-
if (policy.mode === "none")
|
|
25
|
-
return undefined;
|
|
26
|
-
let parsed;
|
|
27
|
-
try {
|
|
28
|
-
parsed = JSON.parse(body);
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
return policy.mode === "shape_only" ? JSON.stringify(null) : undefined;
|
|
32
|
-
}
|
|
33
|
-
if (policy.mode === "shape_only") {
|
|
34
|
-
return JSON.stringify(redactResponseShapeValue(parsed));
|
|
35
|
-
}
|
|
36
|
-
if (policy.mode !== "custom") {
|
|
37
|
-
return body;
|
|
38
|
-
}
|
|
39
|
-
const result = {};
|
|
40
|
-
for (const path of policy.paths ?? []) {
|
|
41
|
-
const segments = path.split(".").filter(Boolean);
|
|
42
|
-
if (!segments.length)
|
|
43
|
-
continue;
|
|
44
|
-
let source = parsed;
|
|
45
|
-
let valid = true;
|
|
46
|
-
for (const segment of segments) {
|
|
47
|
-
if (source && typeof source === "object" && segment in source) {
|
|
48
|
-
source = source[segment];
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
valid = false;
|
|
52
|
-
break;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
if (!valid)
|
|
56
|
-
continue;
|
|
57
|
-
let target = result;
|
|
58
|
-
for (let index = 0; index < segments.length - 1; index += 1) {
|
|
59
|
-
const segment = segments[index];
|
|
60
|
-
target[segment] ??= {};
|
|
61
|
-
target = target[segment];
|
|
62
|
-
}
|
|
63
|
-
target[segments[segments.length - 1]] = source;
|
|
64
|
-
}
|
|
65
|
-
return JSON.stringify(result);
|
|
66
|
-
}
|
|
67
8
|
function toAuditEntry(deps, actor, action, outcome, detail, options) {
|
|
68
9
|
return {
|
|
69
10
|
entryId: deps.ids.newAuditEntryId(),
|
|
@@ -176,14 +117,7 @@ export class VaultCore {
|
|
|
176
117
|
scope: state.write.scope,
|
|
177
118
|
methods: [...state.write.methods],
|
|
178
119
|
},
|
|
179
|
-
read: state.
|
|
180
|
-
? {
|
|
181
|
-
mode: state.read.mode,
|
|
182
|
-
paths: state.read.paths ? [...state.read.paths] : undefined,
|
|
183
|
-
}
|
|
184
|
-
: {
|
|
185
|
-
mode: "none",
|
|
186
|
-
},
|
|
120
|
+
read: { paths: [...(state.readGrant ?? [])] },
|
|
187
121
|
issuedAt: state.issuedAt ?? state.requestedAt,
|
|
188
122
|
expiresAt: state.expiresAt,
|
|
189
123
|
rateLimit: state.rateLimit,
|
|
@@ -204,26 +138,35 @@ export class VaultCore {
|
|
|
204
138
|
methods: [...state.write.methods],
|
|
205
139
|
},
|
|
206
140
|
read: {
|
|
207
|
-
|
|
208
|
-
paths: state.read.paths ? [...state.read.paths] : undefined,
|
|
141
|
+
paths: [...state.read.paths],
|
|
209
142
|
},
|
|
210
143
|
issuedAt: state.issuedAt,
|
|
211
144
|
requestedAt: state.requestedAt,
|
|
212
145
|
expiresAt: state.expiresAt,
|
|
213
146
|
rateLimit: state.rateLimit,
|
|
214
147
|
skipAudit: state.skipAudit,
|
|
148
|
+
writeGrant: state.writeGrant,
|
|
149
|
+
writeGrantedAt: state.writeGrantedAt,
|
|
150
|
+
readGrant: state.readGrant ? [...state.readGrant] : null,
|
|
151
|
+
readGrantedAt: state.readGrantedAt,
|
|
215
152
|
reason: state.reason,
|
|
216
153
|
secretId: state.secretId,
|
|
217
154
|
targetUrl: state.targetUrl,
|
|
218
|
-
actions: {
|
|
219
|
-
write: { ...state.actions.write },
|
|
220
|
-
read: { ...state.actions.read },
|
|
221
|
-
},
|
|
222
155
|
}));
|
|
223
156
|
}
|
|
224
157
|
_isExecutablePendingState(state) {
|
|
225
158
|
return !!(state.requestId && state.targetUrl && state.proof);
|
|
226
159
|
}
|
|
160
|
+
async _resolveRequestState(record) {
|
|
161
|
+
const byRequestId = await this._deps.capabilityStates.getByRequestId(this._deps.vaultId, record.requestId);
|
|
162
|
+
if (byRequestId) {
|
|
163
|
+
return byRequestId;
|
|
164
|
+
}
|
|
165
|
+
if (record.capabilityId) {
|
|
166
|
+
return this._deps.capabilityStates.getByCapabilityId(this._deps.vaultId, record.agentId, record.capabilityId);
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
227
170
|
async _executePendingCapabilityState(command, mode) {
|
|
228
171
|
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
229
172
|
throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
|
|
@@ -232,8 +175,8 @@ export class VaultCore {
|
|
|
232
175
|
if (!pending) {
|
|
233
176
|
throw new VaultCoreError("capability action record not found", "VAULT_REQUEST_NOT_FOUND");
|
|
234
177
|
}
|
|
235
|
-
if (pending.
|
|
236
|
-
throw new VaultCoreError("write
|
|
178
|
+
if (pending.writeGrant !== "once" && pending.writeGrant !== "always") {
|
|
179
|
+
throw new VaultCoreError("write grant required before execution", "VAULT_WRITE_DENIED");
|
|
237
180
|
}
|
|
238
181
|
if (mode === "once" && pending.source !== "dispatch_discovery") {
|
|
239
182
|
throw new VaultCoreError("one-time execution is only available for dispatch discovery requests", "VAULT_WRITE_DENIED");
|
|
@@ -250,10 +193,7 @@ export class VaultCore {
|
|
|
250
193
|
scope: pending.targetUrl ?? pending.write.scope,
|
|
251
194
|
methods: [...pending.write.methods],
|
|
252
195
|
},
|
|
253
|
-
read: {
|
|
254
|
-
mode: pending.read.mode,
|
|
255
|
-
paths: pending.read.paths ? [...pending.read.paths] : undefined,
|
|
256
|
-
},
|
|
196
|
+
read: { paths: [...(pending.readGrant ?? [])] },
|
|
257
197
|
issuedAt,
|
|
258
198
|
expiresAt: pending.expiresAt,
|
|
259
199
|
rateLimit: pending.rateLimit,
|
|
@@ -296,6 +236,8 @@ export class VaultCore {
|
|
|
296
236
|
source: "owner_grant",
|
|
297
237
|
issuedAt,
|
|
298
238
|
decidedAt: issuedAt,
|
|
239
|
+
writeGrant: "always",
|
|
240
|
+
writeGrantedAt: pending.writeGrantedAt ?? issuedAt,
|
|
299
241
|
});
|
|
300
242
|
}
|
|
301
243
|
else {
|
|
@@ -353,7 +295,7 @@ export class VaultCore {
|
|
|
353
295
|
}
|
|
354
296
|
async _listVisibleSecretsForAgent(agentId) {
|
|
355
297
|
const capabilities = (await this._deps.capabilityStates.list(this._deps.vaultId, agentId))
|
|
356
|
-
.filter((state) => !!state.capabilityId && !!state.issuedAt && state.
|
|
298
|
+
.filter((state) => !!state.capabilityId && !!state.issuedAt && state.writeGrant === "always")
|
|
357
299
|
.map((state) => this._stateToGrantedCapability(state));
|
|
358
300
|
const capabilityMap = new Map();
|
|
359
301
|
for (const capability of capabilities) {
|
|
@@ -367,8 +309,7 @@ export class VaultCore {
|
|
|
367
309
|
methods: [...capability.write.methods],
|
|
368
310
|
},
|
|
369
311
|
read: {
|
|
370
|
-
|
|
371
|
-
paths: capability.read.paths ? [...capability.read.paths] : undefined,
|
|
312
|
+
paths: [...capability.read.paths],
|
|
372
313
|
},
|
|
373
314
|
});
|
|
374
315
|
capabilityMap.set(secretId, existing);
|
|
@@ -419,7 +360,7 @@ export class VaultCore {
|
|
|
419
360
|
});
|
|
420
361
|
}
|
|
421
362
|
toVisibleRequestRecord(record, state) {
|
|
422
|
-
const
|
|
363
|
+
const readGrant = state?.readGrant ?? null;
|
|
423
364
|
return {
|
|
424
365
|
requestId: record.requestId,
|
|
425
366
|
createdAt: record.createdAt,
|
|
@@ -431,9 +372,9 @@ export class VaultCore {
|
|
|
431
372
|
executionStatus: record.execution.status,
|
|
432
373
|
responseStatus: record.response?.status,
|
|
433
374
|
error: record.response?.error,
|
|
434
|
-
|
|
375
|
+
readGrant,
|
|
435
376
|
hasResponseBody: typeof record.response?.body === "string" && record.response.body.length > 0,
|
|
436
|
-
resultVisible:
|
|
377
|
+
resultVisible: typeof record.response?.body === "string" && record.response.body.length > 0,
|
|
437
378
|
};
|
|
438
379
|
}
|
|
439
380
|
toOwnerVisibleRequestRecord(record, state) {
|
|
@@ -449,8 +390,8 @@ export class VaultCore {
|
|
|
449
390
|
executionStatus: record.execution.status,
|
|
450
391
|
responseStatus: record.response?.status,
|
|
451
392
|
error: record.response?.error,
|
|
452
|
-
|
|
453
|
-
|
|
393
|
+
writeGrant: state?.writeGrant ?? null,
|
|
394
|
+
readGrant: state?.readGrant ?? null,
|
|
454
395
|
hasResponseBody: typeof record.response?.body === "string" && record.response.body.length > 0,
|
|
455
396
|
};
|
|
456
397
|
}
|
|
@@ -477,10 +418,10 @@ export class VaultCore {
|
|
|
477
418
|
error: record.response.error,
|
|
478
419
|
}
|
|
479
420
|
: undefined,
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
421
|
+
writeGrant: state?.writeGrant ?? null,
|
|
422
|
+
writeGrantedAt: state?.writeGrantedAt,
|
|
423
|
+
readGrant: state?.readGrant ?? null,
|
|
424
|
+
readGrantedAt: state?.readGrantedAt,
|
|
484
425
|
executionStatus: record.execution.status,
|
|
485
426
|
};
|
|
486
427
|
}
|
|
@@ -557,10 +498,10 @@ export class VaultCore {
|
|
|
557
498
|
source: "owner_grant",
|
|
558
499
|
requestId: undefined,
|
|
559
500
|
requestedAt: command.capability.issuedAt,
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
501
|
+
writeGrant: "always",
|
|
502
|
+
writeGrantedAt: command.capability.issuedAt,
|
|
503
|
+
readGrant: [...command.capability.read.paths],
|
|
504
|
+
readGrantedAt: command.capability.issuedAt,
|
|
564
505
|
});
|
|
565
506
|
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REGISTER_CAPABILITY, AuditOutcome.SUCCEEDED, `capability registered: ${command.capability.capabilityId}`, {
|
|
566
507
|
capabilityId: command.capability.capabilityId,
|
|
@@ -601,18 +542,15 @@ export class VaultCore {
|
|
|
601
542
|
methods: [...command.capability.write.methods],
|
|
602
543
|
},
|
|
603
544
|
read: {
|
|
604
|
-
|
|
605
|
-
paths: command.capability.read.paths ? [...command.capability.read.paths] : undefined,
|
|
545
|
+
paths: [...command.capability.read.paths],
|
|
606
546
|
},
|
|
607
547
|
rateLimit: command.capability.rateLimit,
|
|
608
548
|
skipAudit: command.capability.skipAudit,
|
|
609
549
|
expiresAt: command.capability.expiresAt,
|
|
610
550
|
reason: command.reason,
|
|
611
551
|
requestedAt: command.requestedAt,
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
read: { action: "read", status: "PENDING" },
|
|
615
|
-
},
|
|
552
|
+
writeGrant: null,
|
|
553
|
+
readGrant: null,
|
|
616
554
|
};
|
|
617
555
|
await this._deps.capabilityStates.upsert(pendingRecord);
|
|
618
556
|
for (const observer of this._capabilityStateObservers) {
|
|
@@ -635,7 +573,7 @@ export class VaultCore {
|
|
|
635
573
|
throw new VaultCoreError("capability lookup vault mismatch", "VAULT_IDENTITY_DENIED");
|
|
636
574
|
}
|
|
637
575
|
const state = await this._deps.capabilityStates.getByCapabilityId(vaultId, agentId, capabilityId);
|
|
638
|
-
return state && state.capabilityId && state.issuedAt && state.
|
|
576
|
+
return state && state.capabilityId && state.issuedAt && state.writeGrant === "always"
|
|
639
577
|
? this._stateToGrantedCapability(state)
|
|
640
578
|
: null;
|
|
641
579
|
}
|
|
@@ -881,7 +819,7 @@ export class VaultCore {
|
|
|
881
819
|
return { vaultId: this._deps.vaultId, decision: "deny", reason: "agent not found", secretId: null };
|
|
882
820
|
}
|
|
883
821
|
const capabilities = (await this._deps.capabilityStates.list(this._deps.vaultId, request.agent.id))
|
|
884
|
-
.filter((state) => !!state.capabilityId && !!state.issuedAt && state.
|
|
822
|
+
.filter((state) => !!state.capabilityId && !!state.issuedAt && state.writeGrant === "always")
|
|
885
823
|
.map((state) => this._stateToGrantedCapability(state));
|
|
886
824
|
const requestedCapabilityId = request.capability?.capabilityId;
|
|
887
825
|
const candidateCapabilities = requestedCapabilityId
|
|
@@ -903,7 +841,7 @@ export class VaultCore {
|
|
|
903
841
|
methods: [request.method],
|
|
904
842
|
},
|
|
905
843
|
read: {
|
|
906
|
-
|
|
844
|
+
paths: [],
|
|
907
845
|
},
|
|
908
846
|
requestedAt: request.requestedAt,
|
|
909
847
|
reason: request.reason,
|
|
@@ -912,10 +850,8 @@ export class VaultCore {
|
|
|
912
850
|
headers: request.headers,
|
|
913
851
|
body: request.body,
|
|
914
852
|
proof: request.proof,
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
read: { action: "read", status: "PENDING" },
|
|
918
|
-
},
|
|
853
|
+
writeGrant: null,
|
|
854
|
+
readGrant: null,
|
|
919
855
|
};
|
|
920
856
|
await this._deps.capabilityStates.upsert(pendingRecord);
|
|
921
857
|
// Notify observers
|
|
@@ -1082,7 +1018,7 @@ export class VaultCore {
|
|
|
1082
1018
|
}
|
|
1083
1019
|
async ownerListCapabilities(actor, agentId, request) {
|
|
1084
1020
|
const capabilities = (await this._deps.capabilityStates.list(this._deps.vaultId, agentId))
|
|
1085
|
-
.filter((state) => !!state.capabilityId && !!state.issuedAt && state.
|
|
1021
|
+
.filter((state) => !!state.capabilityId && !!state.issuedAt && state.writeGrant === "always")
|
|
1086
1022
|
.map((state) => this._stateToGrantedCapability(state));
|
|
1087
1023
|
await this._appendAudit(toAuditEntry(this._deps, actor, AuditAction.LIST_CAPABILITIES, AuditOutcome.ALLOWED, "capabilities listed", {
|
|
1088
1024
|
requestId: request?.requestId,
|
|
@@ -1092,20 +1028,18 @@ export class VaultCore {
|
|
|
1092
1028
|
}
|
|
1093
1029
|
async ownerListRequests(actor, agentId, request) {
|
|
1094
1030
|
const records = await this._deps.requests.list(this._deps.vaultId, agentId);
|
|
1095
|
-
const states = await this._deps.capabilityStates.list(this._deps.vaultId, agentId);
|
|
1096
|
-
const stateByRequestId = new Map(states.filter((state) => state.requestId).map((state) => [state.requestId, state]));
|
|
1097
1031
|
await this._appendAudit(toAuditEntry(this._deps, actor, AuditAction.LIST_REQUESTS, AuditOutcome.ALLOWED, "request records listed", {
|
|
1098
1032
|
requestId: request?.requestId,
|
|
1099
1033
|
agentId,
|
|
1100
1034
|
}));
|
|
1101
|
-
return records.map((record) => this.toOwnerVisibleRequestRecord(record,
|
|
1035
|
+
return Promise.all(records.map(async (record) => this.toOwnerVisibleRequestRecord(record, await this._resolveRequestState(record))));
|
|
1102
1036
|
}
|
|
1103
1037
|
async ownerGetRequest(actor, targetRequestId, request) {
|
|
1104
1038
|
const record = await this._deps.requests.get(this._deps.vaultId, targetRequestId);
|
|
1105
1039
|
if (!record) {
|
|
1106
1040
|
throw new VaultCoreError("request record not found", "VAULT_READ_DENIED");
|
|
1107
1041
|
}
|
|
1108
|
-
const state = await this.
|
|
1042
|
+
const state = await this._resolveRequestState(record);
|
|
1109
1043
|
await this._appendAudit(toAuditEntry(this._deps, actor, AuditAction.READ_REQUEST, AuditOutcome.ALLOWED, "request record read", {
|
|
1110
1044
|
requestId: request?.requestId,
|
|
1111
1045
|
agentId: record.agentId,
|
|
@@ -1149,9 +1083,7 @@ export class VaultCore {
|
|
|
1149
1083
|
}
|
|
1150
1084
|
await this._verifyAgentControlProof(request, "list_requests");
|
|
1151
1085
|
const records = await this._deps.requests.list(this._deps.vaultId, request.agent.id);
|
|
1152
|
-
|
|
1153
|
-
const stateByRequestId = new Map(states.filter((state) => state.requestId).map((state) => [state.requestId, state]));
|
|
1154
|
-
return records.map((record) => this.toVisibleRequestRecord(record, stateByRequestId.get(record.requestId) ?? null));
|
|
1086
|
+
return Promise.all(records.map(async (record) => this.toVisibleRequestRecord(record, await this._resolveRequestState(record))));
|
|
1155
1087
|
}
|
|
1156
1088
|
async agentGetRequest(request) {
|
|
1157
1089
|
if (request.vaultId.value !== this._deps.vaultId.value) {
|
|
@@ -1162,11 +1094,8 @@ export class VaultCore {
|
|
|
1162
1094
|
if (!record || record.agentId !== request.agent.id) {
|
|
1163
1095
|
throw new VaultCoreError("request record not found", "VAULT_READ_DENIED");
|
|
1164
1096
|
}
|
|
1165
|
-
const state = await this.
|
|
1166
|
-
const
|
|
1167
|
-
const responseBody = readApproved
|
|
1168
|
-
? applyResponseReadPolicy(record.response?.body, state?.read ?? { mode: "full" })
|
|
1169
|
-
: undefined;
|
|
1097
|
+
const state = await this._resolveRequestState(record);
|
|
1098
|
+
const responseBody = applyResponseReadPolicy(record.response?.body, { paths: [...(state?.readGrant ?? [])] });
|
|
1170
1099
|
return {
|
|
1171
1100
|
requestId: record.requestId,
|
|
1172
1101
|
executionStatus: record.execution.status,
|
|
@@ -1234,10 +1163,10 @@ export class VaultCore {
|
|
|
1234
1163
|
await this._deps.capabilityStates.upsert({
|
|
1235
1164
|
...existing,
|
|
1236
1165
|
decidedAt,
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1166
|
+
writeGrant: "none",
|
|
1167
|
+
writeGrantedAt: decidedAt,
|
|
1168
|
+
readGrant: [],
|
|
1169
|
+
readGrantedAt: decidedAt,
|
|
1241
1170
|
});
|
|
1242
1171
|
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REVOKE_CAPABILITY, AuditOutcome.SUCCEEDED, "capability revoked", {
|
|
1243
1172
|
requestId: command.requestId,
|
|
@@ -1289,10 +1218,10 @@ export class VaultCore {
|
|
|
1289
1218
|
throw new VaultCoreError("read vault mismatch", "VAULT_READ_DENIED");
|
|
1290
1219
|
}
|
|
1291
1220
|
return (await this._deps.capabilityStates.list(command.vaultId, command.agentId))
|
|
1292
|
-
.filter((state) =>
|
|
1293
|
-
.filter((state) =>
|
|
1221
|
+
.filter((state) => command.writeGranted === undefined || (command.writeGranted ? state.writeGrant != null : state.writeGrant == null))
|
|
1222
|
+
.filter((state) => command.readGranted === undefined || (command.readGranted ? state.readGrant != null : state.readGrant == null));
|
|
1294
1223
|
}
|
|
1295
|
-
async
|
|
1224
|
+
async ownerApproveCapabilityRead(command) {
|
|
1296
1225
|
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
1297
1226
|
throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
|
|
1298
1227
|
}
|
|
@@ -1300,75 +1229,43 @@ export class VaultCore {
|
|
|
1300
1229
|
if (!pending) {
|
|
1301
1230
|
throw new VaultCoreError("capability action record not found", "VAULT_REQUEST_NOT_FOUND");
|
|
1302
1231
|
}
|
|
1303
|
-
if (pending.
|
|
1304
|
-
throw new VaultCoreError("write
|
|
1232
|
+
if (pending.writeGrant == null) {
|
|
1233
|
+
throw new VaultCoreError("write decision required before read grant", "VAULT_WRITE_DENIED");
|
|
1234
|
+
}
|
|
1235
|
+
if (pending.readGrant != null) {
|
|
1236
|
+
throw new VaultCoreError("read grant already decided", "VAULT_WRITE_DENIED");
|
|
1305
1237
|
}
|
|
1306
1238
|
const decidedAt = this._deps.clock.nowIso();
|
|
1307
1239
|
const next = {
|
|
1308
1240
|
...pending,
|
|
1241
|
+
readGrant: [...(command.read?.paths ?? [])],
|
|
1242
|
+
readGrantedAt: decidedAt,
|
|
1309
1243
|
decidedAt,
|
|
1310
|
-
actions: {
|
|
1311
|
-
...pending.actions,
|
|
1312
|
-
write: {
|
|
1313
|
-
action: "write",
|
|
1314
|
-
status: "APPROVED",
|
|
1315
|
-
decidedAt,
|
|
1316
|
-
},
|
|
1317
|
-
},
|
|
1318
1244
|
};
|
|
1319
1245
|
await this._deps.capabilityStates.upsert(next);
|
|
1320
|
-
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.
|
|
1246
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.APPROVE_CAPABILITY_READ, AuditOutcome.SUCCEEDED, `approved read policy for capability request ${command.requestId}`, {
|
|
1321
1247
|
requestId: command.requestId,
|
|
1322
1248
|
agentId: pending.agentId,
|
|
1323
1249
|
operation: pending.operation,
|
|
1324
1250
|
}));
|
|
1325
1251
|
return next;
|
|
1326
1252
|
}
|
|
1327
|
-
async
|
|
1328
|
-
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
1329
|
-
throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
|
|
1330
|
-
}
|
|
1253
|
+
async ownerAllowOnce(command) {
|
|
1331
1254
|
const pending = await this._deps.capabilityStates.getByRequestId(command.vaultId, command.requestId);
|
|
1332
1255
|
if (!pending) {
|
|
1333
1256
|
throw new VaultCoreError("capability action record not found", "VAULT_REQUEST_NOT_FOUND");
|
|
1334
1257
|
}
|
|
1335
|
-
if (pending.actions.write.status !== "APPROVED") {
|
|
1336
|
-
throw new VaultCoreError("write approval required before read approval", "VAULT_WRITE_DENIED");
|
|
1337
|
-
}
|
|
1338
|
-
if (pending.actions.read.status !== "PENDING") {
|
|
1339
|
-
throw new VaultCoreError("read approval not pending", "VAULT_WRITE_DENIED");
|
|
1340
|
-
}
|
|
1341
1258
|
const decidedAt = this._deps.clock.nowIso();
|
|
1342
|
-
|
|
1343
|
-
...pending,
|
|
1344
|
-
read: command.read
|
|
1345
|
-
? {
|
|
1346
|
-
mode: command.read.mode,
|
|
1347
|
-
paths: command.read.paths ? [...command.read.paths] : undefined,
|
|
1348
|
-
}
|
|
1349
|
-
: pending.read,
|
|
1350
|
-
decidedAt,
|
|
1351
|
-
actions: {
|
|
1352
|
-
...pending.actions,
|
|
1353
|
-
read: {
|
|
1354
|
-
action: "read",
|
|
1355
|
-
status: "APPROVED",
|
|
1356
|
-
decidedAt,
|
|
1357
|
-
},
|
|
1358
|
-
},
|
|
1359
|
-
};
|
|
1360
|
-
await this._deps.capabilityStates.upsert(next);
|
|
1361
|
-
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.APPROVE_CAPABILITY_READ, AuditOutcome.SUCCEEDED, `approved read policy for capability request ${command.requestId}`, {
|
|
1362
|
-
requestId: command.requestId,
|
|
1363
|
-
agentId: pending.agentId,
|
|
1364
|
-
operation: pending.operation,
|
|
1365
|
-
}));
|
|
1366
|
-
return next;
|
|
1367
|
-
}
|
|
1368
|
-
async ownerAllowOnce(command) {
|
|
1259
|
+
await this._deps.capabilityStates.upsert({ ...pending, writeGrant: "once", writeGrantedAt: decidedAt, decidedAt });
|
|
1369
1260
|
return this._executePendingCapabilityState(command, "once");
|
|
1370
1261
|
}
|
|
1371
1262
|
async ownerAllowAlways(command) {
|
|
1263
|
+
const pending = await this._deps.capabilityStates.getByRequestId(command.vaultId, command.requestId);
|
|
1264
|
+
if (!pending) {
|
|
1265
|
+
throw new VaultCoreError("capability action record not found", "VAULT_REQUEST_NOT_FOUND");
|
|
1266
|
+
}
|
|
1267
|
+
const decidedAt = this._deps.clock.nowIso();
|
|
1268
|
+
await this._deps.capabilityStates.upsert({ ...pending, writeGrant: "always", writeGrantedAt: decidedAt, decidedAt });
|
|
1372
1269
|
return this._executePendingCapabilityState(command, "grant");
|
|
1373
1270
|
}
|
|
1374
1271
|
async ownerDeny(command) {
|
|
@@ -1380,22 +1277,18 @@ export class VaultCore {
|
|
|
1380
1277
|
throw new VaultCoreError("capability action record not found", "VAULT_REQUEST_NOT_FOUND");
|
|
1381
1278
|
}
|
|
1382
1279
|
const decidedAt = this._deps.clock.nowIso();
|
|
1383
|
-
const rejectWrite = pending.
|
|
1384
|
-
const rejectRead = !rejectWrite && pending.
|
|
1280
|
+
const rejectWrite = pending.writeGrant == null;
|
|
1281
|
+
const rejectRead = !rejectWrite && pending.readGrant == null;
|
|
1385
1282
|
if (!rejectWrite && !rejectRead) {
|
|
1386
1283
|
throw new VaultCoreError("no capability action approval is pending", "VAULT_WRITE_DENIED");
|
|
1387
1284
|
}
|
|
1388
1285
|
const rejectedState = {
|
|
1389
1286
|
...pending,
|
|
1390
1287
|
decidedAt,
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
read: rejectRead
|
|
1396
|
-
? { action: "read", status: "REJECTED", decidedAt }
|
|
1397
|
-
: { ...pending.actions.read },
|
|
1398
|
-
},
|
|
1288
|
+
writeGrant: rejectWrite ? "none" : pending.writeGrant,
|
|
1289
|
+
writeGrantedAt: rejectWrite ? decidedAt : pending.writeGrantedAt,
|
|
1290
|
+
readGrant: rejectRead ? [] : pending.readGrant,
|
|
1291
|
+
readGrantedAt: rejectRead ? decidedAt : pending.readGrantedAt,
|
|
1399
1292
|
};
|
|
1400
1293
|
await this._deps.capabilityStates.upsert(rejectedState);
|
|
1401
1294
|
await this._appendAudit(toAuditEntry(this._deps, command.owner, rejectWrite ? AuditAction.REJECT_CAPABILITY_WRITE : AuditAction.REJECT_CAPABILITY_READ, AuditOutcome.SUCCEEDED, `rejected capability request ${command.requestId}`, {
|