@the-ai-company/cbio-node-runtime 1.63.7 → 1.64.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 +11 -5
- package/dist/clients/agent/client.d.ts +2 -2
- package/dist/clients/agent/client.js +46 -49
- package/dist/clients/agent/client.js.map +1 -1
- package/dist/clients/agent/contracts.d.ts +5 -5
- package/dist/clients/owner/client.js +209 -195
- package/dist/clients/owner/client.js.map +1 -1
- package/dist/clients/owner/contracts.d.ts +47 -48
- package/dist/protocol/childSecretNaming.d.ts +1 -1
- package/dist/protocol/childSecretNaming.js +2 -2
- package/dist/protocol/childSecretNaming.js.map +1 -1
- package/dist/protocol/crypto.d.ts +4 -4
- package/dist/protocol/crypto.js +14 -14
- package/dist/protocol/crypto.js.map +1 -1
- package/dist/protocol/identity.d.ts +2 -2
- package/dist/protocol/identity.js +4 -4
- package/dist/protocol/identity.js.map +1 -1
- package/dist/public-types.d.ts +1 -1
- package/dist/public-types.js +1 -1
- package/dist/public-types.js.map +1 -1
- package/dist/runtime/bootstrap.d.ts +6 -6
- package/dist/runtime/bootstrap.js +26 -26
- package/dist/runtime/bootstrap.js.map +1 -1
- package/dist/runtime/identity.d.ts +6 -6
- package/dist/runtime/identity.js +14 -12
- package/dist/runtime/identity.js.map +1 -1
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/owner-session.d.ts +1 -5
- package/dist/runtime/owner-session.js +4 -5
- package/dist/runtime/owner-session.js.map +1 -1
- package/dist/runtime/vault-metadata.d.ts +2 -2
- package/dist/runtime/vault-metadata.js +2 -2
- package/dist/runtime/vault-metadata.js.map +1 -1
- package/dist/vault-core/contracts.d.ts +235 -238
- package/dist/vault-core/contracts.js +25 -34
- package/dist/vault-core/contracts.js.map +1 -1
- package/dist/vault-core/core.d.ts +41 -42
- package/dist/vault-core/core.js +251 -274
- package/dist/vault-core/core.js.map +1 -1
- package/dist/vault-core/defaults.d.ts +25 -25
- package/dist/vault-core/defaults.js +95 -95
- package/dist/vault-core/defaults.js.map +1 -1
- package/dist/vault-core/errors.d.ts +1 -1
- package/dist/vault-core/errors.js.map +1 -1
- package/dist/vault-core/index.d.ts +2 -2
- package/dist/vault-core/index.js +2 -2
- package/dist/vault-core/index.js.map +1 -1
- package/dist/vault-core/persistence.d.ts +19 -19
- package/dist/vault-core/persistence.js +78 -67
- package/dist/vault-core/persistence.js.map +1 -1
- package/dist/vault-core/ports.d.ts +23 -23
- package/dist/vault-core/tool-metadata.js +6 -6
- package/dist/vault-core/tool-metadata.js.map +1 -1
- package/dist/vault-ingress/defaults.d.ts +2 -2
- package/dist/vault-ingress/defaults.js +10 -10
- package/dist/vault-ingress/defaults.js.map +1 -1
- package/dist/vault-ingress/index.d.ts +46 -47
- package/dist/vault-ingress/index.js +34 -37
- package/dist/vault-ingress/index.js.map +1 -1
- package/dist/vault-ingress/remote-transport.d.ts +2 -2
- package/dist/vault-ingress/remote-transport.js +27 -27
- package/dist/vault-ingress/remote-transport.js.map +1 -1
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/CUSTODY_MODEL.md +3 -3
- package/docs/IDENTITY_MODEL.md +4 -4
- package/docs/REFERENCE.md +27 -2
- package/docs/api/README.md +3 -4
- package/docs/api/classes/IdentityError.md +1 -1
- package/docs/api/classes/OwnerClientError.md +1 -1
- package/docs/api/classes/PersistentVaultAgentIdentityRegistry.md +6 -6
- package/docs/api/classes/PersistentVaultAgentSecretGrantRegistry.md +12 -12
- package/docs/api/classes/PersistentVaultAuditLog.md +1 -1
- package/docs/api/classes/PersistentVaultSecretCustody.md +7 -7
- package/docs/api/classes/PersistentVaultSecretDestinationGrantRegistry.md +12 -12
- package/docs/api/classes/PersistentVaultSecretRepository.md +7 -7
- package/docs/api/classes/VaultCore.md +53 -69
- package/docs/api/classes/VaultCoreError.md +1 -1
- package/docs/api/enumerations/AuditOperation.md +137 -0
- package/docs/api/enumerations/DispatchStatus.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 +2 -2
- package/docs/api/functions/createOwnerClient.md +1 -1
- package/docs/api/functions/createOwnerSession.md +1 -1
- package/docs/api/functions/createPersistentVaultCoreDependencies.md +3 -3
- package/docs/api/functions/createVault.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/deriveRootAgentId.md +3 -3
- package/docs/api/functions/deriveVaultWorkingKeyFromPassword.md +4 -4
- 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 +3 -3
- package/docs/api/functions/recoverVault.md +4 -4
- package/docs/api/functions/recoverVaultWorkingKey.md +1 -1
- package/docs/api/functions/restoreIdentity.md +3 -3
- package/docs/api/functions/updateVaultMetadata.md +1 -1
- package/docs/api/functions/writeVaultProfile.md +3 -3
- package/docs/api/interfaces/AgentClient.md +3 -3
- package/docs/api/interfaces/AgentDispatchIntent.md +7 -7
- package/docs/api/interfaces/AgentDispatchTransport.md +1 -1
- package/docs/api/interfaces/AgentIdentity.md +3 -3
- package/docs/api/interfaces/AgentIdentityRecord.md +11 -11
- package/docs/api/interfaces/AgentRequestResult.md +9 -9
- package/docs/api/interfaces/AgentRuntimeManifest.md +13 -13
- package/docs/api/interfaces/AgentSecretGrant.md +11 -11
- package/docs/api/interfaces/AgentSigner.md +1 -1
- package/docs/api/interfaces/AgentVisibleRequestRecord.md +13 -13
- package/docs/api/interfaces/AgentVisibleSecretRecord.md +13 -13
- package/docs/api/interfaces/AuditEntry.md +45 -25
- package/docs/api/interfaces/CbioRuntime.md +10 -10
- package/docs/api/interfaces/CreateAgentClientOptions.md +1 -1
- package/docs/api/interfaces/CreateIdentityOptions.md +1 -1
- package/docs/api/interfaces/CreateOwnerClientOptions.md +3 -13
- package/docs/api/interfaces/CreateOwnerSessionOptions.md +4 -10
- package/docs/api/interfaces/CreatePersistentVaultCoreDependenciesOptions.md +3 -3
- package/docs/api/interfaces/CreateVaultOptions.md +2 -2
- package/docs/api/interfaces/CreatedVault.md +1 -1
- package/docs/api/interfaces/DefaultPolicyEngineOptions.md +9 -9
- package/docs/api/interfaces/DispatchAuthorization.md +11 -11
- package/docs/api/interfaces/DispatchInstruction.md +9 -9
- package/docs/api/interfaces/DispatchRequest.md +11 -11
- package/docs/api/interfaces/DispatchResult.md +11 -11
- 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 +3 -3
- package/docs/api/interfaces/OwnerClient.md +43 -11
- package/docs/api/interfaces/OwnerCreateSecretInput.md +3 -3
- package/docs/api/interfaces/OwnerRemoveSecretInput.md +3 -3
- package/docs/api/interfaces/OwnerRequestRecord.md +19 -19
- package/docs/api/interfaces/OwnerSensitiveActionConfirmation.md +1 -1
- package/docs/api/interfaces/OwnerSensitiveActionContext.md +1 -1
- package/docs/api/interfaces/OwnerSession.md +3 -3
- package/docs/api/interfaces/OwnerUpdateSecretInput.md +3 -3
- package/docs/api/interfaces/OwnerVisibleRequestRecord.md +21 -21
- package/docs/api/interfaces/RecoverVaultOptions.md +4 -4
- package/docs/api/interfaces/RecoveredVault.md +1 -1
- package/docs/api/interfaces/RequestRecord.md +19 -19
- package/docs/api/interfaces/RestoreIdentityOptions.md +1 -1
- package/docs/api/interfaces/SecretAlias.md +1 -1
- package/docs/api/interfaces/SecretDestinationGrant.md +11 -11
- package/docs/api/interfaces/SecretId.md +1 -1
- package/docs/api/interfaces/SecretRecord.md +13 -13
- package/docs/api/interfaces/Signer.md +1 -1
- package/docs/api/interfaces/VaultApproveDispatchInput.md +5 -5
- package/docs/api/interfaces/VaultAuditQueryInput.md +7 -7
- package/docs/api/interfaces/VaultCoreDependenciesOptions.md +5 -5
- package/docs/api/interfaces/VaultCreateAgentInput.md +3 -3
- package/docs/api/interfaces/VaultExportSecretInput.md +3 -3
- package/docs/api/interfaces/VaultGetRequestInput.md +5 -5
- package/docs/api/interfaces/VaultGrantAgentSecretInput.md +7 -7
- package/docs/api/interfaces/VaultGrantSecretDestinationInput.md +7 -7
- package/docs/api/interfaces/VaultId.md +1 -1
- package/docs/api/interfaces/VaultImportAgentInput.md +5 -5
- package/docs/api/interfaces/VaultIssueSessionTokenInput.md +5 -5
- package/docs/api/interfaces/VaultListAgentsInput.md +3 -3
- package/docs/api/interfaces/VaultListGrantsInput.md +7 -7
- package/docs/api/interfaces/VaultListRequestsInput.md +5 -5
- package/docs/api/interfaces/VaultListSecretsInput.md +3 -3
- package/docs/api/interfaces/VaultMetadata.md +1 -1
- package/docs/api/interfaces/VaultObject.md +1 -1
- package/docs/api/interfaces/VaultPrincipal.md +1 -1
- package/docs/api/interfaces/VaultProfile.md +1 -1
- package/docs/api/interfaces/VaultReadAgentPrivateKeyInput.md +5 -5
- package/docs/api/interfaces/VaultReadSecretPlaintextInput.md +3 -3
- package/docs/api/interfaces/VaultRevokeAgentSecretInput.md +7 -7
- package/docs/api/interfaces/VaultRevokeSecretDestinationInput.md +7 -7
- package/docs/api/interfaces/VaultRevokeSessionTokenInput.md +1 -1
- package/docs/api/interfaces/VaultService.md +8 -24
- package/docs/api/interfaces/VaultUpdateAgentInput.md +5 -5
- package/docs/api/type-aliases/AgentId.md +1 -1
- package/docs/api/type-aliases/CbioRuntimeModule.md +1 -1
- package/docs/api/type-aliases/DispatchApprovalDecision.md +1 -1
- package/docs/api/type-aliases/GrantStatus.md +1 -1
- package/docs/api/type-aliases/SecretLifecycleStatus.md +1 -1
- package/docs/api/type-aliases/VaultPrincipalKind.md +1 -1
- package/docs/api/variables/DEFAULT_VAULT_KEY_CUSTODY_BLOB_KEY.md +1 -1
- package/docs/zh/README.md +9 -3
- package/examples/process-isolation.ts +21 -21
- package/package.json +2 -2
- package/docs/api/enumerations/AuditAction.md +0 -143
- package/docs/api/enumerations/AuditOutcome.md +0 -35
package/dist/vault-core/core.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AuditOperation, DispatchStatus, } from "./contracts.js";
|
|
2
2
|
import { VaultCoreError } from "./errors.js";
|
|
3
3
|
import { applyResponseReadPolicy } from "./read-policy.js";
|
|
4
4
|
import { getAgentToolbox } from "./tool-metadata.js";
|
|
@@ -18,14 +18,15 @@ function extractDomain(url) {
|
|
|
18
18
|
return url;
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
-
function toAuditEntry(deps, actor,
|
|
21
|
+
function toAuditEntry(deps, actor, operation, decision, execution_status, detail, extra = {}) {
|
|
22
22
|
return {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
event_id: deps.ids.newAuditEntryId(),
|
|
24
|
+
ts: deps.clock.nowIso(),
|
|
25
|
+
vault_id: deps.vault_id.value,
|
|
26
26
|
actor,
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
operation,
|
|
28
|
+
decision,
|
|
29
|
+
execution_status,
|
|
29
30
|
detail,
|
|
30
31
|
...extra,
|
|
31
32
|
};
|
|
@@ -35,8 +36,8 @@ export class VaultCore {
|
|
|
35
36
|
constructor(deps) {
|
|
36
37
|
this._deps = deps;
|
|
37
38
|
}
|
|
38
|
-
get
|
|
39
|
-
return this._deps.
|
|
39
|
+
get vault_id() {
|
|
40
|
+
return this._deps.vault_id;
|
|
40
41
|
}
|
|
41
42
|
_assertOwnerPrincipal(actor, errorCode = "VAULT_ACCESS_DENIED") {
|
|
42
43
|
if (actor.kind !== "owner") {
|
|
@@ -53,108 +54,109 @@ export class VaultCore {
|
|
|
53
54
|
}
|
|
54
55
|
catch (error) {
|
|
55
56
|
const detail = error instanceof Error ? error.message : String(error);
|
|
56
|
-
await this._appendAudit(toAuditEntry(this._deps, command.agent,
|
|
57
|
-
|
|
57
|
+
await this._appendAudit(toAuditEntry(this._deps, command.agent, AuditOperation.POLICY_EVALUATE, "denied", "not_executed", `proof verification failed: ${detail}`, {
|
|
58
|
+
request_id: command.request_id,
|
|
59
|
+
secret_alias: command.secret_alias,
|
|
58
60
|
...extraAudit,
|
|
59
61
|
}));
|
|
60
62
|
throw error;
|
|
61
63
|
}
|
|
62
64
|
}
|
|
63
65
|
// ─── Grant Management ─────────────────────────────────────────────────────────
|
|
64
|
-
async ownerGrantAgentSecret(actor,
|
|
66
|
+
async ownerGrantAgentSecret(actor, root_agent_id, secret_alias, request) {
|
|
65
67
|
this._assertOwnerPrincipal(actor);
|
|
66
68
|
const now = this._deps.clock.nowIso();
|
|
67
69
|
const grant = {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
vault_id: this._deps.vault_id,
|
|
71
|
+
root_agent_id,
|
|
72
|
+
secret_alias,
|
|
71
73
|
status: "approved",
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
requested_at: now,
|
|
75
|
+
granted_at: now,
|
|
74
76
|
};
|
|
75
|
-
await this._deps.
|
|
76
|
-
await this._appendAudit(toAuditEntry(this._deps, actor,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
await this._deps.agent_secretGrants.upsert(grant);
|
|
78
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditOperation.GRANT_SECRET, "allowed", "succeeded", `granted secret "${secret_alias}" to agent "${root_agent_id}"`, {
|
|
79
|
+
request_id: request?.request_id,
|
|
80
|
+
root_agent_id,
|
|
81
|
+
secret_alias: secret_alias,
|
|
80
82
|
}));
|
|
81
83
|
return grant;
|
|
82
84
|
}
|
|
83
|
-
async ownerGrantSecretDestination(actor,
|
|
85
|
+
async ownerGrantSecretDestination(actor, secret_alias, site_id, request) {
|
|
84
86
|
this._assertOwnerPrincipal(actor);
|
|
85
87
|
const now = this._deps.clock.nowIso();
|
|
86
88
|
const grant = {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
89
|
+
vault_id: this._deps.vault_id,
|
|
90
|
+
secret_alias,
|
|
91
|
+
site_id,
|
|
90
92
|
status: "approved",
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
requested_at: now,
|
|
94
|
+
granted_at: now,
|
|
93
95
|
};
|
|
94
|
-
await this._deps.
|
|
95
|
-
await this._appendAudit(toAuditEntry(this._deps, actor,
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
await this._deps.secret_destinationGrants.upsert(grant);
|
|
97
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditOperation.GRANT_DESTINATION, "allowed", "succeeded", `granted destination "${site_id}" for secret "${secret_alias}"`, {
|
|
98
|
+
request_id: request?.request_id,
|
|
99
|
+
secret_alias: secret_alias,
|
|
100
|
+
site_id,
|
|
99
101
|
}));
|
|
100
102
|
return grant;
|
|
101
103
|
}
|
|
102
|
-
async ownerRevokeAgentSecret(actor,
|
|
104
|
+
async ownerRevokeAgentSecret(actor, root_agent_id, secret_alias, request) {
|
|
103
105
|
this._assertOwnerPrincipal(actor);
|
|
104
|
-
await this._deps.
|
|
105
|
-
await this._appendAudit(toAuditEntry(this._deps, actor,
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
106
|
+
await this._deps.agent_secretGrants.delete(this._deps.vault_id, root_agent_id, secret_alias);
|
|
107
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditOperation.REVOKE_SECRET, "allowed", "succeeded", `revoked secret "${secret_alias}" from agent "${root_agent_id}"`, {
|
|
108
|
+
request_id: request?.request_id,
|
|
109
|
+
root_agent_id,
|
|
110
|
+
secret_alias: secret_alias,
|
|
109
111
|
}));
|
|
110
112
|
}
|
|
111
|
-
async ownerRevokeSecretDestination(actor,
|
|
113
|
+
async ownerRevokeSecretDestination(actor, secret_alias, site_id, request) {
|
|
112
114
|
this._assertOwnerPrincipal(actor);
|
|
113
|
-
await this._deps.
|
|
114
|
-
await this._appendAudit(toAuditEntry(this._deps, actor,
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
await this._deps.secret_destinationGrants.delete(this._deps.vault_id, secret_alias, site_id);
|
|
116
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditOperation.REVOKE_DESTINATION, "allowed", "succeeded", `revoked destination "${site_id}" from secret "${secret_alias}"`, {
|
|
117
|
+
request_id: request?.request_id,
|
|
118
|
+
secret_alias: secret_alias,
|
|
119
|
+
site_id,
|
|
118
120
|
}));
|
|
119
121
|
}
|
|
120
|
-
async ownerListGrants(actor,
|
|
122
|
+
async ownerListGrants(actor, root_agent_id, secret_alias) {
|
|
121
123
|
this._assertOwnerPrincipal(actor);
|
|
122
|
-
const [
|
|
123
|
-
this._deps.
|
|
124
|
-
this._deps.
|
|
124
|
+
const [agent_secrets, secret_destinations] = await Promise.all([
|
|
125
|
+
this._deps.agent_secretGrants.list(this._deps.vault_id, root_agent_id),
|
|
126
|
+
this._deps.secret_destinationGrants.list(this._deps.vault_id, secret_alias),
|
|
125
127
|
]);
|
|
126
|
-
return {
|
|
128
|
+
return { agent_secrets, secret_destinations };
|
|
127
129
|
}
|
|
128
130
|
// ─── Dispatch Authorization ───────────────────────────────────────────────────
|
|
129
131
|
async agentAuthorizeDispatch(request) {
|
|
130
|
-
const { agent,
|
|
131
|
-
if (!
|
|
132
|
-
return {
|
|
132
|
+
const { agent, secret_alias, target_url } = request;
|
|
133
|
+
if (!secret_alias) {
|
|
134
|
+
return { vault_id: this._deps.vault_id, decision: "deny", reason: "secret_alias required", secret_id: null };
|
|
133
135
|
}
|
|
134
|
-
const secret = await this._deps.secrets.getByAlias({ value:
|
|
136
|
+
const secret = await this._deps.secrets.getByAlias({ value: secret_alias });
|
|
135
137
|
if (!secret) {
|
|
136
|
-
return {
|
|
138
|
+
return { vault_id: this._deps.vault_id, decision: "deny", reason: `secret not found: ${secret_alias}`, secret_id: null };
|
|
137
139
|
}
|
|
138
140
|
// 1. Check Agent-Secret Grant
|
|
139
|
-
const
|
|
140
|
-
const
|
|
141
|
+
const agent_secretGrant = await this._deps.agent_secretGrants.get(this._deps.vault_id, agent.id, secret_alias);
|
|
142
|
+
const agent_secretApproved = agent_secretGrant?.status === "approved";
|
|
141
143
|
// 2. Check Secret-Destination Grant
|
|
142
|
-
const
|
|
143
|
-
const destGrant = await this._deps.
|
|
144
|
+
const site_id = extractDomain(target_url);
|
|
145
|
+
const destGrant = await this._deps.secret_destinationGrants.get(this._deps.vault_id, secret_alias, site_id);
|
|
144
146
|
const destApproved = destGrant?.status === "approved";
|
|
145
|
-
if (
|
|
146
|
-
return {
|
|
147
|
+
if (agent_secretApproved && destApproved) {
|
|
148
|
+
return { vault_id: this._deps.vault_id, decision: "allow", reason: "granted", secret_id: secret.secret_id };
|
|
147
149
|
}
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
150
|
+
const missing_grants = {
|
|
151
|
+
agent_secret: !agent_secretApproved,
|
|
152
|
+
secret_destination: !destApproved,
|
|
151
153
|
};
|
|
152
154
|
return {
|
|
153
|
-
|
|
155
|
+
vault_id: this._deps.vault_id,
|
|
154
156
|
decision: "pending",
|
|
155
157
|
reason: "pending approval",
|
|
156
|
-
|
|
157
|
-
|
|
158
|
+
secret_id: secret.secret_id,
|
|
159
|
+
missing_grants,
|
|
158
160
|
};
|
|
159
161
|
}
|
|
160
162
|
async agentDispatchSecret(request) {
|
|
@@ -162,73 +164,73 @@ export class VaultCore {
|
|
|
162
164
|
const authorization = await this.agentAuthorizeDispatch(request);
|
|
163
165
|
if (authorization.decision === "deny") {
|
|
164
166
|
const result = {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
+
vault_id: this._deps.vault_id,
|
|
168
|
+
request_id: request.request_id,
|
|
167
169
|
status: DispatchStatus.DENIED,
|
|
168
|
-
|
|
170
|
+
target_url: request.target_url,
|
|
169
171
|
method: request.method,
|
|
170
172
|
error: authorization.reason ?? "denied",
|
|
171
173
|
};
|
|
172
|
-
await this._appendAudit(toAuditEntry(this._deps, request.agent,
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
174
|
+
await this._appendAudit(toAuditEntry(this._deps, request.agent, AuditOperation.POLICY_EVALUATE, "denied", "not_executed", authorization.reason ?? "denied", {
|
|
175
|
+
request_id: request.request_id,
|
|
176
|
+
target: { kind: "http", url: request.target_url },
|
|
177
|
+
secret_alias: request.secret_alias,
|
|
176
178
|
}));
|
|
177
179
|
await this._recordRequestInternal(request, result);
|
|
178
180
|
return result;
|
|
179
181
|
}
|
|
180
182
|
if (authorization.decision === "pending") {
|
|
181
183
|
const result = {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
+
vault_id: this._deps.vault_id,
|
|
185
|
+
request_id: request.request_id,
|
|
184
186
|
status: DispatchStatus.PENDING,
|
|
185
|
-
|
|
187
|
+
target_url: request.target_url,
|
|
186
188
|
method: request.method,
|
|
187
189
|
};
|
|
188
|
-
await this._appendAudit(toAuditEntry(this._deps, request.agent,
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
190
|
+
await this._appendAudit(toAuditEntry(this._deps, request.agent, AuditOperation.DISPATCH_HOLD, "allowed", "not_executed", "request held for human approval", {
|
|
191
|
+
request_id: request.request_id,
|
|
192
|
+
target: { kind: "http", url: request.target_url },
|
|
193
|
+
secret_alias: request.secret_alias,
|
|
192
194
|
}));
|
|
193
|
-
await this._recordRequestInternal(request, result, authorization.
|
|
195
|
+
await this._recordRequestInternal(request, result, authorization.missing_grants);
|
|
194
196
|
return result;
|
|
195
197
|
}
|
|
196
198
|
// Proceed with dispatch
|
|
197
|
-
const
|
|
198
|
-
const secretRecord = await this._deps.secrets.getById(
|
|
199
|
+
const secret_id = authorization.secret_id;
|
|
200
|
+
const secretRecord = await this._deps.secrets.getById(secret_id);
|
|
199
201
|
if (!secretRecord) {
|
|
200
202
|
throw new VaultCoreError("secret record not found after authorization", "VAULT_INTERNAL_ERROR");
|
|
201
203
|
}
|
|
202
|
-
const plaintext = await this._deps.custody.load(
|
|
204
|
+
const plaintext = await this._deps.custody.load(secret_id);
|
|
203
205
|
if (plaintext === null) {
|
|
204
206
|
throw new VaultCoreError("secret material not found", "VAULT_SECRET_NOT_FOUND");
|
|
205
207
|
}
|
|
206
208
|
const result = await this._deps.executor.dispatch({
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
209
|
+
vault_id: this._deps.vault_id,
|
|
210
|
+
request_id: request.request_id,
|
|
211
|
+
secret_id: secret_id,
|
|
212
|
+
target_url: request.target_url,
|
|
211
213
|
method: request.method,
|
|
212
214
|
headers: request.headers,
|
|
213
215
|
body: request.body,
|
|
214
216
|
}, { record: secretRecord, plaintext });
|
|
215
|
-
await this._appendAudit(toAuditEntry(this._deps, request.agent,
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
217
|
+
await this._appendAudit(toAuditEntry(this._deps, request.agent, AuditOperation.SECRET_DISPATCH, "allowed", result.status === DispatchStatus.SUCCEEDED ? "succeeded" : "failed", result.status === DispatchStatus.SUCCEEDED ? "dispatch completed" : (result.error ?? "dispatch failed"), {
|
|
218
|
+
request_id: request.request_id,
|
|
219
|
+
target: { kind: "http", url: request.target_url },
|
|
220
|
+
secret_alias: request.secret_alias,
|
|
221
|
+
secret_id: secret_id.value,
|
|
220
222
|
}));
|
|
221
223
|
await this._recordRequestInternal(request, result);
|
|
222
224
|
return {
|
|
223
225
|
...result,
|
|
224
|
-
|
|
225
|
-
|
|
226
|
+
vault_id: this._deps.vault_id,
|
|
227
|
+
response_body: undefined, // Hide body in direct return
|
|
226
228
|
};
|
|
227
229
|
}
|
|
228
230
|
// ─── Pending Approval ─────────────────────────────────────────────────────────
|
|
229
|
-
async ownerApproveDispatch(actor,
|
|
231
|
+
async ownerApproveDispatch(actor, request_id, decision) {
|
|
230
232
|
this._assertOwnerPrincipal(actor);
|
|
231
|
-
const record = await this._deps.requests.get(this._deps.
|
|
233
|
+
const record = await this._deps.requests.get(this._deps.vault_id, request_id);
|
|
232
234
|
if (!record) {
|
|
233
235
|
throw new VaultCoreError("request record not found", "VAULT_REQUEST_NOT_FOUND");
|
|
234
236
|
}
|
|
@@ -241,61 +243,61 @@ export class VaultCore {
|
|
|
241
243
|
execution: { status: DispatchStatus.DENIED },
|
|
242
244
|
};
|
|
243
245
|
await this._deps.requests.save(updated);
|
|
244
|
-
await this._appendAudit(toAuditEntry(this._deps, actor,
|
|
245
|
-
|
|
246
|
-
|
|
246
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditOperation.DISPATCH_REJECT, "allowed", "succeeded", "dispatch rejected by owner", {
|
|
247
|
+
request_id,
|
|
248
|
+
root_agent_id: record.root_agent_id,
|
|
247
249
|
}));
|
|
248
250
|
return null;
|
|
249
251
|
}
|
|
250
|
-
const
|
|
251
|
-
if (!
|
|
252
|
-
throw new VaultCoreError("record missing
|
|
252
|
+
const secret_alias = record.request.secret_alias;
|
|
253
|
+
if (!secret_alias) {
|
|
254
|
+
throw new VaultCoreError("record missing secret_alias", "VAULT_INTERNAL_ERROR");
|
|
253
255
|
}
|
|
254
|
-
const secret = await this._deps.secrets.getByAlias({ value:
|
|
256
|
+
const secret = await this._deps.secrets.getByAlias({ value: secret_alias });
|
|
255
257
|
if (!secret) {
|
|
256
258
|
throw new VaultCoreError("secret not found during approval", "VAULT_SECRET_NOT_FOUND");
|
|
257
259
|
}
|
|
258
260
|
// Auto-grant if requested
|
|
259
261
|
if (decision === "allow_and_grant") {
|
|
260
262
|
const now = this._deps.clock.nowIso();
|
|
261
|
-
const
|
|
263
|
+
const site_id = extractDomain(record.request.target_url);
|
|
262
264
|
await Promise.all([
|
|
263
|
-
this._deps.
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
265
|
+
this._deps.agent_secretGrants.upsert({
|
|
266
|
+
vault_id: this._deps.vault_id,
|
|
267
|
+
root_agent_id: record.root_agent_id,
|
|
268
|
+
secret_alias,
|
|
267
269
|
status: "approved",
|
|
268
|
-
|
|
269
|
-
|
|
270
|
+
requested_at: now,
|
|
271
|
+
granted_at: now,
|
|
270
272
|
}),
|
|
271
|
-
this._deps.
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
273
|
+
this._deps.secret_destinationGrants.upsert({
|
|
274
|
+
vault_id: this._deps.vault_id,
|
|
275
|
+
secret_alias,
|
|
276
|
+
site_id,
|
|
275
277
|
status: "approved",
|
|
276
|
-
|
|
277
|
-
|
|
278
|
+
requested_at: now,
|
|
279
|
+
granted_at: now,
|
|
278
280
|
}),
|
|
279
281
|
]);
|
|
280
|
-
await this._appendAudit(toAuditEntry(this._deps, actor,
|
|
281
|
-
|
|
282
|
-
|
|
282
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditOperation.GRANT_SECRET, "allowed", "succeeded", "granted during dispatch approval", {
|
|
283
|
+
root_agent_id: record.root_agent_id,
|
|
284
|
+
secret_alias: secret_alias,
|
|
283
285
|
}));
|
|
284
|
-
await this._appendAudit(toAuditEntry(this._deps, actor,
|
|
285
|
-
|
|
286
|
-
|
|
286
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditOperation.GRANT_DESTINATION, "allowed", "succeeded", "granted during dispatch approval", {
|
|
287
|
+
secret_alias: secret_alias,
|
|
288
|
+
site_id,
|
|
287
289
|
}));
|
|
288
290
|
}
|
|
289
291
|
// Execute
|
|
290
|
-
const plaintext = await this._deps.custody.load(secret.
|
|
292
|
+
const plaintext = await this._deps.custody.load(secret.secret_id);
|
|
291
293
|
if (plaintext === null) {
|
|
292
294
|
throw new VaultCoreError("secret material not found", "VAULT_SECRET_NOT_FOUND");
|
|
293
295
|
}
|
|
294
296
|
const result = await this._deps.executor.dispatch({
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
297
|
+
vault_id: this._deps.vault_id,
|
|
298
|
+
request_id,
|
|
299
|
+
secret_id: secret.secret_id,
|
|
300
|
+
target_url: record.request.target_url,
|
|
299
301
|
method: record.request.method,
|
|
300
302
|
headers: record.request.headers,
|
|
301
303
|
body: record.request.body,
|
|
@@ -303,77 +305,77 @@ export class VaultCore {
|
|
|
303
305
|
const finalRecord = {
|
|
304
306
|
...record,
|
|
305
307
|
response: {
|
|
306
|
-
status: result.
|
|
307
|
-
body: result.
|
|
308
|
+
status: result.response_status,
|
|
309
|
+
body: result.response_body,
|
|
308
310
|
error: result.error,
|
|
309
311
|
},
|
|
310
312
|
execution: { status: result.status },
|
|
311
313
|
};
|
|
312
314
|
await this._deps.requests.save(finalRecord);
|
|
313
|
-
await this._appendAudit(toAuditEntry(this._deps, actor,
|
|
314
|
-
|
|
315
|
-
|
|
315
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditOperation.DISPATCH_APPROVE, "allowed", "succeeded", `dispatch approved (${decision})`, {
|
|
316
|
+
request_id,
|
|
317
|
+
root_agent_id: record.root_agent_id,
|
|
316
318
|
}));
|
|
317
319
|
return result;
|
|
318
320
|
}
|
|
319
321
|
// ─── Agent Control APIs ───────────────────────────────────────────────────────
|
|
320
322
|
async agentGetRuntimeManifest(command) {
|
|
321
323
|
await this._verifyAgentControlProof(command, "get_manifest");
|
|
322
|
-
const agentRecord = await this._deps.agentRecords.get(this._deps.
|
|
324
|
+
const agentRecord = await this._deps.agentRecords.get(this._deps.vault_id, command.agent.id);
|
|
323
325
|
if (!agentRecord) {
|
|
324
326
|
throw new VaultCoreError("agent.identity not registered", "VAULT_DISPATCH_DENIED");
|
|
325
327
|
}
|
|
326
|
-
const [
|
|
327
|
-
this._deps.
|
|
328
|
-
this._deps.
|
|
328
|
+
const [agent_secrets, secret_destinations] = await Promise.all([
|
|
329
|
+
this._deps.agent_secretGrants.list(this._deps.vault_id, command.agent.id),
|
|
330
|
+
this._deps.secret_destinationGrants.list(this._deps.vault_id), // All destination grants for these secrets? Or just a subset?
|
|
329
331
|
// For simplicity, return all destinations that mention a secret the agent has a grant for.
|
|
330
332
|
]);
|
|
331
|
-
const
|
|
332
|
-
const relevantDestinations =
|
|
333
|
+
const secret_aliases = new Set(agent_secrets.map(g => g.secret_alias));
|
|
334
|
+
const relevantDestinations = secret_destinations.filter(d => secret_aliases.has(d.secret_alias));
|
|
333
335
|
return {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
336
|
+
root_agent_id: command.agent.id,
|
|
337
|
+
vault_id: this._deps.vault_id.value,
|
|
338
|
+
issued_at: this._deps.clock.nowIso(),
|
|
337
339
|
agent: {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
+
root_agent_id: agentRecord.root_agent_id,
|
|
341
|
+
public_key: agentRecord.public_key,
|
|
340
342
|
nickname: agentRecord.nickname,
|
|
341
343
|
metadata: agentRecord.metadata,
|
|
342
344
|
},
|
|
343
345
|
grants: {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
+
agent_secrets: agent_secrets.filter(g => g.status === "approved"),
|
|
347
|
+
secret_destinations: relevantDestinations.filter(d => d.status === "approved"),
|
|
346
348
|
},
|
|
347
349
|
tools: getAgentToolbox(),
|
|
348
350
|
};
|
|
349
351
|
}
|
|
350
352
|
async agentListSecrets(command) {
|
|
351
353
|
await this._verifyAgentControlProof(command, "list_secrets");
|
|
352
|
-
const records = await this._deps.secrets.list(this._deps.
|
|
353
|
-
const grants = await this._deps.
|
|
354
|
-
const approvedAliases = new Set(grants.filter(g => g.status === "approved").map(g => g.
|
|
354
|
+
const records = await this._deps.secrets.list(this._deps.vault_id);
|
|
355
|
+
const grants = await this._deps.agent_secretGrants.list(this._deps.vault_id, command.agent.id);
|
|
356
|
+
const approvedAliases = new Set(grants.filter(g => g.status === "approved").map(g => g.secret_alias));
|
|
355
357
|
return records.map(record => ({
|
|
356
|
-
|
|
357
|
-
|
|
358
|
+
vault_id: record.vault_id,
|
|
359
|
+
secret_id: record.secret_id,
|
|
358
360
|
alias: record.alias,
|
|
359
361
|
version: record.version,
|
|
360
|
-
|
|
361
|
-
|
|
362
|
+
lifecycle_status: record.lifecycle_status ?? "ACTIVE",
|
|
363
|
+
issuer_id: record.issuer_id,
|
|
362
364
|
source: record.source,
|
|
363
|
-
|
|
364
|
-
|
|
365
|
+
created_at: record.created_at,
|
|
366
|
+
updated_at: record.updated_at,
|
|
365
367
|
granted: approvedAliases.has(record.alias.value),
|
|
366
368
|
}));
|
|
367
369
|
}
|
|
368
370
|
async agentListRequests(command) {
|
|
369
371
|
await this._verifyAgentControlProof(command, "list_requests");
|
|
370
|
-
const records = await this._deps.requests.list(this._deps.
|
|
372
|
+
const records = await this._deps.requests.list(this._deps.vault_id, command.agent.id);
|
|
371
373
|
return records.map(r => this.toAgentVisibleRequestRecord(r));
|
|
372
374
|
}
|
|
373
375
|
async agentGetRequest(command) {
|
|
374
376
|
await this._verifyAgentControlProof(command, "read_request");
|
|
375
|
-
const record = await this._deps.requests.get(this._deps.
|
|
376
|
-
if (!record || record.
|
|
377
|
+
const record = await this._deps.requests.get(this._deps.vault_id, command.target_request_id);
|
|
378
|
+
if (!record || record.root_agent_id !== command.agent.id) {
|
|
377
379
|
throw new VaultCoreError("request record not found", "VAULT_READ_DENIED");
|
|
378
380
|
}
|
|
379
381
|
// By default, no read-policy is granted anymore in this simplified model.
|
|
@@ -382,10 +384,10 @@ export class VaultCore {
|
|
|
382
384
|
// unless they have a specific grant (omitted for now to focus on dispatch).
|
|
383
385
|
const parsedResponseBody = applyResponseReadPolicy(record.response?.body, { paths: [] });
|
|
384
386
|
return {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
387
|
+
request_id: record.request_id,
|
|
388
|
+
execution_status: record.execution.status,
|
|
389
|
+
response_status: record.response?.status,
|
|
390
|
+
response_body: parsedResponseBody,
|
|
389
391
|
error: record.response?.error,
|
|
390
392
|
};
|
|
391
393
|
}
|
|
@@ -393,37 +395,41 @@ export class VaultCore {
|
|
|
393
395
|
async ownerRegisterAgentIdentity(command) {
|
|
394
396
|
this._assertOwnerPrincipal(command.owner);
|
|
395
397
|
await this._deps.agentRecords.register(command.agentRecord);
|
|
396
|
-
await this._appendAudit(toAuditEntry(this._deps, command.owner,
|
|
398
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditOperation.IDENTITY_REGISTER, "allowed", "succeeded", `agent identity registered: "${command.agentRecord.root_agent_id}"`, { root_agent_id: command.agentRecord.root_agent_id }));
|
|
397
399
|
}
|
|
398
400
|
async ownerUpdateAgentIdentity(command) {
|
|
399
401
|
this._assertOwnerPrincipal(command.owner);
|
|
400
|
-
const existing = await this._deps.agentRecords.get(command.
|
|
402
|
+
const existing = await this._deps.agentRecords.get(command.vault_id, command.root_agent_id);
|
|
401
403
|
if (!existing)
|
|
402
404
|
throw new VaultCoreError("agent identity not found", "VAULT_IDENTITY_NOT_FOUND");
|
|
403
405
|
const updated = { ...existing, nickname: command.nickname ?? existing.nickname, metadata: command.metadata ?? existing.metadata };
|
|
404
406
|
await this._deps.agentRecords.register(updated);
|
|
405
|
-
await this._appendAudit(toAuditEntry(this._deps, command.owner,
|
|
407
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditOperation.IDENTITY_UPDATE, "allowed", "succeeded", `agent identity updated: "${command.root_agent_id}"`, { root_agent_id: command.root_agent_id }));
|
|
406
408
|
return updated;
|
|
407
409
|
}
|
|
408
410
|
async ownerCreateSecret(command) {
|
|
409
411
|
this._assertOwnerPrincipal(command.owner);
|
|
410
412
|
await this._deps.policy.authorizeWrite(command);
|
|
411
|
-
const
|
|
413
|
+
const existing = await this._deps.secrets.getByAlias({ value: command.alias });
|
|
414
|
+
if (existing) {
|
|
415
|
+
throw new VaultCoreError(`secret alias already exists: "${command.alias}"`, "VAULT_ALIAS_ALREADY_EXISTS");
|
|
416
|
+
}
|
|
417
|
+
const secret_id = this._deps.ids.newSecretId();
|
|
412
418
|
const now = this._deps.clock.nowIso();
|
|
413
419
|
const record = {
|
|
414
|
-
|
|
415
|
-
|
|
420
|
+
vault_id: command.vault_id,
|
|
421
|
+
secret_id,
|
|
416
422
|
alias: { value: command.alias },
|
|
417
423
|
version: this._deps.ids.newVersion(),
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
source: command.source ? (command.source.kind === "request" ? { kind: "request",
|
|
421
|
-
|
|
422
|
-
|
|
424
|
+
lifecycle_status: "ACTIVE",
|
|
425
|
+
issuer_id: null,
|
|
426
|
+
source: command.source ? (command.source.kind === "request" ? { kind: "request", request_id: command.source.request_id } : { kind: "manual" }) : { kind: "manual" },
|
|
427
|
+
created_at: now,
|
|
428
|
+
updated_at: now,
|
|
423
429
|
};
|
|
424
430
|
await this._deps.secrets.save(record);
|
|
425
|
-
await this._deps.custody.store(
|
|
426
|
-
await this._appendAudit(toAuditEntry(this._deps, command.owner,
|
|
431
|
+
await this._deps.custody.store(secret_id, command.plaintext);
|
|
432
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditOperation.SECRET_WRITE, "allowed", "succeeded", `secret created: "${command.alias}"`, { secret_alias: command.alias, secret_id: secret_id.value }));
|
|
427
433
|
return record;
|
|
428
434
|
}
|
|
429
435
|
async ownerUpdateSecret(command) {
|
|
@@ -432,16 +438,16 @@ export class VaultCore {
|
|
|
432
438
|
const existing = await this._deps.secrets.getByAlias({ value: command.alias });
|
|
433
439
|
if (!existing)
|
|
434
440
|
throw new VaultCoreError("secret not found", "VAULT_SECRET_NOT_FOUND");
|
|
435
|
-
const
|
|
441
|
+
const secret_id = existing.secret_id;
|
|
436
442
|
const now = this._deps.clock.nowIso();
|
|
437
443
|
const record = {
|
|
438
444
|
...existing,
|
|
439
445
|
version: this._deps.ids.newVersion(),
|
|
440
|
-
|
|
446
|
+
updated_at: now,
|
|
441
447
|
};
|
|
442
448
|
await this._deps.secrets.save(record);
|
|
443
|
-
await this._deps.custody.store(
|
|
444
|
-
await this._appendAudit(toAuditEntry(this._deps, command.owner,
|
|
449
|
+
await this._deps.custody.store(secret_id, command.plaintext);
|
|
450
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditOperation.SECRET_WRITE, "allowed", "succeeded", `secret updated: "${command.alias}"`, { secret_alias: command.alias, secret_id: secret_id.value }));
|
|
445
451
|
return record;
|
|
446
452
|
}
|
|
447
453
|
async ownerRemoveSecret(command) {
|
|
@@ -449,43 +455,14 @@ export class VaultCore {
|
|
|
449
455
|
const record = await this._deps.secrets.getByAlias({ value: command.alias });
|
|
450
456
|
if (!record)
|
|
451
457
|
throw new VaultCoreError("secret not found", "VAULT_SECRET_NOT_FOUND");
|
|
452
|
-
await this._deps.secrets.delete(record.
|
|
453
|
-
await this._deps.custody.delete(record.
|
|
454
|
-
await this._appendAudit(toAuditEntry(this._deps, command.owner,
|
|
455
|
-
}
|
|
456
|
-
async ownerWriteSecret(command) {
|
|
457
|
-
this._assertOwnerPrincipal(command.owner ?? command.issuer);
|
|
458
|
-
await this._deps.policy.authorizeWrite(command);
|
|
459
|
-
const existing = await this._deps.secrets.getByAlias({ value: command.alias });
|
|
460
|
-
const secretId = existing ? existing.secretId : this._deps.ids.newSecretId();
|
|
461
|
-
const now = this._deps.clock.nowIso();
|
|
462
|
-
const record = {
|
|
463
|
-
vaultId: command.vaultId,
|
|
464
|
-
secretId,
|
|
465
|
-
alias: { value: command.alias },
|
|
466
|
-
version: this._deps.ids.newVersion(),
|
|
467
|
-
lifecycleStatus: "ACTIVE",
|
|
468
|
-
issuerId: command.issuer?.id ?? null,
|
|
469
|
-
source: command.source,
|
|
470
|
-
createdAt: existing ? existing.createdAt : now,
|
|
471
|
-
updatedAt: now,
|
|
472
|
-
};
|
|
473
|
-
await this._deps.secrets.save(record);
|
|
474
|
-
await this._deps.custody.store(secretId, command.plaintext);
|
|
475
|
-
// Generic write doesn't have a specific audit message here, assuming it's called by Create/Update which do their own audit.
|
|
476
|
-
// However, if called directly:
|
|
477
|
-
if (!existing) {
|
|
478
|
-
await this._appendAudit(toAuditEntry(this._deps, command.owner ?? command.issuer, AuditAction.WRITE_SECRET, AuditOutcome.SUCCEEDED, `secret created via generic write: "${command.alias}"`, { secretAlias: command.alias, secretId: secretId.value }));
|
|
479
|
-
}
|
|
480
|
-
else {
|
|
481
|
-
await this._appendAudit(toAuditEntry(this._deps, command.owner ?? command.issuer, AuditAction.WRITE_SECRET, AuditOutcome.SUCCEEDED, `secret updated via generic write: "${command.alias}"`, { secretAlias: command.alias, secretId: secretId.value }));
|
|
482
|
-
}
|
|
483
|
-
return record;
|
|
458
|
+
await this._deps.secrets.delete(record.secret_id);
|
|
459
|
+
await this._deps.custody.delete(record.secret_id);
|
|
460
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditOperation.SECRET_DELETE, "allowed", "succeeded", `secret deleted: "${command.alias}"`, { secret_alias: command.alias, secret_id: record.secret_id.value }));
|
|
484
461
|
}
|
|
485
462
|
async ownerReadAudit(actor, query) {
|
|
486
463
|
this._assertOwnerPrincipal(actor);
|
|
487
464
|
const entries = await this._deps.audit.query(query);
|
|
488
|
-
await this._appendAudit(toAuditEntry(this._deps, actor,
|
|
465
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditOperation.MANAGEMENT_READ_AUDIT, "allowed", "succeeded", "audit log accessed", { detail: JSON.stringify(query) }));
|
|
489
466
|
return entries;
|
|
490
467
|
}
|
|
491
468
|
async ownerExportSecret(actor, alias) {
|
|
@@ -493,73 +470,73 @@ export class VaultCore {
|
|
|
493
470
|
const record = await this._deps.secrets.getByAlias({ value: alias });
|
|
494
471
|
if (!record)
|
|
495
472
|
throw new VaultCoreError("secret not found", "VAULT_SECRET_NOT_FOUND");
|
|
496
|
-
const plaintext = await this._deps.custody.load(record.
|
|
473
|
+
const plaintext = await this._deps.custody.load(record.secret_id);
|
|
497
474
|
if (plaintext === null)
|
|
498
475
|
throw new VaultCoreError("secret material not found", "VAULT_SECRET_NOT_FOUND");
|
|
499
|
-
await this._appendAudit(toAuditEntry(this._deps, actor,
|
|
500
|
-
return {
|
|
476
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditOperation.SECRET_EXPORT, "allowed", "succeeded", `secret exported as plaintext: "${alias}"`, { secret_alias: alias, secret_id: record.secret_id.value }));
|
|
477
|
+
return { vault_id: this._deps.vault_id, secret_id: record.secret_id, alias: record.alias, plaintext, exported_at: this._deps.clock.nowIso() };
|
|
501
478
|
}
|
|
502
479
|
async ownerListAgents(actor) {
|
|
503
480
|
this._assertOwnerPrincipal(actor);
|
|
504
|
-
const identities = await this._deps.agentRecords.list(this._deps.
|
|
505
|
-
const
|
|
481
|
+
const identities = await this._deps.agentRecords.list(this._deps.vault_id);
|
|
482
|
+
const session_tokens = await this._deps.session_tokens.list();
|
|
506
483
|
const tokensByAgentId = new Map();
|
|
507
|
-
for (const st of
|
|
508
|
-
const list = tokensByAgentId.get(st.
|
|
484
|
+
for (const st of session_tokens) {
|
|
485
|
+
const list = tokensByAgentId.get(st.root_agent_id) ?? [];
|
|
509
486
|
list.push(st);
|
|
510
|
-
tokensByAgentId.set(st.
|
|
487
|
+
tokensByAgentId.set(st.root_agent_id, list);
|
|
511
488
|
}
|
|
512
|
-
const result = identities.map(id => ({ ...id,
|
|
513
|
-
await this._appendAudit(toAuditEntry(this._deps, actor,
|
|
489
|
+
const result = identities.map(id => ({ ...id, session_tokens: tokensByAgentId.get(id.root_agent_id) ?? [] }));
|
|
490
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditOperation.MANAGEMENT_LIST_AGENTS, "allowed", "succeeded", "agent identity list accessed"));
|
|
514
491
|
return result;
|
|
515
492
|
}
|
|
516
|
-
async ownerListRequests(actor,
|
|
493
|
+
async ownerListRequests(actor, root_agent_id) {
|
|
517
494
|
this._assertOwnerPrincipal(actor);
|
|
518
|
-
const records = await this._deps.requests.list(this._deps.
|
|
519
|
-
await this._appendAudit(toAuditEntry(this._deps, actor,
|
|
495
|
+
const records = await this._deps.requests.list(this._deps.vault_id, root_agent_id);
|
|
496
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditOperation.MANAGEMENT_LIST_REQUESTS, "allowed", "succeeded", "request list accessed"));
|
|
520
497
|
return records.map(r => this.toOwnerVisibleRequestRecord(r));
|
|
521
498
|
}
|
|
522
|
-
async ownerGetRequest(actor,
|
|
499
|
+
async ownerGetRequest(actor, request_id) {
|
|
523
500
|
this._assertOwnerPrincipal(actor);
|
|
524
|
-
const record = await this._deps.requests.get(this._deps.
|
|
501
|
+
const record = await this._deps.requests.get(this._deps.vault_id, request_id);
|
|
525
502
|
if (!record)
|
|
526
503
|
throw new VaultCoreError("request record not found", "VAULT_REQUEST_NOT_FOUND");
|
|
527
504
|
const result = this.toOwnerRequestRecord(record);
|
|
528
|
-
await this._appendAudit(toAuditEntry(this._deps, actor,
|
|
505
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditOperation.MANAGEMENT_READ_REQUEST, "allowed", "succeeded", `dispatch request detailed: "${request_id}"`, { request_id }));
|
|
529
506
|
return result;
|
|
530
507
|
}
|
|
531
508
|
async ownerListSecrets(actor) {
|
|
532
509
|
this._assertOwnerPrincipal(actor);
|
|
533
|
-
const records = await this._deps.secrets.list(this._deps.
|
|
534
|
-
await this._appendAudit(toAuditEntry(this._deps, actor,
|
|
510
|
+
const records = await this._deps.secrets.list(this._deps.vault_id);
|
|
511
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditOperation.MANAGEMENT_LIST_SECRETS, "allowed", "succeeded", "secret list accessed"));
|
|
535
512
|
return records.map(r => ({
|
|
536
|
-
|
|
537
|
-
|
|
513
|
+
vault_id: r.vault_id,
|
|
514
|
+
secret_id: r.secret_id,
|
|
538
515
|
alias: r.alias,
|
|
539
516
|
version: r.version,
|
|
540
|
-
|
|
541
|
-
|
|
517
|
+
lifecycle_status: r.lifecycle_status ?? "ACTIVE",
|
|
518
|
+
issuer_id: r.issuer_id,
|
|
542
519
|
source: r.source,
|
|
543
|
-
|
|
544
|
-
|
|
520
|
+
created_at: r.created_at,
|
|
521
|
+
updated_at: r.updated_at,
|
|
545
522
|
granted: true,
|
|
546
523
|
}));
|
|
547
524
|
}
|
|
548
525
|
async ownerIssueSessionToken(request) {
|
|
549
526
|
this._assertOwnerPrincipal(request.actor);
|
|
550
|
-
const token = await this._deps.
|
|
551
|
-
await this._appendAudit(toAuditEntry(this._deps, request.actor,
|
|
552
|
-
return { token,
|
|
527
|
+
const token = await this._deps.session_tokens.issue(request.root_agent_id);
|
|
528
|
+
await this._appendAudit(toAuditEntry(this._deps, request.actor, AuditOperation.IDENTITY_ISSUE_TOKEN, "allowed", "succeeded", `session token issued for agent: "${request.root_agent_id}"`, { root_agent_id: request.root_agent_id }));
|
|
529
|
+
return { token, root_agent_id: request.root_agent_id, issued_at: this._deps.clock.nowIso() };
|
|
553
530
|
}
|
|
554
531
|
async ownerIssueAllAgentSessionTokens(actor) {
|
|
555
532
|
this._assertOwnerPrincipal(actor);
|
|
556
533
|
const agents = await this.ownerListAgents(actor);
|
|
557
|
-
return Promise.all(agents.map(a => this.ownerIssueSessionToken({
|
|
534
|
+
return Promise.all(agents.map(a => this.ownerIssueSessionToken({ vault_id: this._deps.vault_id, actor, root_agent_id: a.root_agent_id })));
|
|
558
535
|
}
|
|
559
536
|
async ownerRevokeSessionToken(request) {
|
|
560
537
|
this._assertOwnerPrincipal(request.actor);
|
|
561
|
-
await this._deps.
|
|
562
|
-
await this._appendAudit(toAuditEntry(this._deps, request.actor,
|
|
538
|
+
await this._deps.session_tokens.revoke(request.token);
|
|
539
|
+
await this._appendAudit(toAuditEntry(this._deps, request.actor, AuditOperation.IDENTITY_REVOKE_TOKEN, "allowed", "succeeded", "session token revoked"));
|
|
563
540
|
}
|
|
564
541
|
// ─── Event Observers ──────────────────────────────────────────────────────────
|
|
565
542
|
_requestObservers = [];
|
|
@@ -575,27 +552,27 @@ export class VaultCore {
|
|
|
575
552
|
return this.ownerOnPendingDispatch(callback);
|
|
576
553
|
}
|
|
577
554
|
// ─── Internal Helpers ──────────────────────────────────────────────────────────
|
|
578
|
-
async _recordRequestInternal(request, result,
|
|
555
|
+
async _recordRequestInternal(request, result, missing_grants) {
|
|
579
556
|
const record = {
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
557
|
+
vault_id: this._deps.vault_id,
|
|
558
|
+
request_id: request.request_id,
|
|
559
|
+
root_agent_id: request.agent.id,
|
|
583
560
|
reason: request.reason,
|
|
584
|
-
|
|
561
|
+
created_at: this._deps.clock.nowIso(),
|
|
585
562
|
request: {
|
|
586
|
-
|
|
563
|
+
target_url: request.target_url,
|
|
587
564
|
method: request.method,
|
|
588
565
|
headers: request.headers,
|
|
589
566
|
body: request.body,
|
|
590
|
-
|
|
567
|
+
secret_alias: request.secret_alias,
|
|
591
568
|
},
|
|
592
569
|
response: {
|
|
593
|
-
status: result.
|
|
594
|
-
body: result.
|
|
570
|
+
status: result.response_status,
|
|
571
|
+
body: result.response_body,
|
|
595
572
|
error: result.error,
|
|
596
573
|
},
|
|
597
574
|
execution: { status: result.status },
|
|
598
|
-
|
|
575
|
+
missing_grants,
|
|
599
576
|
};
|
|
600
577
|
await this._deps.requests.save(record);
|
|
601
578
|
if (result.status === DispatchStatus.PENDING) {
|
|
@@ -604,46 +581,46 @@ export class VaultCore {
|
|
|
604
581
|
}
|
|
605
582
|
toAgentVisibleRequestRecord(record) {
|
|
606
583
|
return {
|
|
607
|
-
|
|
608
|
-
|
|
584
|
+
request_id: record.request_id,
|
|
585
|
+
created_at: record.created_at,
|
|
609
586
|
reason: record.reason,
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
587
|
+
target_url: record.request.target_url,
|
|
588
|
+
execution_status: record.execution.status,
|
|
589
|
+
response_status: record.response?.status,
|
|
613
590
|
error: record.response?.error,
|
|
614
|
-
|
|
591
|
+
has_response_body: !!record.response?.body,
|
|
615
592
|
};
|
|
616
593
|
}
|
|
617
594
|
toOwnerVisibleRequestRecord(record) {
|
|
618
595
|
return {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
596
|
+
request_id: record.request_id,
|
|
597
|
+
created_at: record.created_at,
|
|
598
|
+
root_agent_id: record.root_agent_id,
|
|
622
599
|
reason: record.reason,
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
600
|
+
target_url: record.request.target_url,
|
|
601
|
+
execution_status: record.execution.status,
|
|
602
|
+
response_status: record.response?.status,
|
|
626
603
|
error: record.response?.error,
|
|
627
|
-
|
|
628
|
-
|
|
604
|
+
has_response_body: !!record.response?.body,
|
|
605
|
+
missing_grants: record.missing_grants,
|
|
629
606
|
};
|
|
630
607
|
}
|
|
631
608
|
toOwnerRequestRecord(record) {
|
|
632
609
|
return {
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
610
|
+
request_id: record.request_id,
|
|
611
|
+
created_at: record.created_at,
|
|
612
|
+
root_agent_id: record.root_agent_id,
|
|
636
613
|
reason: record.reason,
|
|
637
614
|
request: {
|
|
638
|
-
|
|
615
|
+
target_url: record.request.target_url,
|
|
639
616
|
method: record.request.method,
|
|
640
617
|
headers: record.request.headers,
|
|
641
618
|
body: record.request.body,
|
|
642
|
-
|
|
619
|
+
secret_alias: record.request.secret_alias,
|
|
643
620
|
},
|
|
644
621
|
response: record.response,
|
|
645
|
-
|
|
646
|
-
|
|
622
|
+
execution_status: record.execution.status,
|
|
623
|
+
missing_grants: record.missing_grants,
|
|
647
624
|
};
|
|
648
625
|
}
|
|
649
626
|
}
|