@the-ai-company/cbio-node-runtime 1.63.6 → 1.63.8
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 +2 -2
- 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 +169 -176
- package/dist/clients/owner/client.js.map +1 -1
- package/dist/clients/owner/contracts.d.ts +45 -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 -41
- package/dist/vault-core/core.js +257 -255
- 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/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 -66
- 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 -46
- package/dist/vault-ingress/index.js +34 -34
- 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 +1 -1
- 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 -53
- 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 +5 -5
- 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 -8
- 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 +1 -1
- 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,37 @@ 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 secret_id = this._deps.ids.newSecretId();
|
|
412
414
|
const now = this._deps.clock.nowIso();
|
|
413
415
|
const record = {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
+
vault_id: command.vault_id,
|
|
417
|
+
secret_id,
|
|
416
418
|
alias: { value: command.alias },
|
|
417
419
|
version: this._deps.ids.newVersion(),
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
source: command.source ? (command.source.kind === "request" ? { kind: "request",
|
|
421
|
-
|
|
422
|
-
|
|
420
|
+
lifecycle_status: "ACTIVE",
|
|
421
|
+
issuer_id: null,
|
|
422
|
+
source: command.source ? (command.source.kind === "request" ? { kind: "request", request_id: command.source.request_id } : { kind: "manual" }) : { kind: "manual" },
|
|
423
|
+
created_at: now,
|
|
424
|
+
updated_at: now,
|
|
423
425
|
};
|
|
424
426
|
await this._deps.secrets.save(record);
|
|
425
|
-
await this._deps.custody.store(
|
|
426
|
-
await this._appendAudit(toAuditEntry(this._deps, command.owner,
|
|
427
|
+
await this._deps.custody.store(secret_id, command.plaintext);
|
|
428
|
+
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
429
|
return record;
|
|
428
430
|
}
|
|
429
431
|
async ownerUpdateSecret(command) {
|
|
@@ -432,16 +434,16 @@ export class VaultCore {
|
|
|
432
434
|
const existing = await this._deps.secrets.getByAlias({ value: command.alias });
|
|
433
435
|
if (!existing)
|
|
434
436
|
throw new VaultCoreError("secret not found", "VAULT_SECRET_NOT_FOUND");
|
|
435
|
-
const
|
|
437
|
+
const secret_id = existing.secret_id;
|
|
436
438
|
const now = this._deps.clock.nowIso();
|
|
437
439
|
const record = {
|
|
438
440
|
...existing,
|
|
439
441
|
version: this._deps.ids.newVersion(),
|
|
440
|
-
|
|
442
|
+
updated_at: now,
|
|
441
443
|
};
|
|
442
444
|
await this._deps.secrets.save(record);
|
|
443
|
-
await this._deps.custody.store(
|
|
444
|
-
await this._appendAudit(toAuditEntry(this._deps, command.owner,
|
|
445
|
+
await this._deps.custody.store(secret_id, command.plaintext);
|
|
446
|
+
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
447
|
return record;
|
|
446
448
|
}
|
|
447
449
|
async ownerRemoveSecret(command) {
|
|
@@ -449,43 +451,43 @@ export class VaultCore {
|
|
|
449
451
|
const record = await this._deps.secrets.getByAlias({ value: command.alias });
|
|
450
452
|
if (!record)
|
|
451
453
|
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,
|
|
454
|
+
await this._deps.secrets.delete(record.secret_id);
|
|
455
|
+
await this._deps.custody.delete(record.secret_id);
|
|
456
|
+
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 }));
|
|
455
457
|
}
|
|
456
458
|
async ownerWriteSecret(command) {
|
|
457
459
|
this._assertOwnerPrincipal(command.owner ?? command.issuer);
|
|
458
460
|
await this._deps.policy.authorizeWrite(command);
|
|
459
461
|
const existing = await this._deps.secrets.getByAlias({ value: command.alias });
|
|
460
|
-
const
|
|
462
|
+
const secret_id = existing ? existing.secret_id : this._deps.ids.newSecretId();
|
|
461
463
|
const now = this._deps.clock.nowIso();
|
|
462
464
|
const record = {
|
|
463
|
-
|
|
464
|
-
|
|
465
|
+
vault_id: command.vault_id,
|
|
466
|
+
secret_id,
|
|
465
467
|
alias: { value: command.alias },
|
|
466
468
|
version: this._deps.ids.newVersion(),
|
|
467
|
-
|
|
468
|
-
|
|
469
|
+
lifecycle_status: "ACTIVE",
|
|
470
|
+
issuer_id: command.issuer?.id ?? null,
|
|
469
471
|
source: command.source,
|
|
470
|
-
|
|
471
|
-
|
|
472
|
+
created_at: existing ? existing.created_at : now,
|
|
473
|
+
updated_at: now,
|
|
472
474
|
};
|
|
473
475
|
await this._deps.secrets.save(record);
|
|
474
|
-
await this._deps.custody.store(
|
|
476
|
+
await this._deps.custody.store(secret_id, command.plaintext);
|
|
475
477
|
// Generic write doesn't have a specific audit message here, assuming it's called by Create/Update which do their own audit.
|
|
476
478
|
// However, if called directly:
|
|
477
479
|
if (!existing) {
|
|
478
|
-
await this._appendAudit(toAuditEntry(this._deps, command.owner ?? command.issuer,
|
|
480
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner ?? command.issuer, AuditOperation.SECRET_WRITE, "allowed", "succeeded", `secret created via generic write: "${command.alias}"`, { secret_alias: command.alias, secret_id: secret_id.value }));
|
|
479
481
|
}
|
|
480
482
|
else {
|
|
481
|
-
await this._appendAudit(toAuditEntry(this._deps, command.owner ?? command.issuer,
|
|
483
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner ?? command.issuer, AuditOperation.SECRET_WRITE, "allowed", "succeeded", `secret updated via generic write: "${command.alias}"`, { secret_alias: command.alias, secret_id: secret_id.value }));
|
|
482
484
|
}
|
|
483
485
|
return record;
|
|
484
486
|
}
|
|
485
487
|
async ownerReadAudit(actor, query) {
|
|
486
488
|
this._assertOwnerPrincipal(actor);
|
|
487
489
|
const entries = await this._deps.audit.query(query);
|
|
488
|
-
await this._appendAudit(toAuditEntry(this._deps, actor,
|
|
490
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditOperation.MANAGEMENT_READ_AUDIT, "allowed", "succeeded", "audit log accessed", { detail: JSON.stringify(query) }));
|
|
489
491
|
return entries;
|
|
490
492
|
}
|
|
491
493
|
async ownerExportSecret(actor, alias) {
|
|
@@ -493,73 +495,73 @@ export class VaultCore {
|
|
|
493
495
|
const record = await this._deps.secrets.getByAlias({ value: alias });
|
|
494
496
|
if (!record)
|
|
495
497
|
throw new VaultCoreError("secret not found", "VAULT_SECRET_NOT_FOUND");
|
|
496
|
-
const plaintext = await this._deps.custody.load(record.
|
|
498
|
+
const plaintext = await this._deps.custody.load(record.secret_id);
|
|
497
499
|
if (plaintext === null)
|
|
498
500
|
throw new VaultCoreError("secret material not found", "VAULT_SECRET_NOT_FOUND");
|
|
499
|
-
await this._appendAudit(toAuditEntry(this._deps, actor,
|
|
500
|
-
return {
|
|
501
|
+
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 }));
|
|
502
|
+
return { vault_id: this._deps.vault_id, secret_id: record.secret_id, alias: record.alias, plaintext, exported_at: this._deps.clock.nowIso() };
|
|
501
503
|
}
|
|
502
504
|
async ownerListAgents(actor) {
|
|
503
505
|
this._assertOwnerPrincipal(actor);
|
|
504
|
-
const identities = await this._deps.agentRecords.list(this._deps.
|
|
505
|
-
const
|
|
506
|
+
const identities = await this._deps.agentRecords.list(this._deps.vault_id);
|
|
507
|
+
const session_tokens = await this._deps.session_tokens.list();
|
|
506
508
|
const tokensByAgentId = new Map();
|
|
507
|
-
for (const st of
|
|
508
|
-
const list = tokensByAgentId.get(st.
|
|
509
|
+
for (const st of session_tokens) {
|
|
510
|
+
const list = tokensByAgentId.get(st.root_agent_id) ?? [];
|
|
509
511
|
list.push(st);
|
|
510
|
-
tokensByAgentId.set(st.
|
|
512
|
+
tokensByAgentId.set(st.root_agent_id, list);
|
|
511
513
|
}
|
|
512
|
-
const result = identities.map(id => ({ ...id,
|
|
513
|
-
await this._appendAudit(toAuditEntry(this._deps, actor,
|
|
514
|
+
const result = identities.map(id => ({ ...id, session_tokens: tokensByAgentId.get(id.root_agent_id) ?? [] }));
|
|
515
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditOperation.MANAGEMENT_LIST_AGENTS, "allowed", "succeeded", "agent identity list accessed"));
|
|
514
516
|
return result;
|
|
515
517
|
}
|
|
516
|
-
async ownerListRequests(actor,
|
|
518
|
+
async ownerListRequests(actor, root_agent_id) {
|
|
517
519
|
this._assertOwnerPrincipal(actor);
|
|
518
|
-
const records = await this._deps.requests.list(this._deps.
|
|
519
|
-
await this._appendAudit(toAuditEntry(this._deps, actor,
|
|
520
|
+
const records = await this._deps.requests.list(this._deps.vault_id, root_agent_id);
|
|
521
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditOperation.MANAGEMENT_LIST_REQUESTS, "allowed", "succeeded", "request list accessed"));
|
|
520
522
|
return records.map(r => this.toOwnerVisibleRequestRecord(r));
|
|
521
523
|
}
|
|
522
|
-
async ownerGetRequest(actor,
|
|
524
|
+
async ownerGetRequest(actor, request_id) {
|
|
523
525
|
this._assertOwnerPrincipal(actor);
|
|
524
|
-
const record = await this._deps.requests.get(this._deps.
|
|
526
|
+
const record = await this._deps.requests.get(this._deps.vault_id, request_id);
|
|
525
527
|
if (!record)
|
|
526
528
|
throw new VaultCoreError("request record not found", "VAULT_REQUEST_NOT_FOUND");
|
|
527
529
|
const result = this.toOwnerRequestRecord(record);
|
|
528
|
-
await this._appendAudit(toAuditEntry(this._deps, actor,
|
|
530
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditOperation.MANAGEMENT_READ_REQUEST, "allowed", "succeeded", `dispatch request detailed: "${request_id}"`, { request_id }));
|
|
529
531
|
return result;
|
|
530
532
|
}
|
|
531
533
|
async ownerListSecrets(actor) {
|
|
532
534
|
this._assertOwnerPrincipal(actor);
|
|
533
|
-
const records = await this._deps.secrets.list(this._deps.
|
|
534
|
-
await this._appendAudit(toAuditEntry(this._deps, actor,
|
|
535
|
+
const records = await this._deps.secrets.list(this._deps.vault_id);
|
|
536
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditOperation.MANAGEMENT_LIST_SECRETS, "allowed", "succeeded", "secret list accessed"));
|
|
535
537
|
return records.map(r => ({
|
|
536
|
-
|
|
537
|
-
|
|
538
|
+
vault_id: r.vault_id,
|
|
539
|
+
secret_id: r.secret_id,
|
|
538
540
|
alias: r.alias,
|
|
539
541
|
version: r.version,
|
|
540
|
-
|
|
541
|
-
|
|
542
|
+
lifecycle_status: r.lifecycle_status ?? "ACTIVE",
|
|
543
|
+
issuer_id: r.issuer_id,
|
|
542
544
|
source: r.source,
|
|
543
|
-
|
|
544
|
-
|
|
545
|
+
created_at: r.created_at,
|
|
546
|
+
updated_at: r.updated_at,
|
|
545
547
|
granted: true,
|
|
546
548
|
}));
|
|
547
549
|
}
|
|
548
550
|
async ownerIssueSessionToken(request) {
|
|
549
551
|
this._assertOwnerPrincipal(request.actor);
|
|
550
|
-
const token = await this._deps.
|
|
551
|
-
await this._appendAudit(toAuditEntry(this._deps, request.actor,
|
|
552
|
-
return { token,
|
|
552
|
+
const token = await this._deps.session_tokens.issue(request.root_agent_id);
|
|
553
|
+
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 }));
|
|
554
|
+
return { token, root_agent_id: request.root_agent_id, issued_at: this._deps.clock.nowIso() };
|
|
553
555
|
}
|
|
554
556
|
async ownerIssueAllAgentSessionTokens(actor) {
|
|
555
557
|
this._assertOwnerPrincipal(actor);
|
|
556
558
|
const agents = await this.ownerListAgents(actor);
|
|
557
|
-
return Promise.all(agents.map(a => this.ownerIssueSessionToken({
|
|
559
|
+
return Promise.all(agents.map(a => this.ownerIssueSessionToken({ vault_id: this._deps.vault_id, actor, root_agent_id: a.root_agent_id })));
|
|
558
560
|
}
|
|
559
561
|
async ownerRevokeSessionToken(request) {
|
|
560
562
|
this._assertOwnerPrincipal(request.actor);
|
|
561
|
-
await this._deps.
|
|
562
|
-
await this._appendAudit(toAuditEntry(this._deps, request.actor,
|
|
563
|
+
await this._deps.session_tokens.revoke(request.token);
|
|
564
|
+
await this._appendAudit(toAuditEntry(this._deps, request.actor, AuditOperation.IDENTITY_REVOKE_TOKEN, "allowed", "succeeded", "session token revoked"));
|
|
563
565
|
}
|
|
564
566
|
// ─── Event Observers ──────────────────────────────────────────────────────────
|
|
565
567
|
_requestObservers = [];
|
|
@@ -575,27 +577,27 @@ export class VaultCore {
|
|
|
575
577
|
return this.ownerOnPendingDispatch(callback);
|
|
576
578
|
}
|
|
577
579
|
// ─── Internal Helpers ──────────────────────────────────────────────────────────
|
|
578
|
-
async _recordRequestInternal(request, result,
|
|
580
|
+
async _recordRequestInternal(request, result, missing_grants) {
|
|
579
581
|
const record = {
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
582
|
+
vault_id: this._deps.vault_id,
|
|
583
|
+
request_id: request.request_id,
|
|
584
|
+
root_agent_id: request.agent.id,
|
|
583
585
|
reason: request.reason,
|
|
584
|
-
|
|
586
|
+
created_at: this._deps.clock.nowIso(),
|
|
585
587
|
request: {
|
|
586
|
-
|
|
588
|
+
target_url: request.target_url,
|
|
587
589
|
method: request.method,
|
|
588
590
|
headers: request.headers,
|
|
589
591
|
body: request.body,
|
|
590
|
-
|
|
592
|
+
secret_alias: request.secret_alias,
|
|
591
593
|
},
|
|
592
594
|
response: {
|
|
593
|
-
status: result.
|
|
594
|
-
body: result.
|
|
595
|
+
status: result.response_status,
|
|
596
|
+
body: result.response_body,
|
|
595
597
|
error: result.error,
|
|
596
598
|
},
|
|
597
599
|
execution: { status: result.status },
|
|
598
|
-
|
|
600
|
+
missing_grants,
|
|
599
601
|
};
|
|
600
602
|
await this._deps.requests.save(record);
|
|
601
603
|
if (result.status === DispatchStatus.PENDING) {
|
|
@@ -604,46 +606,46 @@ export class VaultCore {
|
|
|
604
606
|
}
|
|
605
607
|
toAgentVisibleRequestRecord(record) {
|
|
606
608
|
return {
|
|
607
|
-
|
|
608
|
-
|
|
609
|
+
request_id: record.request_id,
|
|
610
|
+
created_at: record.created_at,
|
|
609
611
|
reason: record.reason,
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
612
|
+
target_url: record.request.target_url,
|
|
613
|
+
execution_status: record.execution.status,
|
|
614
|
+
response_status: record.response?.status,
|
|
613
615
|
error: record.response?.error,
|
|
614
|
-
|
|
616
|
+
has_response_body: !!record.response?.body,
|
|
615
617
|
};
|
|
616
618
|
}
|
|
617
619
|
toOwnerVisibleRequestRecord(record) {
|
|
618
620
|
return {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
621
|
+
request_id: record.request_id,
|
|
622
|
+
created_at: record.created_at,
|
|
623
|
+
root_agent_id: record.root_agent_id,
|
|
622
624
|
reason: record.reason,
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
625
|
+
target_url: record.request.target_url,
|
|
626
|
+
execution_status: record.execution.status,
|
|
627
|
+
response_status: record.response?.status,
|
|
626
628
|
error: record.response?.error,
|
|
627
|
-
|
|
628
|
-
|
|
629
|
+
has_response_body: !!record.response?.body,
|
|
630
|
+
missing_grants: record.missing_grants,
|
|
629
631
|
};
|
|
630
632
|
}
|
|
631
633
|
toOwnerRequestRecord(record) {
|
|
632
634
|
return {
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
635
|
+
request_id: record.request_id,
|
|
636
|
+
created_at: record.created_at,
|
|
637
|
+
root_agent_id: record.root_agent_id,
|
|
636
638
|
reason: record.reason,
|
|
637
639
|
request: {
|
|
638
|
-
|
|
640
|
+
target_url: record.request.target_url,
|
|
639
641
|
method: record.request.method,
|
|
640
642
|
headers: record.request.headers,
|
|
641
643
|
body: record.request.body,
|
|
642
|
-
|
|
644
|
+
secret_alias: record.request.secret_alias,
|
|
643
645
|
},
|
|
644
646
|
response: record.response,
|
|
645
|
-
|
|
646
|
-
|
|
647
|
+
execution_status: record.execution.status,
|
|
648
|
+
missing_grants: record.missing_grants,
|
|
647
649
|
};
|
|
648
650
|
}
|
|
649
651
|
}
|