@the-ai-company/cbio-node-runtime 1.48.5 → 1.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -25
- package/dist/clients/agent/client.d.ts +8 -6
- package/dist/clients/agent/client.js +67 -49
- package/dist/clients/agent/client.js.map +1 -1
- package/dist/clients/agent/contracts.d.ts +13 -1
- package/dist/clients/agent/index.d.ts +1 -1
- package/dist/clients/owner/client.d.ts +20 -14
- package/dist/clients/owner/client.js +140 -50
- package/dist/clients/owner/client.js.map +1 -1
- package/dist/clients/owner/contracts.d.ts +58 -26
- package/dist/clients/owner/index.d.ts +1 -1
- package/dist/runtime/index.d.ts +4 -3
- package/dist/runtime/index.js +5 -1
- package/dist/runtime/index.js.map +1 -1
- package/dist/vault-core/contracts.d.ts +90 -3
- package/dist/vault-core/contracts.js +3 -0
- package/dist/vault-core/contracts.js.map +1 -1
- package/dist/vault-core/core.d.ts +44 -25
- package/dist/vault-core/core.js +290 -73
- package/dist/vault-core/core.js.map +1 -1
- package/dist/vault-core/defaults.d.ts +9 -1
- package/dist/vault-core/defaults.js +39 -6
- package/dist/vault-core/defaults.js.map +1 -1
- package/dist/vault-core/index.d.ts +3 -3
- package/dist/vault-core/index.js +1 -1
- package/dist/vault-core/index.js.map +1 -1
- package/dist/vault-core/persistence.d.ts +1 -0
- package/dist/vault-core/persistence.js +7 -1
- package/dist/vault-core/persistence.js.map +1 -1
- package/dist/vault-core/ports.d.ts +8 -0
- package/dist/vault-ingress/defaults.d.ts +4 -1
- package/dist/vault-ingress/defaults.js +12 -3
- package/dist/vault-ingress/defaults.js.map +1 -1
- package/dist/vault-ingress/index.d.ts +137 -21
- package/dist/vault-ingress/index.js +156 -46
- package/dist/vault-ingress/index.js.map +1 -1
- package/dist/vault-ingress/remote-transport.d.ts +7 -2
- package/dist/vault-ingress/remote-transport.js +61 -3
- package/dist/vault-ingress/remote-transport.js.map +1 -1
- package/dist/vault-ingress/server-utils.d.ts +2 -1
- package/dist/vault-ingress/server-utils.js +42 -1
- package/dist/vault-ingress/server-utils.js.map +1 -1
- package/docs/REFERENCE.md +46 -17
- package/docs/api/README.md +10 -3
- package/docs/api/classes/IdentityError.md +1 -1
- package/docs/api/classes/VaultCore.md +258 -102
- package/docs/api/classes/VaultCoreError.md +1 -1
- package/docs/api/enumerations/IdentityErrorCode.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/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 +21 -0
- 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 +41 -5
- package/docs/api/interfaces/AgentDispatchIntent.md +1 -1
- package/docs/api/interfaces/AgentDispatchTransport.md +51 -3
- package/docs/api/interfaces/AgentIdentity.md +1 -1
- package/docs/api/interfaces/AgentSigner.md +1 -1
- package/docs/api/interfaces/AgentSubmitCapabilityRequestInput.md +41 -0
- package/docs/api/interfaces/CbioRuntime.md +21 -1
- package/docs/api/interfaces/CreateAgentClientOptions.md +3 -9
- package/docs/api/interfaces/CreateIdentityOptions.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 +17 -0
- package/docs/api/interfaces/OwnerDefineSecretTargetsInput.md +1 -1
- package/docs/api/interfaces/OwnerSecretTargetBinding.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 +23 -0
- package/docs/api/interfaces/VaultAuditQueryInput.md +1 -1
- package/docs/api/interfaces/VaultClient.md +123 -33
- 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 +13 -19
- package/docs/api/interfaces/VaultIdentity.md +1 -1
- package/docs/api/interfaces/{VaultRegisterAgentInput.md → VaultImportAgentInput.md} +4 -10
- package/docs/api/interfaces/VaultListAgentsInput.md +1 -1
- package/docs/api/interfaces/VaultListCapabilitiesInput.md +1 -1
- package/docs/api/interfaces/VaultListSecretsInput.md +11 -0
- 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/VaultRegisterFlowInput.md +1 -1
- package/docs/api/interfaces/VaultRevokeCapabilityInput.md +1 -1
- package/docs/api/interfaces/VaultSigner.md +1 -1
- package/docs/api/interfaces/VaultSubmitCapabilityRequestInput.md +79 -0
- package/docs/api/type-aliases/AgentCapabilityEnvelope.md +1 -1
- package/docs/api/type-aliases/AgentVisibleSecretRecord.md +7 -0
- package/docs/api/type-aliases/CbioRuntimeModule.md +1 -1
- package/docs/api/variables/DEFAULT_VAULT_KEY_CUSTODY_BLOB_KEY.md +1 -1
- package/examples/process-isolation.ts +24 -15
- package/package.json +1 -1
package/dist/vault-core/core.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AuditAction, AuditOutcome, DispatchStatus, } from "./contracts.js";
|
|
2
2
|
import { VaultCoreError } from "./errors.js";
|
|
3
|
+
import { verifySignature } from "../protocol/crypto.js";
|
|
3
4
|
function toAuditEntry(deps, actor, action, outcome, detail, options) {
|
|
4
5
|
return {
|
|
5
6
|
entryId: deps.ids.newAuditEntryId(),
|
|
@@ -33,6 +34,21 @@ function buildSecretRecord(deps, command) {
|
|
|
33
34
|
updatedAt: now,
|
|
34
35
|
};
|
|
35
36
|
}
|
|
37
|
+
function isScopeMatch(scope, targetUrl) {
|
|
38
|
+
if (scope.endsWith("*")) {
|
|
39
|
+
return targetUrl.startsWith(scope.slice(0, -1));
|
|
40
|
+
}
|
|
41
|
+
return scope === targetUrl;
|
|
42
|
+
}
|
|
43
|
+
function createAgentControlBinding(requestId, requestedAt, agentId, action, payload = {}) {
|
|
44
|
+
return JSON.stringify({
|
|
45
|
+
requestId,
|
|
46
|
+
requestedAt,
|
|
47
|
+
agentId,
|
|
48
|
+
action,
|
|
49
|
+
...payload,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
36
52
|
/**
|
|
37
53
|
* The Sovereign Vault Core.
|
|
38
54
|
* This is the primary implementation of the Vault logic.
|
|
@@ -40,13 +56,14 @@ function buildSecretRecord(deps, command) {
|
|
|
40
56
|
export class VaultCore {
|
|
41
57
|
_deps;
|
|
42
58
|
_pendingObservers = new Set();
|
|
59
|
+
_pendingCapabilityObservers = new Set();
|
|
43
60
|
constructor(_deps) {
|
|
44
61
|
this._deps = _deps;
|
|
45
62
|
}
|
|
46
63
|
get vaultId() {
|
|
47
64
|
return this._deps.vaultId;
|
|
48
65
|
}
|
|
49
|
-
async
|
|
66
|
+
async _appendAudit(entry) {
|
|
50
67
|
try {
|
|
51
68
|
await this._deps.audit.append(entry);
|
|
52
69
|
}
|
|
@@ -55,8 +72,8 @@ export class VaultCore {
|
|
|
55
72
|
throw new VaultCoreError(`audit append failed: ${message}`, "VAULT_AUDIT_FAILED");
|
|
56
73
|
}
|
|
57
74
|
}
|
|
58
|
-
async
|
|
59
|
-
await this.
|
|
75
|
+
async _appendDecisionAudit(request, outcome, detail, options) {
|
|
76
|
+
await this._appendAudit(toAuditEntry(this._deps, request.agent, AuditAction.AUTHORIZE_DISPATCH, outcome, detail, {
|
|
60
77
|
requestId: request.requestId,
|
|
61
78
|
capabilityId: request.capability?.capabilityId,
|
|
62
79
|
operation: request.capability?.operation ?? AuditAction.AUTHORIZE_DISPATCH,
|
|
@@ -65,13 +82,75 @@ export class VaultCore {
|
|
|
65
82
|
secretId: options?.secretId,
|
|
66
83
|
}));
|
|
67
84
|
}
|
|
68
|
-
|
|
85
|
+
async _verifyAgentControlProof(request, action, payload = {}) {
|
|
86
|
+
if (request.proof.agentId !== request.agent.id) {
|
|
87
|
+
throw new VaultCoreError("agent identity mismatch", "VAULT_DISPATCH_DENIED");
|
|
88
|
+
}
|
|
89
|
+
if (request.proof.token) {
|
|
90
|
+
const valid = await this._deps.sessionTokens.verify(request.proof.token, request.agent.id);
|
|
91
|
+
if (!valid) {
|
|
92
|
+
throw new VaultCoreError("invalid or expired session token", "VAULT_DISPATCH_DENIED");
|
|
93
|
+
}
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (!request.proof.signature) {
|
|
97
|
+
throw new VaultCoreError("missing agent proof (signature or token required)", "VAULT_DISPATCH_DENIED");
|
|
98
|
+
}
|
|
99
|
+
if (request.proof.requestId !== request.requestId || request.proof.requestedAt !== request.requestedAt) {
|
|
100
|
+
throw new VaultCoreError("proof binding mismatch", "VAULT_DISPATCH_DENIED");
|
|
101
|
+
}
|
|
102
|
+
const identity = await this._deps.agentIdentities.get(request.vaultId, request.agent.id);
|
|
103
|
+
if (!identity) {
|
|
104
|
+
throw new VaultCoreError("agent identity not registered", "VAULT_DISPATCH_DENIED");
|
|
105
|
+
}
|
|
106
|
+
const binding = createAgentControlBinding(request.requestId, request.requestedAt, request.agent.id, action, payload);
|
|
107
|
+
if (!verifySignature(identity.publicKey, request.proof.signature, binding)) {
|
|
108
|
+
throw new VaultCoreError("invalid proof signature", "VAULT_DISPATCH_DENIED");
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async _listVisibleSecretsForAgent(agentId) {
|
|
112
|
+
const capabilities = await this._deps.capabilities.list(this._deps.vaultId, agentId);
|
|
113
|
+
const capabilityMap = new Map();
|
|
114
|
+
for (const capability of capabilities) {
|
|
115
|
+
for (const alias of capability.secretAliases ?? []) {
|
|
116
|
+
const existing = capabilityMap.get(alias) ?? [];
|
|
117
|
+
existing.push({
|
|
118
|
+
capabilityId: capability.capabilityId,
|
|
119
|
+
scope: capability.scope,
|
|
120
|
+
methods: [...capability.methods],
|
|
121
|
+
});
|
|
122
|
+
capabilityMap.set(alias, existing);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const records = await this._deps.secrets.list(this._deps.vaultId);
|
|
126
|
+
return records.map((record) => {
|
|
127
|
+
const authorizedCapabilities = capabilityMap.get(record.alias.value) ?? [];
|
|
128
|
+
return {
|
|
129
|
+
vaultId: record.vaultId,
|
|
130
|
+
secretId: record.secretId,
|
|
131
|
+
alias: record.alias,
|
|
132
|
+
issuerId: record.issuerId,
|
|
133
|
+
targetBindings: [...record.targetBindings],
|
|
134
|
+
createdAt: record.createdAt,
|
|
135
|
+
updatedAt: record.updatedAt,
|
|
136
|
+
isAuthorizedForAgent: authorizedCapabilities.length > 0,
|
|
137
|
+
authorizedCapabilities,
|
|
138
|
+
};
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
ownerOnPendingDispatch(callback) {
|
|
69
142
|
this._pendingObservers.add(callback);
|
|
70
143
|
return () => {
|
|
71
144
|
this._pendingObservers.delete(callback);
|
|
72
145
|
};
|
|
73
146
|
}
|
|
74
|
-
|
|
147
|
+
ownerOnPendingCapabilityRequest(callback) {
|
|
148
|
+
this._pendingCapabilityObservers.add(callback);
|
|
149
|
+
return () => {
|
|
150
|
+
this._pendingCapabilityObservers.delete(callback);
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
async ownerRegisterAgentIdentity(command) {
|
|
75
154
|
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
76
155
|
throw new VaultCoreError("identity registration vault mismatch", "VAULT_IDENTITY_DENIED");
|
|
77
156
|
}
|
|
@@ -81,15 +160,15 @@ export class VaultCore {
|
|
|
81
160
|
try {
|
|
82
161
|
// Sovereign Vault: Owner has full privileges. No signature required for unlocked vault.
|
|
83
162
|
await this._deps.agentIdentities.register(command.agentIdentity);
|
|
84
|
-
await this.
|
|
163
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REGISTER_AGENT_IDENTITY, AuditOutcome.SUCCEEDED, `agent identity registered: ${command.agentIdentity.agentId}`));
|
|
85
164
|
}
|
|
86
165
|
catch (error) {
|
|
87
166
|
const detail = error instanceof Error ? error.message : String(error);
|
|
88
|
-
await this.
|
|
167
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REGISTER_AGENT_IDENTITY, AuditOutcome.DENIED, detail));
|
|
89
168
|
throw error;
|
|
90
169
|
}
|
|
91
170
|
}
|
|
92
|
-
async
|
|
171
|
+
async ownerRegisterCapability(command) {
|
|
93
172
|
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
94
173
|
throw new VaultCoreError("capability registration vault mismatch", "VAULT_IDENTITY_DENIED");
|
|
95
174
|
}
|
|
@@ -104,27 +183,73 @@ export class VaultCore {
|
|
|
104
183
|
}
|
|
105
184
|
try {
|
|
106
185
|
await this._deps.capabilities.register(command.capability);
|
|
107
|
-
await this.
|
|
186
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REGISTER_CAPABILITY, AuditOutcome.SUCCEEDED, `capability registered: ${command.capability.capabilityId}`, {
|
|
108
187
|
capabilityId: command.capability.capabilityId,
|
|
109
188
|
operation: command.capability.operation,
|
|
110
189
|
}));
|
|
111
190
|
}
|
|
112
191
|
catch (error) {
|
|
113
192
|
const detail = error instanceof Error ? error.message : String(error);
|
|
114
|
-
await this.
|
|
193
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REGISTER_CAPABILITY, AuditOutcome.DENIED, detail, {
|
|
115
194
|
capabilityId: command.capability.capabilityId,
|
|
116
195
|
operation: command.capability.operation,
|
|
117
196
|
}));
|
|
118
197
|
throw error;
|
|
119
198
|
}
|
|
120
199
|
}
|
|
121
|
-
async
|
|
200
|
+
async ownerSubmitCapabilityRequest(command) {
|
|
201
|
+
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
202
|
+
throw new VaultCoreError("capability request vault mismatch", "VAULT_IDENTITY_DENIED");
|
|
203
|
+
}
|
|
204
|
+
if (!command.agentId.trim()) {
|
|
205
|
+
throw new VaultCoreError("capability request agent id required", "VAULT_IDENTITY_DENIED");
|
|
206
|
+
}
|
|
207
|
+
if (!command.scope.scope.trim()) {
|
|
208
|
+
throw new VaultCoreError("capability request scope required", "VAULT_IDENTITY_DENIED");
|
|
209
|
+
}
|
|
210
|
+
if (command.scope.methods.length === 0) {
|
|
211
|
+
throw new VaultCoreError("capability request method required", "VAULT_IDENTITY_DENIED");
|
|
212
|
+
}
|
|
213
|
+
const pendingRecord = {
|
|
214
|
+
vaultId: this._deps.vaultId,
|
|
215
|
+
requestId: command.requestId,
|
|
216
|
+
requester: command.requester,
|
|
217
|
+
agentId: command.agentId,
|
|
218
|
+
scope: {
|
|
219
|
+
operation: command.scope.operation,
|
|
220
|
+
secretAliases: command.scope.secretAliases ? [...command.scope.secretAliases] : [],
|
|
221
|
+
scope: command.scope.scope,
|
|
222
|
+
methods: [...command.scope.methods],
|
|
223
|
+
rateLimit: command.scope.rateLimit,
|
|
224
|
+
skipAudit: command.scope.skipAudit,
|
|
225
|
+
expiresAt: command.scope.expiresAt,
|
|
226
|
+
},
|
|
227
|
+
justification: command.justification,
|
|
228
|
+
requestedAt: command.requestedAt,
|
|
229
|
+
};
|
|
230
|
+
await this._deps.pendingCapabilityRequests.save(pendingRecord);
|
|
231
|
+
for (const observer of this._pendingCapabilityObservers) {
|
|
232
|
+
try {
|
|
233
|
+
observer(pendingRecord);
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
console.error("VaultCore: error in pending capability observer:", error);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
await this._appendAudit(toAuditEntry(this._deps, command.requester, AuditAction.SUBMIT_CAPABILITY_REQUEST, AuditOutcome.PENDING, `capability request submitted for agent: ${command.agentId}`, {
|
|
240
|
+
requestId: command.requestId,
|
|
241
|
+
agentId: command.agentId,
|
|
242
|
+
operation: command.scope.operation,
|
|
243
|
+
}));
|
|
244
|
+
return pendingRecord;
|
|
245
|
+
}
|
|
246
|
+
async _getCapability(vaultId, agentId, capabilityId) {
|
|
122
247
|
if (vaultId.value !== this._deps.vaultId.value) {
|
|
123
248
|
throw new VaultCoreError("capability lookup vault mismatch", "VAULT_IDENTITY_DENIED");
|
|
124
249
|
}
|
|
125
250
|
return this._deps.capabilities.get(vaultId, agentId, capabilityId);
|
|
126
251
|
}
|
|
127
|
-
async
|
|
252
|
+
async ownerRegisterCustomFlow(command) {
|
|
128
253
|
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
129
254
|
throw new VaultCoreError("custom flow vault mismatch", "VAULT_IDENTITY_DENIED");
|
|
130
255
|
}
|
|
@@ -146,15 +271,15 @@ export class VaultCore {
|
|
|
146
271
|
responseSecret: command.flow.responseSecret,
|
|
147
272
|
createdAt: this._deps.clock.nowIso(),
|
|
148
273
|
});
|
|
149
|
-
await this.
|
|
274
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REGISTER_CUSTOM_FLOW, AuditOutcome.SUCCEEDED, `custom http flow registered: ${command.flow.flowId}`));
|
|
150
275
|
}
|
|
151
276
|
catch (error) {
|
|
152
277
|
const detail = error instanceof Error ? error.message : String(error);
|
|
153
|
-
await this.
|
|
278
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REGISTER_CUSTOM_FLOW, AuditOutcome.DENIED, detail));
|
|
154
279
|
throw error;
|
|
155
280
|
}
|
|
156
281
|
}
|
|
157
|
-
async
|
|
282
|
+
async _storeCustomFlowSecret(flow, alias, plaintext) {
|
|
158
283
|
const actor = { kind: "owner", id: flow.ownerId };
|
|
159
284
|
const targetBindings = [{
|
|
160
285
|
kind: "site",
|
|
@@ -165,7 +290,7 @@ export class VaultCore {
|
|
|
165
290
|
}];
|
|
166
291
|
const existing = await this._deps.secrets.getByAlias({ value: alias });
|
|
167
292
|
if (existing) {
|
|
168
|
-
await this.
|
|
293
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditAction.REASSIGN_ALIAS, AuditOutcome.DENIED, "alias already bound to existing secret; explicit alias lifecycle required", {
|
|
169
294
|
secretAlias: existing.alias.value,
|
|
170
295
|
secretId: existing.secretId.value,
|
|
171
296
|
}));
|
|
@@ -184,7 +309,7 @@ export class VaultCore {
|
|
|
184
309
|
try {
|
|
185
310
|
await this._deps.custody.store(record.secretId, plaintext);
|
|
186
311
|
await this._deps.secrets.save(record);
|
|
187
|
-
await this.
|
|
312
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditAction.WRITE_SECRET, AuditOutcome.SUCCEEDED, `custom flow stored secret: ${alias}`, {
|
|
188
313
|
secretAlias: record.alias.value,
|
|
189
314
|
secretId: record.secretId.value,
|
|
190
315
|
}));
|
|
@@ -198,7 +323,7 @@ export class VaultCore {
|
|
|
198
323
|
}
|
|
199
324
|
return record;
|
|
200
325
|
}
|
|
201
|
-
async
|
|
326
|
+
async ownerWriteSecret(command) {
|
|
202
327
|
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
203
328
|
throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
|
|
204
329
|
}
|
|
@@ -207,14 +332,14 @@ export class VaultCore {
|
|
|
207
332
|
}
|
|
208
333
|
catch (error) {
|
|
209
334
|
const detail = error instanceof Error ? error.message : String(error);
|
|
210
|
-
await this.
|
|
335
|
+
await this._appendAudit(toAuditEntry(this._deps, command.kind === "owner.write_secret" ? command.owner : command.issuer, AuditAction.WRITE_SECRET, AuditOutcome.DENIED, detail, {
|
|
211
336
|
secretAlias: command.alias,
|
|
212
337
|
}));
|
|
213
338
|
throw error;
|
|
214
339
|
}
|
|
215
340
|
const existing = await this._deps.secrets.getByAlias({ value: command.alias });
|
|
216
341
|
if (existing) {
|
|
217
|
-
await this.
|
|
342
|
+
await this._appendAudit(toAuditEntry(this._deps, command.kind === "owner.write_secret" ? command.owner : command.issuer, AuditAction.REASSIGN_ALIAS, AuditOutcome.DENIED, "alias already bound to existing secret; explicit alias lifecycle required", {
|
|
218
343
|
secretAlias: existing.alias.value,
|
|
219
344
|
secretId: existing.secretId.value,
|
|
220
345
|
}));
|
|
@@ -224,7 +349,7 @@ export class VaultCore {
|
|
|
224
349
|
try {
|
|
225
350
|
await this._deps.custody.store(record.secretId, command.plaintext);
|
|
226
351
|
await this._deps.secrets.save(record);
|
|
227
|
-
await this.
|
|
352
|
+
await this._appendAudit(toAuditEntry(this._deps, command.kind === "owner.write_secret" ? command.owner : command.issuer, AuditAction.WRITE_SECRET, AuditOutcome.SUCCEEDED, "secret stored", {
|
|
228
353
|
secretAlias: record.alias.value,
|
|
229
354
|
secretId: record.secretId.value,
|
|
230
355
|
}));
|
|
@@ -238,20 +363,20 @@ export class VaultCore {
|
|
|
238
363
|
}
|
|
239
364
|
return record;
|
|
240
365
|
}
|
|
241
|
-
async
|
|
366
|
+
async ownerDeleteSecret(command) {
|
|
242
367
|
const record = await this._deps.secrets.getByAlias({ value: command.alias });
|
|
243
368
|
if (!record) {
|
|
244
369
|
throw new VaultCoreError(`secret not found: ${command.alias}`, "VAULT_SECRET_NOT_FOUND");
|
|
245
370
|
}
|
|
246
371
|
await this._deps.secrets.delete(record.secretId);
|
|
247
372
|
await this._deps.custody.delete(record.secretId);
|
|
248
|
-
await this.
|
|
373
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.DELETE_SECRET, AuditOutcome.SUCCEEDED, `deleted secret ${command.alias}`, {
|
|
249
374
|
requestId: command.requestId,
|
|
250
375
|
secretAlias: command.alias,
|
|
251
376
|
secretId: record.secretId.value,
|
|
252
377
|
}));
|
|
253
378
|
}
|
|
254
|
-
async
|
|
379
|
+
async ownerDefineSecretTargets(command) {
|
|
255
380
|
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
256
381
|
throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
|
|
257
382
|
}
|
|
@@ -260,7 +385,7 @@ export class VaultCore {
|
|
|
260
385
|
}
|
|
261
386
|
catch (error) {
|
|
262
387
|
const detail = error instanceof Error ? error.message : String(error);
|
|
263
|
-
await this.
|
|
388
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.DEFINE_SECRET_TARGETS, AuditOutcome.DENIED, detail, {
|
|
264
389
|
secretAlias: command.alias,
|
|
265
390
|
}));
|
|
266
391
|
throw error;
|
|
@@ -268,7 +393,7 @@ export class VaultCore {
|
|
|
268
393
|
const existing = await this._deps.secrets.getByAlias({ value: command.alias });
|
|
269
394
|
if (!existing) {
|
|
270
395
|
const error = new VaultCoreError("secret not found", "VAULT_SECRET_NOT_FOUND");
|
|
271
|
-
await this.
|
|
396
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.DEFINE_SECRET_TARGETS, AuditOutcome.DENIED, error.message, {
|
|
272
397
|
secretAlias: command.alias,
|
|
273
398
|
}));
|
|
274
399
|
throw error;
|
|
@@ -279,14 +404,14 @@ export class VaultCore {
|
|
|
279
404
|
updatedAt: this._deps.clock.nowIso(),
|
|
280
405
|
};
|
|
281
406
|
await this._deps.secrets.save(nextRecord);
|
|
282
|
-
await this.
|
|
407
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.DEFINE_SECRET_TARGETS, AuditOutcome.SUCCEEDED, "secret targets defined", {
|
|
283
408
|
requestId: command.requestId,
|
|
284
409
|
secretAlias: nextRecord.alias.value,
|
|
285
410
|
secretId: nextRecord.secretId.value,
|
|
286
411
|
}));
|
|
287
412
|
return nextRecord;
|
|
288
413
|
}
|
|
289
|
-
async
|
|
414
|
+
async agentAuthorizeDispatch(request) {
|
|
290
415
|
if (request.vaultId.value !== this._deps.vaultId.value) {
|
|
291
416
|
throw new VaultCoreError("request vault mismatch", "VAULT_DISPATCH_DENIED");
|
|
292
417
|
}
|
|
@@ -294,7 +419,7 @@ export class VaultCore {
|
|
|
294
419
|
? await this._deps.secrets.getByAlias({ value: request.secretAlias })
|
|
295
420
|
: null;
|
|
296
421
|
if (request.secretAlias && !record) {
|
|
297
|
-
await this.
|
|
422
|
+
await this._appendDecisionAudit(request, AuditOutcome.DENIED, "secret not found");
|
|
298
423
|
return {
|
|
299
424
|
vaultId: this._deps.vaultId,
|
|
300
425
|
decision: "deny",
|
|
@@ -310,7 +435,7 @@ export class VaultCore {
|
|
|
310
435
|
}
|
|
311
436
|
catch (error) {
|
|
312
437
|
const detail = error instanceof Error ? error.message : String(error);
|
|
313
|
-
await this.
|
|
438
|
+
await this._appendDecisionAudit(request, AuditOutcome.DENIED, detail, {
|
|
314
439
|
secretAlias: record?.alias.value ?? request.secretAlias,
|
|
315
440
|
secretId: record?.secretId.value,
|
|
316
441
|
});
|
|
@@ -352,7 +477,7 @@ export class VaultCore {
|
|
|
352
477
|
console.error("VaultCore: error in pending observer:", error);
|
|
353
478
|
}
|
|
354
479
|
}
|
|
355
|
-
await this.
|
|
480
|
+
await this._appendDecisionAudit(request, AuditOutcome.PENDING, "dispatch stalled for manual discovery approval", {
|
|
356
481
|
secretAlias: record?.alias.value ?? request.secretAlias,
|
|
357
482
|
secretId: record?.secretId.value,
|
|
358
483
|
});
|
|
@@ -366,7 +491,7 @@ export class VaultCore {
|
|
|
366
491
|
}
|
|
367
492
|
// Capability found, proceed
|
|
368
493
|
if (!capability.skipAudit) {
|
|
369
|
-
await this.
|
|
494
|
+
await this._appendDecisionAudit(request, AuditOutcome.ALLOWED, "dispatch authorized", {
|
|
370
495
|
secretAlias: record?.alias.value ?? request.secretAlias,
|
|
371
496
|
secretId: record?.secretId.value,
|
|
372
497
|
});
|
|
@@ -380,8 +505,8 @@ export class VaultCore {
|
|
|
380
505
|
capability, // Expose the found capability for subsequent steps
|
|
381
506
|
};
|
|
382
507
|
}
|
|
383
|
-
async
|
|
384
|
-
const authorization = await this.
|
|
508
|
+
async agentDispatchSecret(request) {
|
|
509
|
+
const authorization = await this.agentAuthorizeDispatch(request);
|
|
385
510
|
if (authorization.decision === "deny" || !authorization.secretId) {
|
|
386
511
|
throw new VaultCoreError("dispatch denied", "VAULT_DISPATCH_DENIED");
|
|
387
512
|
}
|
|
@@ -411,7 +536,7 @@ export class VaultCore {
|
|
|
411
536
|
headers: request.headers,
|
|
412
537
|
body: request.body,
|
|
413
538
|
}, { record, plaintext });
|
|
414
|
-
await this.
|
|
539
|
+
await this._appendAudit(toAuditEntry(this._deps, request.agent, AuditAction.DISPATCH_SECRET, result.status === DispatchStatus.SUCCEEDED ? AuditOutcome.SUCCEEDED : AuditOutcome.FAILED, result.status === DispatchStatus.SUCCEEDED ? "dispatch completed" : (result.error ?? "dispatch failed"), {
|
|
415
540
|
requestId: request.requestId,
|
|
416
541
|
capabilityId: authorization.capability?.capabilityId,
|
|
417
542
|
operation: authorization.capability?.operation,
|
|
@@ -424,12 +549,12 @@ export class VaultCore {
|
|
|
424
549
|
vaultId: this._deps.vaultId,
|
|
425
550
|
};
|
|
426
551
|
}
|
|
427
|
-
async
|
|
552
|
+
async ownerReadAudit(actor, query, request) {
|
|
428
553
|
const entries = await this._deps.audit.query(query);
|
|
429
|
-
await this.
|
|
554
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditAction.READ_AUDIT, AuditOutcome.ALLOWED, "audit queried"));
|
|
430
555
|
return entries;
|
|
431
556
|
}
|
|
432
|
-
async
|
|
557
|
+
async ownerExportSecret(actor, alias, request) {
|
|
433
558
|
try {
|
|
434
559
|
const record = await this._deps.secrets.getByAlias({ value: alias });
|
|
435
560
|
if (!record) {
|
|
@@ -440,7 +565,7 @@ export class VaultCore {
|
|
|
440
565
|
throw new VaultCoreError("secret material not found", "VAULT_SECRET_NOT_FOUND");
|
|
441
566
|
}
|
|
442
567
|
const exportedAt = this._deps.clock.nowIso();
|
|
443
|
-
await this.
|
|
568
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditAction.EXPORT_SECRET, AuditOutcome.SUCCEEDED, "secret exported", {
|
|
444
569
|
requestId: request?.requestId,
|
|
445
570
|
secretAlias: record.alias.value,
|
|
446
571
|
secretId: record.secretId.value,
|
|
@@ -455,7 +580,7 @@ export class VaultCore {
|
|
|
455
580
|
}
|
|
456
581
|
catch (error) {
|
|
457
582
|
const detail = error instanceof Error ? error.message : String(error);
|
|
458
|
-
await this.
|
|
583
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditAction.EXPORT_SECRET, AuditOutcome.DENIED, detail, {
|
|
459
584
|
requestId: request?.requestId,
|
|
460
585
|
secretAlias: alias,
|
|
461
586
|
}));
|
|
@@ -467,47 +592,88 @@ export class VaultCore {
|
|
|
467
592
|
if (request.secretAlias && !capability.secretAliases?.includes(request.secretAlias)) {
|
|
468
593
|
return false;
|
|
469
594
|
}
|
|
470
|
-
if (request.method && capability.
|
|
595
|
+
if (request.method && capability.methods?.length > 0 && !capability.methods.includes(request.method)) {
|
|
471
596
|
return false;
|
|
472
597
|
}
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
const match = capability.allowedTargets.some(target => {
|
|
476
|
-
if (target.endsWith("*")) {
|
|
477
|
-
const prefix = target.slice(0, -1);
|
|
478
|
-
return request.targetUrl.startsWith(prefix);
|
|
479
|
-
}
|
|
480
|
-
return target === request.targetUrl;
|
|
481
|
-
});
|
|
482
|
-
if (!match)
|
|
483
|
-
return false;
|
|
598
|
+
if (capability.scope && !isScopeMatch(capability.scope, request.targetUrl)) {
|
|
599
|
+
return false;
|
|
484
600
|
}
|
|
485
601
|
return true;
|
|
486
602
|
}
|
|
487
|
-
async
|
|
603
|
+
async ownerListAgents(actor, request) {
|
|
488
604
|
const identities = await this._deps.agentIdentities.list(this._deps.vaultId);
|
|
489
|
-
await this.
|
|
605
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditAction.LIST_AGENTS, AuditOutcome.ALLOWED, "agent identities listed", {
|
|
490
606
|
requestId: request?.requestId,
|
|
491
607
|
}));
|
|
492
608
|
return identities;
|
|
493
609
|
}
|
|
494
|
-
async
|
|
610
|
+
async ownerListCapabilities(actor, agentId, request) {
|
|
495
611
|
const capabilities = await this._deps.capabilities.list(this._deps.vaultId, agentId);
|
|
496
|
-
await this.
|
|
612
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditAction.LIST_CAPABILITIES, AuditOutcome.ALLOWED, "capabilities listed", {
|
|
497
613
|
requestId: request?.requestId,
|
|
498
614
|
agentId,
|
|
499
615
|
}));
|
|
500
616
|
return capabilities;
|
|
501
617
|
}
|
|
502
|
-
async
|
|
618
|
+
async ownerListSecrets(actor, request) {
|
|
619
|
+
const records = await this._deps.secrets.list(this._deps.vaultId);
|
|
620
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditAction.READ_AUDIT, AuditOutcome.ALLOWED, "secret metadata listed", {
|
|
621
|
+
requestId: request?.requestId,
|
|
622
|
+
}));
|
|
623
|
+
return records.map((record) => ({
|
|
624
|
+
vaultId: record.vaultId,
|
|
625
|
+
secretId: record.secretId,
|
|
626
|
+
alias: record.alias,
|
|
627
|
+
issuerId: record.issuerId,
|
|
628
|
+
targetBindings: [...record.targetBindings],
|
|
629
|
+
createdAt: record.createdAt,
|
|
630
|
+
updatedAt: record.updatedAt,
|
|
631
|
+
}));
|
|
632
|
+
}
|
|
633
|
+
async agentListCapabilities(request) {
|
|
634
|
+
if (request.vaultId.value !== this._deps.vaultId.value) {
|
|
635
|
+
throw new VaultCoreError("read vault mismatch", "VAULT_READ_DENIED");
|
|
636
|
+
}
|
|
637
|
+
await this._verifyAgentControlProof(request, "list_capabilities");
|
|
638
|
+
return this._deps.capabilities.list(this._deps.vaultId, request.agent.id);
|
|
639
|
+
}
|
|
640
|
+
async agentListSecrets(request) {
|
|
641
|
+
if (request.vaultId.value !== this._deps.vaultId.value) {
|
|
642
|
+
throw new VaultCoreError("read vault mismatch", "VAULT_READ_DENIED");
|
|
643
|
+
}
|
|
644
|
+
await this._verifyAgentControlProof(request, "list_secrets");
|
|
645
|
+
return this._listVisibleSecretsForAgent(request.agent.id);
|
|
646
|
+
}
|
|
647
|
+
async agentSubmitCapabilityRequest(command) {
|
|
648
|
+
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
649
|
+
throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
|
|
650
|
+
}
|
|
651
|
+
await this._verifyAgentControlProof(command, "submit_capability_request", {
|
|
652
|
+
scope: command.scope.scope,
|
|
653
|
+
methods: command.scope.methods,
|
|
654
|
+
operation: command.scope.operation,
|
|
655
|
+
secretAliases: command.scope.secretAliases ?? [],
|
|
656
|
+
justification: command.justification ?? null,
|
|
657
|
+
});
|
|
658
|
+
return this.ownerSubmitCapabilityRequest({
|
|
659
|
+
vaultId: command.vaultId,
|
|
660
|
+
requestId: command.requestId,
|
|
661
|
+
requester: command.agent,
|
|
662
|
+
agentId: command.agent.id,
|
|
663
|
+
scope: command.scope,
|
|
664
|
+
justification: command.justification,
|
|
665
|
+
requestedAt: command.requestedAt,
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
async ownerRevokeCapability(command) {
|
|
503
669
|
await this._deps.policy.revokeCapability(command.vaultId, command.agentId, command.capabilityId);
|
|
504
|
-
await this.
|
|
670
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REVOKE_CAPABILITY, AuditOutcome.SUCCEEDED, "capability revoked", {
|
|
505
671
|
requestId: command.requestId,
|
|
506
672
|
agentId: command.agentId,
|
|
507
673
|
capabilityId: command.capabilityId,
|
|
508
674
|
}));
|
|
509
675
|
}
|
|
510
|
-
async
|
|
676
|
+
async ownerIssueSessionToken(request) {
|
|
511
677
|
if (request.vaultId.value !== this._deps.vaultId.value) {
|
|
512
678
|
throw new VaultCoreError("session token vault mismatch", "VAULT_IDENTITY_DENIED");
|
|
513
679
|
}
|
|
@@ -517,19 +683,19 @@ export class VaultCore {
|
|
|
517
683
|
}
|
|
518
684
|
const token = await this._deps.sessionTokens.issue(request.agentId);
|
|
519
685
|
const issuedAt = this._deps.clock.nowIso();
|
|
520
|
-
await this.
|
|
686
|
+
await this._appendAudit(toAuditEntry(this._deps, request.actor, AuditAction.ISSUE_SESSION_TOKEN, AuditOutcome.SUCCEEDED, `session token issued for agent: ${request.agentId}`));
|
|
521
687
|
return {
|
|
522
688
|
token,
|
|
523
689
|
agentId: request.agentId,
|
|
524
690
|
issuedAt,
|
|
525
691
|
};
|
|
526
692
|
}
|
|
527
|
-
async
|
|
693
|
+
async ownerIssueAllAgentSessionTokens(actor) {
|
|
528
694
|
const agents = await this._deps.agentIdentities.list(this._deps.vaultId);
|
|
529
695
|
const results = [];
|
|
530
696
|
const requestedAt = this._deps.clock.nowIso();
|
|
531
697
|
for (const agent of agents) {
|
|
532
|
-
results.push(await this.
|
|
698
|
+
results.push(await this.ownerIssueSessionToken({
|
|
533
699
|
vaultId: this._deps.vaultId,
|
|
534
700
|
requestId: `warmup_${this._deps.ids.newVersion().value}`,
|
|
535
701
|
actor,
|
|
@@ -539,20 +705,72 @@ export class VaultCore {
|
|
|
539
705
|
}
|
|
540
706
|
return results;
|
|
541
707
|
}
|
|
542
|
-
async
|
|
708
|
+
async ownerRevokeSessionToken(request) {
|
|
543
709
|
if (request.vaultId.value !== this._deps.vaultId.value) {
|
|
544
710
|
throw new VaultCoreError("session token vault mismatch", "VAULT_IDENTITY_DENIED");
|
|
545
711
|
}
|
|
546
712
|
await this._deps.sessionTokens.revoke(request.token);
|
|
547
|
-
await this.
|
|
713
|
+
await this._appendAudit(toAuditEntry(this._deps, request.actor, AuditAction.REVOKE_SESSION_TOKEN, AuditOutcome.SUCCEEDED, "session token revoked"));
|
|
548
714
|
}
|
|
549
|
-
async
|
|
715
|
+
async ownerListPendingDispatches(command) {
|
|
550
716
|
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
551
717
|
throw new VaultCoreError("read vault mismatch", "VAULT_READ_DENIED");
|
|
552
718
|
}
|
|
553
719
|
return this._deps.pendingRequests.list(command.vaultId);
|
|
554
720
|
}
|
|
555
|
-
async
|
|
721
|
+
async ownerListPendingCapabilityRequests(command) {
|
|
722
|
+
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
723
|
+
throw new VaultCoreError("read vault mismatch", "VAULT_READ_DENIED");
|
|
724
|
+
}
|
|
725
|
+
return this._deps.pendingCapabilityRequests.list(command.vaultId);
|
|
726
|
+
}
|
|
727
|
+
async ownerApproveCapabilityRequest(command) {
|
|
728
|
+
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
729
|
+
throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
|
|
730
|
+
}
|
|
731
|
+
const pending = await this._deps.pendingCapabilityRequests.get(command.requestId);
|
|
732
|
+
if (!pending) {
|
|
733
|
+
throw new VaultCoreError("pending capability request not found", "VAULT_REQUEST_NOT_FOUND");
|
|
734
|
+
}
|
|
735
|
+
const capability = {
|
|
736
|
+
vaultId: this._deps.vaultId,
|
|
737
|
+
agentId: pending.agentId,
|
|
738
|
+
capabilityId: command.capabilityId ?? `cap_${this._deps.ids.newVersion().value}`,
|
|
739
|
+
operation: pending.scope.operation,
|
|
740
|
+
secretAliases: pending.scope.secretAliases ? [...pending.scope.secretAliases] : [],
|
|
741
|
+
scope: pending.scope.scope,
|
|
742
|
+
methods: [...pending.scope.methods],
|
|
743
|
+
rateLimit: pending.scope.rateLimit,
|
|
744
|
+
skipAudit: pending.scope.skipAudit,
|
|
745
|
+
expiresAt: pending.scope.expiresAt,
|
|
746
|
+
issuedAt: this._deps.clock.nowIso(),
|
|
747
|
+
};
|
|
748
|
+
await this._deps.capabilities.register(capability);
|
|
749
|
+
await this._deps.pendingCapabilityRequests.delete(command.requestId);
|
|
750
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.APPROVE_CAPABILITY_REQUEST, AuditOutcome.SUCCEEDED, `approved capability request ${command.requestId}`, {
|
|
751
|
+
requestId: command.requestId,
|
|
752
|
+
agentId: pending.agentId,
|
|
753
|
+
capabilityId: capability.capabilityId,
|
|
754
|
+
operation: capability.operation,
|
|
755
|
+
}));
|
|
756
|
+
return capability;
|
|
757
|
+
}
|
|
758
|
+
async ownerRejectCapabilityRequest(command) {
|
|
759
|
+
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
760
|
+
throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
|
|
761
|
+
}
|
|
762
|
+
const pending = await this._deps.pendingCapabilityRequests.get(command.requestId);
|
|
763
|
+
if (!pending) {
|
|
764
|
+
throw new VaultCoreError("pending capability request not found", "VAULT_REQUEST_NOT_FOUND");
|
|
765
|
+
}
|
|
766
|
+
await this._deps.pendingCapabilityRequests.delete(command.requestId);
|
|
767
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REJECT_CAPABILITY_REQUEST, AuditOutcome.SUCCEEDED, `rejected capability request ${command.requestId}`, {
|
|
768
|
+
requestId: command.requestId,
|
|
769
|
+
agentId: pending.agentId,
|
|
770
|
+
operation: pending.scope.operation,
|
|
771
|
+
}));
|
|
772
|
+
}
|
|
773
|
+
async ownerApproveDispatch(command) {
|
|
556
774
|
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
557
775
|
throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
|
|
558
776
|
}
|
|
@@ -580,9 +798,8 @@ export class VaultCore {
|
|
|
580
798
|
agentId: pending.agentId,
|
|
581
799
|
capabilityId,
|
|
582
800
|
secretAliases: [pending.secretAlias],
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
allowedPaths: [],
|
|
801
|
+
methods: [pending.method],
|
|
802
|
+
scope: pending.targetUrl,
|
|
586
803
|
operation: "dispatch_http",
|
|
587
804
|
issuedAt: this._deps.clock.nowIso(),
|
|
588
805
|
skipAudit: command.skipAudit ?? false,
|
|
@@ -591,7 +808,7 @@ export class VaultCore {
|
|
|
591
808
|
await this._deps.capabilities.register(capability);
|
|
592
809
|
}
|
|
593
810
|
}
|
|
594
|
-
const result = await this.
|
|
811
|
+
const result = await this.agentDispatchSecret({
|
|
595
812
|
vaultId: this._deps.vaultId,
|
|
596
813
|
agent: { kind: "agent", id: pending.agentId },
|
|
597
814
|
capability: capability,
|
|
@@ -605,14 +822,14 @@ export class VaultCore {
|
|
|
605
822
|
requestedAt: pending.requestedAt,
|
|
606
823
|
});
|
|
607
824
|
await this._deps.pendingRequests.delete(command.requestId);
|
|
608
|
-
await this.
|
|
825
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.APPROVE_DISPATCH, AuditOutcome.SUCCEEDED, `approved dispatch ${command.requestId}${command.permanent ? " and granted permanent capability" : ""}`, {
|
|
609
826
|
requestId: command.requestId,
|
|
610
827
|
agentId: pending.agentId,
|
|
611
828
|
capabilityId: capability.capabilityId,
|
|
612
829
|
}));
|
|
613
830
|
return result;
|
|
614
831
|
}
|
|
615
|
-
async
|
|
832
|
+
async ownerRejectDispatch(command) {
|
|
616
833
|
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
617
834
|
throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
|
|
618
835
|
}
|
|
@@ -621,7 +838,7 @@ export class VaultCore {
|
|
|
621
838
|
throw new VaultCoreError("pending request not found", "VAULT_REQUEST_NOT_FOUND");
|
|
622
839
|
}
|
|
623
840
|
await this._deps.pendingRequests.delete(command.requestId);
|
|
624
|
-
await this.
|
|
841
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REJECT_DISPATCH, AuditOutcome.SUCCEEDED, `rejected dispatch ${command.requestId}`, {
|
|
625
842
|
requestId: command.requestId,
|
|
626
843
|
}));
|
|
627
844
|
}
|