@the-ai-company/cbio-node-runtime 1.58.0 → 1.59.1
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 +51 -22
- package/dist/clients/agent/client.d.ts +3 -1
- package/dist/clients/agent/client.js +41 -11
- package/dist/clients/agent/client.js.map +1 -1
- package/dist/clients/agent/contracts.d.ts +5 -2
- package/dist/clients/owner/client.d.ts +6 -4
- package/dist/clients/owner/client.js +43 -19
- package/dist/clients/owner/client.js.map +1 -1
- package/dist/clients/owner/contracts.d.ts +11 -11
- package/dist/vault-core/contracts.d.ts +120 -24
- package/dist/vault-core/contracts.js +4 -2
- package/dist/vault-core/contracts.js.map +1 -1
- package/dist/vault-core/core.d.ts +10 -4
- package/dist/vault-core/core.js +302 -101
- package/dist/vault-core/core.js.map +1 -1
- package/dist/vault-core/defaults.d.ts +8 -2
- package/dist/vault-core/defaults.js +33 -10
- package/dist/vault-core/defaults.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/persistence.d.ts +11 -2
- package/dist/vault-core/persistence.js +37 -1
- package/dist/vault-core/persistence.js.map +1 -1
- package/dist/vault-core/ports.d.ts +7 -1
- package/dist/vault-core/tool-metadata.js +25 -8
- package/dist/vault-core/tool-metadata.js.map +1 -1
- package/dist/vault-ingress/defaults.d.ts +2 -0
- package/dist/vault-ingress/defaults.js +6 -0
- package/dist/vault-ingress/defaults.js.map +1 -1
- package/dist/vault-ingress/index.d.ts +39 -9
- package/dist/vault-ingress/index.js +140 -45
- package/dist/vault-ingress/index.js.map +1 -1
- package/dist/vault-ingress/remote-transport.d.ts +2 -0
- package/dist/vault-ingress/remote-transport.js +33 -4
- package/dist/vault-ingress/remote-transport.js.map +1 -1
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/REFERENCE.md +36 -27
- package/docs/WORKS_WITH_CUSTOM_FETCH.md +2 -2
- 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 +92 -28
- 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 +27 -1
- package/docs/api/interfaces/AgentDispatchIntent.md +1 -1
- package/docs/api/interfaces/AgentDispatchTransport.md +33 -1
- package/docs/api/interfaces/AgentIdentity.md +1 -1
- package/docs/api/interfaces/AgentSigner.md +1 -1
- package/docs/api/interfaces/AgentSubmitCapabilityRequestInput.md +9 -9
- 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/OwnerSensitiveActionConfirmation.md +1 -1
- package/docs/api/interfaces/OwnerSensitiveActionContext.md +1 -1
- package/docs/api/interfaces/OwnerSession.md +1 -1
- package/docs/api/interfaces/OwnerStoreSecretInput.md +1 -1
- package/docs/api/interfaces/OwnerWriteSecretInput.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 +69 -37
- package/docs/api/interfaces/VaultCoreDependenciesOptions.md +1 -1
- package/docs/api/interfaces/VaultCreateAgentInput.md +1 -1
- package/docs/api/interfaces/VaultDeleteSecretInput.md +1 -1
- package/docs/api/interfaces/VaultExportSecretInput.md +1 -1
- package/docs/api/interfaces/VaultGrantCapabilityInput.md +9 -21
- 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 +11 -17
- 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 +25 -9
- package/examples/process-isolation.ts +6 -4
- package/package.json +1 -1
package/dist/vault-core/core.js
CHANGED
|
@@ -2,6 +2,7 @@ import { AuditAction, AuditOutcome, DispatchStatus, } from "./contracts.js";
|
|
|
2
2
|
import { VaultCoreError } from "./errors.js";
|
|
3
3
|
import { verifySignature } from "../protocol/crypto.js";
|
|
4
4
|
import { getAgentToolbox } from "./tool-metadata.js";
|
|
5
|
+
import { InMemoryRequestRecordRegistry } from "./defaults.js";
|
|
5
6
|
const VAULT_MASTER_ID = "vault-master";
|
|
6
7
|
function toAuditEntry(deps, actor, action, outcome, detail, options) {
|
|
7
8
|
return {
|
|
@@ -98,12 +99,21 @@ export class VaultCore {
|
|
|
98
99
|
vaultId: state.vaultId,
|
|
99
100
|
capabilityId: state.capabilityId ?? "",
|
|
100
101
|
agentId: state.agentId,
|
|
101
|
-
secretIds: state.secretIds ? [...state.secretIds] : undefined,
|
|
102
|
-
secretAliases: state.secretAliases ? [...state.secretAliases] : undefined,
|
|
103
102
|
operation: state.operation,
|
|
104
103
|
customFlowId: state.customFlowId,
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
write: {
|
|
105
|
+
secretIds: state.write.secretIds ? [...state.write.secretIds] : undefined,
|
|
106
|
+
scope: state.write.scope,
|
|
107
|
+
methods: [...state.write.methods],
|
|
108
|
+
},
|
|
109
|
+
read: state.actions.read.status === "APPROVED"
|
|
110
|
+
? {
|
|
111
|
+
mode: state.read.mode,
|
|
112
|
+
paths: state.read.paths ? [...state.read.paths] : undefined,
|
|
113
|
+
}
|
|
114
|
+
: {
|
|
115
|
+
mode: "none",
|
|
116
|
+
},
|
|
107
117
|
issuedAt: state.issuedAt ?? state.requestedAt,
|
|
108
118
|
expiresAt: state.expiresAt,
|
|
109
119
|
rateLimit: state.rateLimit,
|
|
@@ -112,49 +122,68 @@ export class VaultCore {
|
|
|
112
122
|
}
|
|
113
123
|
async _buildAgentCapabilityStates(agentId) {
|
|
114
124
|
return (await this._deps.capabilityStates.list(this._deps.vaultId, agentId)).map((state) => ({
|
|
115
|
-
status: state.status,
|
|
116
125
|
source: state.source,
|
|
117
126
|
agentId: state.agentId,
|
|
118
127
|
requestId: state.requestId,
|
|
119
128
|
capabilityId: state.capabilityId,
|
|
120
129
|
operation: state.operation,
|
|
121
|
-
secretIds: state.secretIds ? [...state.secretIds] : undefined,
|
|
122
|
-
secretAliases: state.secretAliases ? [...state.secretAliases] : undefined,
|
|
123
130
|
customFlowId: state.customFlowId,
|
|
124
|
-
|
|
125
|
-
|
|
131
|
+
write: {
|
|
132
|
+
secretIds: state.write.secretIds ? [...state.write.secretIds] : undefined,
|
|
133
|
+
scope: state.write.scope,
|
|
134
|
+
methods: [...state.write.methods],
|
|
135
|
+
},
|
|
136
|
+
read: {
|
|
137
|
+
mode: state.read.mode,
|
|
138
|
+
paths: state.read.paths ? [...state.read.paths] : undefined,
|
|
139
|
+
},
|
|
126
140
|
issuedAt: state.issuedAt,
|
|
127
141
|
requestedAt: state.requestedAt,
|
|
128
142
|
expiresAt: state.expiresAt,
|
|
129
143
|
rateLimit: state.rateLimit,
|
|
130
144
|
skipAudit: state.skipAudit,
|
|
131
145
|
justification: state.justification,
|
|
132
|
-
|
|
146
|
+
secretId: state.secretId,
|
|
133
147
|
targetUrl: state.targetUrl,
|
|
148
|
+
actions: {
|
|
149
|
+
write: { ...state.actions.write },
|
|
150
|
+
read: { ...state.actions.read },
|
|
151
|
+
},
|
|
134
152
|
}));
|
|
135
153
|
}
|
|
136
154
|
_isExecutablePendingState(state) {
|
|
137
|
-
return !!(state.requestId && state.targetUrl && state.
|
|
155
|
+
return !!(state.requestId && state.targetUrl && state.proof);
|
|
138
156
|
}
|
|
139
157
|
async _executePendingCapabilityState(command, mode) {
|
|
140
158
|
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
141
159
|
throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
|
|
142
160
|
}
|
|
143
161
|
const pending = await this._deps.capabilityStates.getByRequestId(command.vaultId, command.requestId);
|
|
144
|
-
if (!pending
|
|
145
|
-
throw new VaultCoreError("
|
|
162
|
+
if (!pending) {
|
|
163
|
+
throw new VaultCoreError("capability action record not found", "VAULT_REQUEST_NOT_FOUND");
|
|
164
|
+
}
|
|
165
|
+
if (pending.actions.write.status !== "APPROVED") {
|
|
166
|
+
throw new VaultCoreError("write approval required before execution", "VAULT_WRITE_DENIED");
|
|
167
|
+
}
|
|
168
|
+
if (mode === "once" && pending.source !== "dispatch_discovery") {
|
|
169
|
+
throw new VaultCoreError("one-time execution is only available for dispatch discovery requests", "VAULT_WRITE_DENIED");
|
|
146
170
|
}
|
|
147
171
|
const issuedAt = this._deps.clock.nowIso();
|
|
148
172
|
const capability = {
|
|
149
173
|
vaultId: this._deps.vaultId,
|
|
150
174
|
agentId: pending.agentId,
|
|
151
175
|
capabilityId: pending.capabilityId ?? this._deps.ids.newCapabilityId(),
|
|
152
|
-
secretIds: pending.secretIds ? [...pending.secretIds] : undefined,
|
|
153
|
-
secretAliases: pending.secretAliases ? [...pending.secretAliases] : (pending.secretAlias ? [pending.secretAlias] : []),
|
|
154
176
|
operation: pending.operation,
|
|
155
177
|
customFlowId: pending.customFlowId,
|
|
156
|
-
|
|
157
|
-
|
|
178
|
+
write: {
|
|
179
|
+
secretIds: pending.write.secretIds ? [...pending.write.secretIds] : undefined,
|
|
180
|
+
scope: pending.targetUrl ?? pending.write.scope,
|
|
181
|
+
methods: [...pending.write.methods],
|
|
182
|
+
},
|
|
183
|
+
read: {
|
|
184
|
+
mode: pending.read.mode,
|
|
185
|
+
paths: pending.read.paths ? [...pending.read.paths] : undefined,
|
|
186
|
+
},
|
|
158
187
|
issuedAt,
|
|
159
188
|
expiresAt: pending.expiresAt,
|
|
160
189
|
rateLimit: pending.rateLimit,
|
|
@@ -166,9 +195,9 @@ export class VaultCore {
|
|
|
166
195
|
vaultId: this._deps.vaultId,
|
|
167
196
|
agent: { kind: "agent", id: pending.agentId },
|
|
168
197
|
capability,
|
|
169
|
-
|
|
198
|
+
secretId: pending.secretId,
|
|
170
199
|
targetUrl: pending.targetUrl,
|
|
171
|
-
method: pending.methods[0] ?? "POST",
|
|
200
|
+
method: pending.write.methods[0] ?? "POST",
|
|
172
201
|
headers: pending.headers,
|
|
173
202
|
body: pending.body,
|
|
174
203
|
proof: pending.proof,
|
|
@@ -181,8 +210,8 @@ export class VaultCore {
|
|
|
181
210
|
vaultId: this._deps.vaultId,
|
|
182
211
|
requestId: pending.requestId ?? command.requestId,
|
|
183
212
|
status: DispatchStatus.SUCCEEDED,
|
|
184
|
-
targetUrl: pending.scope,
|
|
185
|
-
method: pending.methods[0] ?? "POST",
|
|
213
|
+
targetUrl: pending.write.scope,
|
|
214
|
+
method: pending.write.methods[0] ?? "POST",
|
|
186
215
|
};
|
|
187
216
|
}
|
|
188
217
|
else {
|
|
@@ -192,26 +221,13 @@ export class VaultCore {
|
|
|
192
221
|
await this._deps.capabilityStates.upsert({
|
|
193
222
|
...pending,
|
|
194
223
|
capabilityId: capability.capabilityId,
|
|
195
|
-
status: "GRANTED",
|
|
196
224
|
source: "owner_grant",
|
|
197
225
|
issuedAt,
|
|
198
226
|
decidedAt: issuedAt,
|
|
199
227
|
});
|
|
200
|
-
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.APPROVE_CAPABILITY_REQUEST, AuditOutcome.SUCCEEDED, `executed and granted capability state ${command.requestId}`, {
|
|
201
|
-
requestId: command.requestId,
|
|
202
|
-
agentId: pending.agentId,
|
|
203
|
-
capabilityId: capability.capabilityId,
|
|
204
|
-
operation: capability.operation,
|
|
205
|
-
}));
|
|
206
228
|
}
|
|
207
229
|
else {
|
|
208
230
|
await this._deps.capabilityStates.deleteByRequestId(command.vaultId, command.requestId);
|
|
209
|
-
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.APPROVE_CAPABILITY_REQUEST, AuditOutcome.SUCCEEDED, `executed once and deleted capability state ${command.requestId}`, {
|
|
210
|
-
requestId: command.requestId,
|
|
211
|
-
agentId: pending.agentId,
|
|
212
|
-
capabilityId: capability.capabilityId,
|
|
213
|
-
operation: capability.operation,
|
|
214
|
-
}));
|
|
215
231
|
}
|
|
216
232
|
return result;
|
|
217
233
|
}
|
|
@@ -233,8 +249,8 @@ export class VaultCore {
|
|
|
233
249
|
capabilityId: request.capability?.capabilityId,
|
|
234
250
|
operation: request.capability?.operation ?? AuditAction.AUTHORIZE_DISPATCH,
|
|
235
251
|
targetUrl: request.targetUrl,
|
|
236
|
-
secretAlias: options?.secretAlias
|
|
237
|
-
secretId: options?.secretId,
|
|
252
|
+
secretAlias: options?.secretAlias,
|
|
253
|
+
secretId: options?.secretId ?? request.secretId,
|
|
238
254
|
}));
|
|
239
255
|
}
|
|
240
256
|
async _verifyAgentControlProof(request, action, payload = {}) {
|
|
@@ -265,26 +281,32 @@ export class VaultCore {
|
|
|
265
281
|
}
|
|
266
282
|
async _listVisibleSecretsForAgent(agentId) {
|
|
267
283
|
const capabilities = (await this._deps.capabilityStates.list(this._deps.vaultId, agentId))
|
|
268
|
-
.filter((state) => state.status === "
|
|
284
|
+
.filter((state) => !!state.capabilityId && !!state.issuedAt && state.actions.write.status === "APPROVED")
|
|
269
285
|
.map((state) => this._stateToGrantedCapability(state));
|
|
270
286
|
const capabilityMap = new Map();
|
|
271
287
|
for (const capability of capabilities) {
|
|
272
|
-
for (const
|
|
273
|
-
const existing = capabilityMap.get(
|
|
288
|
+
for (const secretId of capability.write.secretIds ?? []) {
|
|
289
|
+
const existing = capabilityMap.get(secretId) ?? [];
|
|
274
290
|
existing.push({
|
|
275
291
|
capabilityId: capability.capabilityId,
|
|
276
|
-
|
|
277
|
-
|
|
292
|
+
write: {
|
|
293
|
+
secretIds: capability.write.secretIds ? [...capability.write.secretIds] : undefined,
|
|
294
|
+
scope: capability.write.scope,
|
|
295
|
+
methods: [...capability.write.methods],
|
|
296
|
+
},
|
|
297
|
+
read: {
|
|
298
|
+
mode: capability.read.mode,
|
|
299
|
+
paths: capability.read.paths ? [...capability.read.paths] : undefined,
|
|
300
|
+
},
|
|
278
301
|
});
|
|
279
|
-
capabilityMap.set(
|
|
302
|
+
capabilityMap.set(secretId, existing);
|
|
280
303
|
}
|
|
281
304
|
}
|
|
282
305
|
const records = await this._deps.secrets.list(this._deps.vaultId);
|
|
283
306
|
return records.map((record) => {
|
|
284
|
-
const authorizedCapabilities = capabilityMap.get(record.
|
|
307
|
+
const authorizedCapabilities = capabilityMap.get(record.secretId.value) ?? [];
|
|
285
308
|
return {
|
|
286
309
|
vaultId: record.vaultId,
|
|
287
|
-
secretId: record.secretId,
|
|
288
310
|
alias: record.alias,
|
|
289
311
|
issuerId: record.issuerId,
|
|
290
312
|
source: record.source,
|
|
@@ -295,6 +317,48 @@ export class VaultCore {
|
|
|
295
317
|
};
|
|
296
318
|
});
|
|
297
319
|
}
|
|
320
|
+
async _recordRequestExecution(request, capability, result) {
|
|
321
|
+
await this._deps.requests.save({
|
|
322
|
+
vaultId: this._deps.vaultId,
|
|
323
|
+
requestId: request.requestId,
|
|
324
|
+
agentId: request.agent.id,
|
|
325
|
+
capabilityId: capability?.capabilityId,
|
|
326
|
+
operation: capability?.operation ?? "dispatch_http",
|
|
327
|
+
createdAt: this._deps.clock.nowIso(),
|
|
328
|
+
request: {
|
|
329
|
+
targetUrl: request.targetUrl,
|
|
330
|
+
method: request.method,
|
|
331
|
+
headers: request.headers,
|
|
332
|
+
body: request.body,
|
|
333
|
+
secretId: request.secretId,
|
|
334
|
+
},
|
|
335
|
+
response: {
|
|
336
|
+
status: result.responseStatus,
|
|
337
|
+
body: result.responseBody,
|
|
338
|
+
error: result.error,
|
|
339
|
+
},
|
|
340
|
+
execution: {
|
|
341
|
+
status: result.status,
|
|
342
|
+
},
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
toVisibleRequestRecord(record, state) {
|
|
346
|
+
const readStatus = state?.actions.read.status ?? "PENDING";
|
|
347
|
+
return {
|
|
348
|
+
requestId: record.requestId,
|
|
349
|
+
createdAt: record.createdAt,
|
|
350
|
+
capabilityId: record.capabilityId,
|
|
351
|
+
operation: record.operation,
|
|
352
|
+
targetUrl: record.request.targetUrl,
|
|
353
|
+
method: record.request.method,
|
|
354
|
+
executionStatus: record.execution.status,
|
|
355
|
+
responseStatus: record.response?.status,
|
|
356
|
+
error: record.response?.error,
|
|
357
|
+
readStatus,
|
|
358
|
+
hasResponseBody: typeof record.response?.body === "string" && record.response.body.length > 0,
|
|
359
|
+
resultVisible: readStatus === "APPROVED",
|
|
360
|
+
};
|
|
361
|
+
}
|
|
298
362
|
ownerOnCapabilityState(callback) {
|
|
299
363
|
this._capabilityStateObservers.add(callback);
|
|
300
364
|
return () => {
|
|
@@ -365,10 +429,13 @@ export class VaultCore {
|
|
|
365
429
|
try {
|
|
366
430
|
await this._deps.capabilityStates.upsert({
|
|
367
431
|
...command.capability,
|
|
368
|
-
status: "GRANTED",
|
|
369
432
|
source: "owner_grant",
|
|
370
433
|
requestId: undefined,
|
|
371
434
|
requestedAt: command.capability.issuedAt,
|
|
435
|
+
actions: {
|
|
436
|
+
write: { action: "write", status: "APPROVED", decidedAt: command.capability.issuedAt },
|
|
437
|
+
read: { action: "read", status: "APPROVED", decidedAt: command.capability.issuedAt },
|
|
438
|
+
},
|
|
372
439
|
});
|
|
373
440
|
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REGISTER_CAPABILITY, AuditOutcome.SUCCEEDED, `capability registered: ${command.capability.capabilityId}`, {
|
|
374
441
|
capabilityId: command.capability.capabilityId,
|
|
@@ -391,27 +458,36 @@ export class VaultCore {
|
|
|
391
458
|
if (!command.agentId.trim()) {
|
|
392
459
|
throw new VaultCoreError("capability request agent id required", "VAULT_IDENTITY_DENIED");
|
|
393
460
|
}
|
|
394
|
-
if (!command.
|
|
461
|
+
if (!command.capability.write.scope.trim()) {
|
|
395
462
|
throw new VaultCoreError("capability request scope required", "VAULT_IDENTITY_DENIED");
|
|
396
463
|
}
|
|
397
|
-
if (command.
|
|
464
|
+
if (command.capability.write.methods.length === 0) {
|
|
398
465
|
throw new VaultCoreError("capability request method required", "VAULT_IDENTITY_DENIED");
|
|
399
466
|
}
|
|
400
467
|
const pendingRecord = {
|
|
401
468
|
vaultId: this._deps.vaultId,
|
|
402
|
-
status: "PENDING",
|
|
403
469
|
source: "explicit_request",
|
|
404
470
|
requestId: command.requestId,
|
|
405
471
|
agentId: command.agentId,
|
|
406
|
-
operation: command.
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
472
|
+
operation: command.capability.operation,
|
|
473
|
+
write: {
|
|
474
|
+
secretIds: command.capability.write.secretIds ? [...command.capability.write.secretIds] : undefined,
|
|
475
|
+
scope: command.capability.write.scope,
|
|
476
|
+
methods: [...command.capability.write.methods],
|
|
477
|
+
},
|
|
478
|
+
read: {
|
|
479
|
+
mode: command.capability.read.mode,
|
|
480
|
+
paths: command.capability.read.paths ? [...command.capability.read.paths] : undefined,
|
|
481
|
+
},
|
|
482
|
+
rateLimit: command.capability.rateLimit,
|
|
483
|
+
skipAudit: command.capability.skipAudit,
|
|
484
|
+
expiresAt: command.capability.expiresAt,
|
|
413
485
|
justification: command.justification,
|
|
414
486
|
requestedAt: command.requestedAt,
|
|
487
|
+
actions: {
|
|
488
|
+
write: { action: "write", status: "PENDING" },
|
|
489
|
+
read: { action: "read", status: "PENDING" },
|
|
490
|
+
},
|
|
415
491
|
};
|
|
416
492
|
await this._deps.capabilityStates.upsert(pendingRecord);
|
|
417
493
|
for (const observer of this._capabilityStateObservers) {
|
|
@@ -425,7 +501,7 @@ export class VaultCore {
|
|
|
425
501
|
await this._appendAudit(toAuditEntry(this._deps, command.requester, AuditAction.SUBMIT_CAPABILITY_REQUEST, AuditOutcome.PENDING, `capability request submitted for agent: ${command.agentId}`, {
|
|
426
502
|
requestId: command.requestId,
|
|
427
503
|
agentId: command.agentId,
|
|
428
|
-
operation: command.
|
|
504
|
+
operation: command.capability.operation,
|
|
429
505
|
}));
|
|
430
506
|
return pendingRecord;
|
|
431
507
|
}
|
|
@@ -434,17 +510,19 @@ export class VaultCore {
|
|
|
434
510
|
throw new VaultCoreError("capability lookup vault mismatch", "VAULT_IDENTITY_DENIED");
|
|
435
511
|
}
|
|
436
512
|
const state = await this._deps.capabilityStates.getByCapabilityId(vaultId, agentId, capabilityId);
|
|
437
|
-
return state && state.
|
|
513
|
+
return state && state.capabilityId && state.issuedAt && state.actions.write.status === "APPROVED"
|
|
514
|
+
? this._stateToGrantedCapability(state)
|
|
515
|
+
: null;
|
|
438
516
|
}
|
|
439
517
|
async ownerRegisterCustomFlow(command) {
|
|
440
518
|
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
441
|
-
throw new VaultCoreError("
|
|
519
|
+
throw new VaultCoreError("request template vault mismatch", "VAULT_IDENTITY_DENIED");
|
|
442
520
|
}
|
|
443
521
|
if (!command.flow.flowId.trim()) {
|
|
444
|
-
throw new VaultCoreError("
|
|
522
|
+
throw new VaultCoreError("request template id required", "VAULT_IDENTITY_DENIED");
|
|
445
523
|
}
|
|
446
524
|
if (command.flow.mode !== "send_secret" && !command.flow.responseSecret) {
|
|
447
|
-
throw new VaultCoreError("
|
|
525
|
+
throw new VaultCoreError("request template response secret rule required", "VAULT_IDENTITY_DENIED");
|
|
448
526
|
}
|
|
449
527
|
try {
|
|
450
528
|
await this._deps.customFlows.register({
|
|
@@ -458,7 +536,7 @@ export class VaultCore {
|
|
|
458
536
|
responseSecret: command.flow.responseSecret,
|
|
459
537
|
createdAt: this._deps.clock.nowIso(),
|
|
460
538
|
});
|
|
461
|
-
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REGISTER_CUSTOM_FLOW, AuditOutcome.SUCCEEDED, `
|
|
539
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REGISTER_CUSTOM_FLOW, AuditOutcome.SUCCEEDED, `request template registered: ${command.flow.flowId}`));
|
|
462
540
|
}
|
|
463
541
|
catch (error) {
|
|
464
542
|
const detail = error instanceof Error ? error.message : String(error);
|
|
@@ -493,7 +571,7 @@ export class VaultCore {
|
|
|
493
571
|
try {
|
|
494
572
|
await this._deps.custody.store(record.secretId, plaintext);
|
|
495
573
|
await this._deps.secrets.save(record);
|
|
496
|
-
await this._appendAudit(toAuditEntry(this._deps, actor, AuditAction.WRITE_SECRET, AuditOutcome.SUCCEEDED, `
|
|
574
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditAction.WRITE_SECRET, AuditOutcome.SUCCEEDED, `request template stored secret: ${alias}`, {
|
|
497
575
|
secretAlias: record.alias.value,
|
|
498
576
|
secretId: record.secretId.value,
|
|
499
577
|
}));
|
|
@@ -568,10 +646,10 @@ export class VaultCore {
|
|
|
568
646
|
if (request.vaultId.value !== this._deps.vaultId.value) {
|
|
569
647
|
throw new VaultCoreError("request vault mismatch", "VAULT_DISPATCH_DENIED");
|
|
570
648
|
}
|
|
571
|
-
const record = request.
|
|
572
|
-
? await this._deps.secrets.
|
|
649
|
+
const record = request.secretId
|
|
650
|
+
? await this._deps.secrets.getById({ value: request.secretId })
|
|
573
651
|
: null;
|
|
574
|
-
if (request.
|
|
652
|
+
if (request.secretId && !record) {
|
|
575
653
|
await this._appendDecisionAudit(request, AuditOutcome.DENIED, "secret not found");
|
|
576
654
|
return {
|
|
577
655
|
vaultId: this._deps.vaultId,
|
|
@@ -588,7 +666,7 @@ export class VaultCore {
|
|
|
588
666
|
catch (error) {
|
|
589
667
|
const detail = error instanceof Error ? error.message : String(error);
|
|
590
668
|
await this._appendDecisionAudit(request, AuditOutcome.DENIED, detail, {
|
|
591
|
-
secretAlias: record?.alias.value
|
|
669
|
+
secretAlias: record?.alias.value,
|
|
592
670
|
secretId: record?.secretId.value,
|
|
593
671
|
});
|
|
594
672
|
throw error;
|
|
@@ -599,7 +677,7 @@ export class VaultCore {
|
|
|
599
677
|
return { vaultId: this._deps.vaultId, decision: "deny", reason: "agent not found", secretId: null };
|
|
600
678
|
}
|
|
601
679
|
const capabilities = (await this._deps.capabilityStates.list(this._deps.vaultId, request.agent.id))
|
|
602
|
-
.filter((state) => state.status === "
|
|
680
|
+
.filter((state) => !!state.capabilityId && !!state.issuedAt && state.actions.write.status === "APPROVED")
|
|
603
681
|
.map((state) => this._stateToGrantedCapability(state));
|
|
604
682
|
const requestedCapabilityId = request.capability?.capabilityId;
|
|
605
683
|
const candidateCapabilities = requestedCapabilityId
|
|
@@ -610,21 +688,29 @@ export class VaultCore {
|
|
|
610
688
|
// It's a discovery case if the agent and secret exist but no capability matches
|
|
611
689
|
const pendingRecord = {
|
|
612
690
|
vaultId: this._deps.vaultId,
|
|
613
|
-
status: "PENDING",
|
|
614
691
|
source: "dispatch_discovery",
|
|
615
692
|
requestId: request.requestId,
|
|
616
693
|
agentId: request.agent.id,
|
|
617
694
|
capabilityId: undefined,
|
|
618
695
|
operation: "dispatch_http",
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
696
|
+
write: {
|
|
697
|
+
secretIds: request.secretId ? [request.secretId] : undefined,
|
|
698
|
+
scope: request.targetUrl,
|
|
699
|
+
methods: [request.method],
|
|
700
|
+
},
|
|
701
|
+
read: {
|
|
702
|
+
mode: "none",
|
|
703
|
+
},
|
|
622
704
|
requestedAt: request.requestedAt,
|
|
623
|
-
|
|
705
|
+
secretId: request.secretId,
|
|
624
706
|
targetUrl: request.targetUrl,
|
|
625
707
|
headers: request.headers,
|
|
626
708
|
body: request.body,
|
|
627
709
|
proof: request.proof,
|
|
710
|
+
actions: {
|
|
711
|
+
write: { action: "write", status: "PENDING" },
|
|
712
|
+
read: { action: "read", status: "PENDING" },
|
|
713
|
+
},
|
|
628
714
|
};
|
|
629
715
|
await this._deps.capabilityStates.upsert(pendingRecord);
|
|
630
716
|
// Notify observers
|
|
@@ -637,7 +723,7 @@ export class VaultCore {
|
|
|
637
723
|
}
|
|
638
724
|
}
|
|
639
725
|
await this._appendDecisionAudit(request, AuditOutcome.PENDING, "dispatch stalled for manual discovery approval", {
|
|
640
|
-
secretAlias: record?.alias.value
|
|
726
|
+
secretAlias: record?.alias.value,
|
|
641
727
|
secretId: record?.secretId.value,
|
|
642
728
|
});
|
|
643
729
|
return {
|
|
@@ -656,7 +742,7 @@ export class VaultCore {
|
|
|
656
742
|
catch (error) {
|
|
657
743
|
const detail = error instanceof Error ? error.message : String(error);
|
|
658
744
|
await this._appendDecisionAudit(request, AuditOutcome.DENIED, detail, {
|
|
659
|
-
secretAlias: record?.alias.value
|
|
745
|
+
secretAlias: record?.alias.value,
|
|
660
746
|
secretId: record?.secretId.value,
|
|
661
747
|
});
|
|
662
748
|
return {
|
|
@@ -669,7 +755,7 @@ export class VaultCore {
|
|
|
669
755
|
// Capability found, proceed
|
|
670
756
|
if (!capability.skipAudit) {
|
|
671
757
|
await this._appendDecisionAudit(request, AuditOutcome.ALLOWED, "dispatch authorized", {
|
|
672
|
-
secretAlias: record?.alias.value
|
|
758
|
+
secretAlias: record?.alias.value,
|
|
673
759
|
secretId: record?.secretId.value,
|
|
674
760
|
});
|
|
675
761
|
}
|
|
@@ -720,9 +806,11 @@ export class VaultCore {
|
|
|
720
806
|
secretAlias: record.alias.value,
|
|
721
807
|
secretId: record.secretId.value,
|
|
722
808
|
}));
|
|
809
|
+
await this._recordRequestExecution(request, authorization.capability, result);
|
|
723
810
|
return {
|
|
724
811
|
...result,
|
|
725
812
|
vaultId: this._deps.vaultId,
|
|
813
|
+
responseBody: undefined,
|
|
726
814
|
};
|
|
727
815
|
}
|
|
728
816
|
async ownerReadAudit(actor, query, request) {
|
|
@@ -766,18 +854,16 @@ export class VaultCore {
|
|
|
766
854
|
}
|
|
767
855
|
}
|
|
768
856
|
isCapabilityMatch(capability, request, secretId) {
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
const idMatched = secretId ? (capability.secretIds?.includes(secretId) ?? false) : false;
|
|
773
|
-
if (!aliasMatched && !idMatched) {
|
|
857
|
+
if (request.secretId) {
|
|
858
|
+
const idMatched = secretId ? (capability.write.secretIds?.includes(secretId) ?? false) : false;
|
|
859
|
+
if (!idMatched) {
|
|
774
860
|
return false;
|
|
775
861
|
}
|
|
776
862
|
}
|
|
777
|
-
if (request.method && capability.methods?.length > 0 && !capability.methods.includes(request.method)) {
|
|
863
|
+
if (request.method && capability.write.methods?.length > 0 && !capability.write.methods.includes(request.method)) {
|
|
778
864
|
return false;
|
|
779
865
|
}
|
|
780
|
-
if (capability.scope && !isScopeMatch(capability.scope, request.targetUrl)) {
|
|
866
|
+
if (capability.write.scope && !isScopeMatch(capability.write.scope, request.targetUrl)) {
|
|
781
867
|
return false;
|
|
782
868
|
}
|
|
783
869
|
return true;
|
|
@@ -791,7 +877,7 @@ export class VaultCore {
|
|
|
791
877
|
}
|
|
792
878
|
async ownerListCapabilities(actor, agentId, request) {
|
|
793
879
|
const capabilities = (await this._deps.capabilityStates.list(this._deps.vaultId, agentId))
|
|
794
|
-
.filter((state) => state.status === "
|
|
880
|
+
.filter((state) => !!state.capabilityId && !!state.issuedAt && state.actions.write.status === "APPROVED")
|
|
795
881
|
.map((state) => this._stateToGrantedCapability(state));
|
|
796
882
|
await this._appendAudit(toAuditEntry(this._deps, actor, AuditAction.LIST_CAPABILITIES, AuditOutcome.ALLOWED, "capabilities listed", {
|
|
797
883
|
requestId: request?.requestId,
|
|
@@ -806,7 +892,6 @@ export class VaultCore {
|
|
|
806
892
|
}));
|
|
807
893
|
return records.map((record) => ({
|
|
808
894
|
vaultId: record.vaultId,
|
|
809
|
-
secretId: record.secretId,
|
|
810
895
|
alias: record.alias,
|
|
811
896
|
issuerId: record.issuerId,
|
|
812
897
|
source: record.source,
|
|
@@ -828,6 +913,35 @@ export class VaultCore {
|
|
|
828
913
|
await this._verifyAgentControlProof(request, "list_secrets");
|
|
829
914
|
return this._listVisibleSecretsForAgent(request.agent.id);
|
|
830
915
|
}
|
|
916
|
+
async agentListRequests(request) {
|
|
917
|
+
if (request.vaultId.value !== this._deps.vaultId.value) {
|
|
918
|
+
throw new VaultCoreError("read vault mismatch", "VAULT_READ_DENIED");
|
|
919
|
+
}
|
|
920
|
+
await this._verifyAgentControlProof(request, "list_requests");
|
|
921
|
+
const records = await this._deps.requests.list(this._deps.vaultId, request.agent.id);
|
|
922
|
+
const states = await this._deps.capabilityStates.list(this._deps.vaultId, request.agent.id);
|
|
923
|
+
const stateByRequestId = new Map(states.filter((state) => state.requestId).map((state) => [state.requestId, state]));
|
|
924
|
+
return records.map((record) => this.toVisibleRequestRecord(record, stateByRequestId.get(record.requestId) ?? null));
|
|
925
|
+
}
|
|
926
|
+
async agentGetRequest(request) {
|
|
927
|
+
if (request.vaultId.value !== this._deps.vaultId.value) {
|
|
928
|
+
throw new VaultCoreError("read vault mismatch", "VAULT_READ_DENIED");
|
|
929
|
+
}
|
|
930
|
+
await this._verifyAgentControlProof(request, "read_request_result", { targetRequestId: request.targetRequestId });
|
|
931
|
+
const record = await this._deps.requests.get(this._deps.vaultId, request.targetRequestId);
|
|
932
|
+
if (!record || record.agentId !== request.agent.id) {
|
|
933
|
+
throw new VaultCoreError("request record not found", "VAULT_READ_DENIED");
|
|
934
|
+
}
|
|
935
|
+
const state = await this._deps.capabilityStates.getByRequestId(this._deps.vaultId, request.targetRequestId);
|
|
936
|
+
const readApproved = state?.actions.read.status === "APPROVED";
|
|
937
|
+
return {
|
|
938
|
+
requestId: record.requestId,
|
|
939
|
+
executionStatus: record.execution.status,
|
|
940
|
+
responseStatus: record.response?.status,
|
|
941
|
+
responseBody: readApproved ? record.response?.body : undefined,
|
|
942
|
+
error: record.response?.error,
|
|
943
|
+
};
|
|
944
|
+
}
|
|
831
945
|
async agentGetRuntimeManifest(command) {
|
|
832
946
|
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
833
947
|
throw new VaultCoreError("read vault mismatch", "VAULT_READ_DENIED");
|
|
@@ -860,10 +974,9 @@ export class VaultCore {
|
|
|
860
974
|
throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
|
|
861
975
|
}
|
|
862
976
|
await this._verifyAgentControlProof(command, "submit_capability_request", {
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
operation: command.
|
|
866
|
-
secretAliases: command.scope.secretAliases ?? [],
|
|
977
|
+
write: command.capability.write,
|
|
978
|
+
read: command.capability.read,
|
|
979
|
+
operation: command.capability.operation,
|
|
867
980
|
justification: command.justification ?? null,
|
|
868
981
|
});
|
|
869
982
|
return this.ownerSubmitCapabilityRequest({
|
|
@@ -871,7 +984,7 @@ export class VaultCore {
|
|
|
871
984
|
requestId: command.requestId,
|
|
872
985
|
requester: command.agent,
|
|
873
986
|
agentId: command.agent.id,
|
|
874
|
-
|
|
987
|
+
capability: command.capability,
|
|
875
988
|
justification: command.justification,
|
|
876
989
|
requestedAt: command.requestedAt,
|
|
877
990
|
});
|
|
@@ -881,10 +994,14 @@ export class VaultCore {
|
|
|
881
994
|
if (!existing) {
|
|
882
995
|
throw new VaultCoreError("capability not found", "VAULT_CAPABILITY_NOT_FOUND");
|
|
883
996
|
}
|
|
997
|
+
const decidedAt = this._deps.clock.nowIso();
|
|
884
998
|
await this._deps.capabilityStates.upsert({
|
|
885
999
|
...existing,
|
|
886
|
-
|
|
887
|
-
|
|
1000
|
+
decidedAt,
|
|
1001
|
+
actions: {
|
|
1002
|
+
write: { action: "write", status: "REJECTED", decidedAt },
|
|
1003
|
+
read: { action: "read", status: "REJECTED", decidedAt },
|
|
1004
|
+
},
|
|
888
1005
|
});
|
|
889
1006
|
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REVOKE_CAPABILITY, AuditOutcome.SUCCEEDED, "capability revoked", {
|
|
890
1007
|
requestId: command.requestId,
|
|
@@ -936,29 +1053,110 @@ export class VaultCore {
|
|
|
936
1053
|
throw new VaultCoreError("read vault mismatch", "VAULT_READ_DENIED");
|
|
937
1054
|
}
|
|
938
1055
|
return (await this._deps.capabilityStates.list(command.vaultId, command.agentId))
|
|
939
|
-
.filter((state) => !command.
|
|
1056
|
+
.filter((state) => !command.writeStatus || state.actions.write.status === command.writeStatus)
|
|
1057
|
+
.filter((state) => !command.readStatus || state.actions.read.status === command.readStatus);
|
|
1058
|
+
}
|
|
1059
|
+
async ownerApproveCapabilityWrite(command) {
|
|
1060
|
+
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
1061
|
+
throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
|
|
1062
|
+
}
|
|
1063
|
+
const pending = await this._deps.capabilityStates.getByRequestId(command.vaultId, command.requestId);
|
|
1064
|
+
if (!pending) {
|
|
1065
|
+
throw new VaultCoreError("capability action record not found", "VAULT_REQUEST_NOT_FOUND");
|
|
1066
|
+
}
|
|
1067
|
+
if (pending.actions.write.status !== "PENDING") {
|
|
1068
|
+
throw new VaultCoreError("write approval not pending", "VAULT_WRITE_DENIED");
|
|
1069
|
+
}
|
|
1070
|
+
const decidedAt = this._deps.clock.nowIso();
|
|
1071
|
+
const next = {
|
|
1072
|
+
...pending,
|
|
1073
|
+
decidedAt,
|
|
1074
|
+
actions: {
|
|
1075
|
+
...pending.actions,
|
|
1076
|
+
write: {
|
|
1077
|
+
action: "write",
|
|
1078
|
+
status: "APPROVED",
|
|
1079
|
+
decidedAt,
|
|
1080
|
+
},
|
|
1081
|
+
},
|
|
1082
|
+
};
|
|
1083
|
+
await this._deps.capabilityStates.upsert(next);
|
|
1084
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.APPROVE_CAPABILITY_WRITE, AuditOutcome.SUCCEEDED, `approved write policy for capability request ${command.requestId}`, {
|
|
1085
|
+
requestId: command.requestId,
|
|
1086
|
+
agentId: pending.agentId,
|
|
1087
|
+
operation: pending.operation,
|
|
1088
|
+
}));
|
|
1089
|
+
return next;
|
|
1090
|
+
}
|
|
1091
|
+
async ownerApproveCapabilityRead(command) {
|
|
1092
|
+
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
1093
|
+
throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
|
|
1094
|
+
}
|
|
1095
|
+
const pending = await this._deps.capabilityStates.getByRequestId(command.vaultId, command.requestId);
|
|
1096
|
+
if (!pending) {
|
|
1097
|
+
throw new VaultCoreError("capability action record not found", "VAULT_REQUEST_NOT_FOUND");
|
|
1098
|
+
}
|
|
1099
|
+
if (pending.actions.write.status !== "APPROVED") {
|
|
1100
|
+
throw new VaultCoreError("write approval required before read approval", "VAULT_WRITE_DENIED");
|
|
1101
|
+
}
|
|
1102
|
+
if (pending.actions.read.status !== "PENDING") {
|
|
1103
|
+
throw new VaultCoreError("read approval not pending", "VAULT_WRITE_DENIED");
|
|
1104
|
+
}
|
|
1105
|
+
const decidedAt = this._deps.clock.nowIso();
|
|
1106
|
+
const next = {
|
|
1107
|
+
...pending,
|
|
1108
|
+
decidedAt,
|
|
1109
|
+
actions: {
|
|
1110
|
+
...pending.actions,
|
|
1111
|
+
read: {
|
|
1112
|
+
action: "read",
|
|
1113
|
+
status: "APPROVED",
|
|
1114
|
+
decidedAt,
|
|
1115
|
+
},
|
|
1116
|
+
},
|
|
1117
|
+
};
|
|
1118
|
+
await this._deps.capabilityStates.upsert(next);
|
|
1119
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.APPROVE_CAPABILITY_READ, AuditOutcome.SUCCEEDED, `approved read policy for capability request ${command.requestId}`, {
|
|
1120
|
+
requestId: command.requestId,
|
|
1121
|
+
agentId: pending.agentId,
|
|
1122
|
+
operation: pending.operation,
|
|
1123
|
+
}));
|
|
1124
|
+
return next;
|
|
940
1125
|
}
|
|
941
|
-
async
|
|
1126
|
+
async ownerAllowOnce(command) {
|
|
942
1127
|
return this._executePendingCapabilityState(command, "once");
|
|
943
1128
|
}
|
|
944
|
-
async
|
|
1129
|
+
async ownerAllowAlways(command) {
|
|
945
1130
|
return this._executePendingCapabilityState(command, "grant");
|
|
946
1131
|
}
|
|
947
|
-
async
|
|
1132
|
+
async ownerDeny(command) {
|
|
948
1133
|
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
949
1134
|
throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
|
|
950
1135
|
}
|
|
951
1136
|
const pending = await this._deps.capabilityStates.getByRequestId(command.vaultId, command.requestId);
|
|
952
|
-
if (!pending
|
|
953
|
-
throw new VaultCoreError("
|
|
1137
|
+
if (!pending) {
|
|
1138
|
+
throw new VaultCoreError("capability action record not found", "VAULT_REQUEST_NOT_FOUND");
|
|
1139
|
+
}
|
|
1140
|
+
const decidedAt = this._deps.clock.nowIso();
|
|
1141
|
+
const rejectWrite = pending.actions.write.status === "PENDING";
|
|
1142
|
+
const rejectRead = !rejectWrite && pending.actions.read.status === "PENDING";
|
|
1143
|
+
if (!rejectWrite && !rejectRead) {
|
|
1144
|
+
throw new VaultCoreError("no capability action approval is pending", "VAULT_WRITE_DENIED");
|
|
954
1145
|
}
|
|
955
1146
|
const rejectedState = {
|
|
956
1147
|
...pending,
|
|
957
|
-
|
|
958
|
-
|
|
1148
|
+
decidedAt,
|
|
1149
|
+
actions: {
|
|
1150
|
+
write: rejectWrite
|
|
1151
|
+
? { action: "write", status: "REJECTED", decidedAt }
|
|
1152
|
+
: { ...pending.actions.write },
|
|
1153
|
+
read: rejectRead
|
|
1154
|
+
? { action: "read", status: "REJECTED", decidedAt }
|
|
1155
|
+
: { ...pending.actions.read },
|
|
1156
|
+
},
|
|
959
1157
|
};
|
|
960
1158
|
await this._deps.capabilityStates.upsert(rejectedState);
|
|
961
|
-
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.
|
|
1159
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, rejectWrite ? AuditAction.REJECT_CAPABILITY_WRITE : AuditAction.REJECT_CAPABILITY_READ, AuditOutcome.SUCCEEDED, `rejected capability request ${command.requestId}`, {
|
|
962
1160
|
requestId: command.requestId,
|
|
963
1161
|
agentId: pending.agentId,
|
|
964
1162
|
operation: pending.operation,
|
|
@@ -967,6 +1165,9 @@ export class VaultCore {
|
|
|
967
1165
|
}
|
|
968
1166
|
}
|
|
969
1167
|
export function createVaultCore(deps) {
|
|
970
|
-
return new VaultCore(
|
|
1168
|
+
return new VaultCore({
|
|
1169
|
+
...deps,
|
|
1170
|
+
requests: deps.requests ?? new InMemoryRequestRecordRegistry(),
|
|
1171
|
+
});
|
|
971
1172
|
}
|
|
972
1173
|
//# sourceMappingURL=core.js.map
|