@the-ai-company/cbio-node-runtime 1.59.1 → 1.61.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 +7 -4
- package/dist/clients/agent/client.d.ts +6 -0
- package/dist/clients/agent/client.js.map +1 -1
- package/dist/clients/owner/client.d.ts +9 -7
- package/dist/clients/owner/client.js +46 -11
- package/dist/clients/owner/client.js.map +1 -1
- package/dist/clients/owner/contracts.d.ts +22 -11
- package/dist/clients/owner/index.d.ts +1 -1
- package/dist/runtime/index.d.ts +1 -1
- package/dist/vault-core/contracts.d.ts +87 -3
- package/dist/vault-core/contracts.js +2 -0
- package/dist/vault-core/contracts.js.map +1 -1
- package/dist/vault-core/core.d.ts +14 -1
- package/dist/vault-core/core.js +254 -24
- package/dist/vault-core/core.js.map +1 -1
- package/dist/vault-core/defaults.d.ts +1 -0
- package/dist/vault-core/defaults.js +8 -4
- package/dist/vault-core/defaults.js.map +1 -1
- package/dist/vault-core/index.d.ts +1 -1
- package/dist/vault-core/index.js.map +1 -1
- package/dist/vault-core/persistence.d.ts +1 -0
- package/dist/vault-core/persistence.js +6 -3
- package/dist/vault-core/persistence.js.map +1 -1
- package/dist/vault-core/tool-metadata.js +4 -4
- package/dist/vault-core/tool-metadata.js.map +1 -1
- package/dist/vault-ingress/index.d.ts +16 -0
- package/dist/vault-ingress/index.js +34 -2
- package/dist/vault-ingress/index.js.map +1 -1
- package/docs/REFERENCE.md +9 -5
- package/docs/api/README.md +5 -5
- package/docs/api/classes/IdentityError.md +1 -1
- package/docs/api/classes/OwnerClientError.md +1 -1
- package/docs/api/classes/VaultCore.md +97 -1
- package/docs/api/classes/VaultCoreError.md +1 -1
- package/docs/api/enumerations/IdentityErrorCode.md +1 -1
- package/docs/api/enumerations/OwnerClientErrorCode.md +1 -1
- package/docs/api/functions/createAgentClient.md +1 -1
- package/docs/api/functions/createIdentity.md +1 -1
- package/docs/api/functions/createOwnerHttpFlowBoundary.md +1 -1
- package/docs/api/functions/createOwnerSession.md +1 -1
- package/docs/api/functions/createPersistentVaultCoreDependencies.md +1 -1
- package/docs/api/functions/createStandardAcquireBoundary.md +1 -1
- package/docs/api/functions/createStandardDispatchBoundary.md +1 -1
- package/docs/api/functions/createVault.md +1 -1
- package/docs/api/functions/createVaultClient.md +1 -1
- package/docs/api/functions/createVaultCore.md +1 -1
- package/docs/api/functions/createVaultCoreDependencies.md +1 -1
- package/docs/api/functions/createVaultService.md +1 -1
- package/docs/api/functions/createWorkspaceStorage.md +1 -1
- package/docs/api/functions/deriveIdentityId.md +1 -1
- package/docs/api/functions/deriveVaultWorkingKeyFromPassword.md +1 -1
- package/docs/api/functions/getDefaultWorkspaceDir.md +1 -1
- package/docs/api/functions/handleVaultAgentControlHttp.md +1 -1
- package/docs/api/functions/handleVaultHttpDispatch.md +1 -1
- package/docs/api/functions/initializeVaultCustody.md +1 -1
- package/docs/api/functions/listVaults.md +1 -1
- package/docs/api/functions/readVaultProfile.md +1 -1
- package/docs/api/functions/recoverVault.md +1 -1
- package/docs/api/functions/recoverVaultWorkingKey.md +1 -1
- package/docs/api/functions/restoreIdentity.md +1 -1
- package/docs/api/functions/updateVaultMetadata.md +1 -1
- package/docs/api/functions/wrapVaultCoreAsVaultService.md +1 -1
- package/docs/api/functions/writeVaultProfile.md +1 -1
- package/docs/api/interfaces/AgentClient.md +7 -1
- package/docs/api/interfaces/AgentDispatchIntent.md +1 -1
- package/docs/api/interfaces/AgentDispatchTransport.md +1 -1
- package/docs/api/interfaces/AgentIdentity.md +1 -1
- package/docs/api/interfaces/AgentSigner.md +1 -1
- package/docs/api/interfaces/AgentSubmitCapabilityRequestInput.md +1 -1
- package/docs/api/interfaces/CbioRuntime.md +1 -1
- package/docs/api/interfaces/CreateAgentClientOptions.md +1 -1
- package/docs/api/interfaces/CreateIdentityOptions.md +1 -1
- package/docs/api/interfaces/CreateOwnerSessionOptions.md +1 -1
- package/docs/api/interfaces/CreatePersistentVaultCoreDependenciesOptions.md +1 -1
- package/docs/api/interfaces/CreateVaultClientOptions.md +1 -1
- package/docs/api/interfaces/CreateVaultOptions.md +1 -1
- package/docs/api/interfaces/CreatedVault.md +1 -1
- package/docs/api/interfaces/DefaultPolicyEngineOptions.md +1 -1
- package/docs/api/interfaces/IStorageProvider.md +1 -1
- package/docs/api/interfaces/InitializeVaultCustodyOptions.md +1 -1
- package/docs/api/interfaces/InitializedVaultCustody.md +1 -1
- package/docs/api/interfaces/OwnerAgentProvisionResult.md +1 -1
- package/docs/api/interfaces/{OwnerStoreSecretInput.md → OwnerCreateSecretInput.md} +2 -2
- package/docs/api/interfaces/{VaultDeleteSecretInput.md → OwnerRemoveSecretInput.md} +2 -2
- package/docs/api/interfaces/OwnerSensitiveActionConfirmation.md +1 -1
- package/docs/api/interfaces/OwnerSensitiveActionContext.md +1 -1
- package/docs/api/interfaces/OwnerSession.md +1 -1
- package/docs/api/interfaces/{OwnerWriteSecretInput.md → OwnerUpdateSecretInput.md} +2 -2
- package/docs/api/interfaces/RecoverVaultOptions.md +1 -1
- package/docs/api/interfaces/RecoveredVault.md +1 -1
- package/docs/api/interfaces/RestoreIdentityOptions.md +1 -1
- package/docs/api/interfaces/Signer.md +1 -1
- package/docs/api/interfaces/VaultApproveCapabilityRequestInput.md +7 -1
- package/docs/api/interfaces/VaultApproveDispatchInput.md +1 -1
- package/docs/api/interfaces/VaultAuditQueryInput.md +1 -1
- package/docs/api/interfaces/VaultClient.md +55 -23
- package/docs/api/interfaces/VaultCoreDependenciesOptions.md +1 -1
- package/docs/api/interfaces/VaultCreateAgentInput.md +1 -1
- package/docs/api/interfaces/VaultExportSecretInput.md +1 -1
- package/docs/api/interfaces/VaultGrantCapabilityInput.md +1 -1
- package/docs/api/interfaces/VaultGrantCapabilityRequest.md +1 -1
- package/docs/api/interfaces/VaultIdentity.md +1 -1
- package/docs/api/interfaces/VaultImportAgentInput.md +1 -1
- package/docs/api/interfaces/VaultIssueSessionTokenInput.md +1 -1
- package/docs/api/interfaces/VaultListAgentsInput.md +1 -1
- package/docs/api/interfaces/VaultListCapabilitiesInput.md +1 -1
- package/docs/api/interfaces/VaultListSecretsInput.md +1 -1
- package/docs/api/interfaces/VaultMetadata.md +1 -1
- package/docs/api/interfaces/VaultObject.md +1 -1
- package/docs/api/interfaces/VaultProfile.md +1 -1
- package/docs/api/interfaces/VaultReadAgentPrivateKeyInput.md +1 -1
- package/docs/api/interfaces/VaultReadSecretPlaintextInput.md +1 -1
- package/docs/api/interfaces/VaultRegisterFlowInput.md +1 -1
- package/docs/api/interfaces/VaultRevokeCapabilityInput.md +1 -1
- package/docs/api/interfaces/VaultRevokeSessionTokenInput.md +1 -1
- package/docs/api/interfaces/VaultSigner.md +1 -1
- package/docs/api/interfaces/VaultSubmitCapabilityRequestInput.md +1 -1
- package/docs/api/interfaces/VaultUpdateAgentInput.md +1 -1
- package/docs/api/type-aliases/AgentCapabilityEnvelope.md +1 -1
- package/docs/api/type-aliases/AgentVisibleSecretRecord.md +1 -1
- package/docs/api/type-aliases/CbioRuntimeModule.md +1 -1
- package/docs/api/type-aliases/OwnerGrantCapabilityInput.md +1 -1
- package/docs/api/variables/DEFAULT_VAULT_KEY_CUSTODY_BLOB_KEY.md +1 -1
- package/docs/zh/README.md +4 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentListCapabilitiesRequest, AgentListSecretsRequest, AgentListRequestsRequest, AgentGetRequestRequest, AgentRequestResult, AgentGetRuntimeManifestRequest, AgentRuntimeManifest, AgentSubmitCapabilityRequestCommand, AgentVisibleRequestRecord, AgentVisibleSecretRecord, AuditEntry, AuditQuery, CustomHttpFlowDefinition, DispatchAuthorization, DispatchRequest, DispatchResult, OwnerAllowAlwaysCommand, OwnerAllowOnceCommand, OwnerIssueSessionTokenRequest, OwnerDenyCommand, OwnerDeleteSecretCommand, OwnerExportSecretRequest, OwnerRegisterAgentIdentityCommand, OwnerUpdateAgentIdentityCommand, OwnerRegisterCapabilityCommand, OwnerRegisterCustomHttpFlowCommand, OwnerRevokeCapabilityCommand, OwnerListAgentsRequest, OwnerListCapabilitiesRequest, OwnerListCapabilityStatesRequest, OwnerSecretExport, OwnerSessionToken, SecretRecord, SubmitCapabilityRequestCommand, VaultId, VaultPrincipal, VaultWriteSecretCommand, AgentIdentityRecord, AgentCapability, CapabilityStateRecord } from "./contracts.js";
|
|
1
|
+
import type { AgentListCapabilitiesRequest, AgentListSecretsRequest, AgentListRequestsRequest, AgentGetRequestRequest, AgentRequestResult, AgentGetRuntimeManifestRequest, AgentRuntimeManifest, AgentSubmitCapabilityRequestCommand, AgentVisibleRequestRecord, OwnerVisibleRequestRecord, OwnerRequestRecord, AgentVisibleSecretRecord, AuditEntry, AuditQuery, CustomHttpFlowDefinition, DispatchAuthorization, DispatchRequest, DispatchResult, OwnerAllowAlwaysCommand, OwnerAllowOnceCommand, OwnerIssueSessionTokenRequest, OwnerDenyCommand, OwnerCreateSecretCommand, OwnerDeleteSecretCommand, OwnerExportSecretRequest, OwnerRegisterAgentIdentityCommand, OwnerUpdateAgentIdentityCommand, OwnerRegisterCapabilityCommand, OwnerRegisterCustomHttpFlowCommand, OwnerRevokeCapabilityCommand, OwnerListAgentsRequest, OwnerListCapabilitiesRequest, OwnerListRequestsRequest, OwnerGetRequestRequest, OwnerListCapabilityStatesRequest, OwnerSecretExport, OwnerSessionToken, SecretRecord, SubmitCapabilityRequestCommand, VaultId, VaultPrincipal, VaultWriteSecretCommand, AgentIdentityRecord, AgentCapability, CapabilityStateRecord } from "./contracts.js";
|
|
2
2
|
import type { VaultCoreDependencies } from "./ports.js";
|
|
3
3
|
/**
|
|
4
4
|
* The Sovereign Vault Core.
|
|
@@ -20,6 +20,8 @@ export declare class VaultCore {
|
|
|
20
20
|
private _listVisibleSecretsForAgent;
|
|
21
21
|
private _recordRequestExecution;
|
|
22
22
|
private toVisibleRequestRecord;
|
|
23
|
+
private toOwnerVisibleRequestRecord;
|
|
24
|
+
private toOwnerRequestRecord;
|
|
23
25
|
ownerOnCapabilityState(callback: (record: CapabilityStateRecord) => void): () => void;
|
|
24
26
|
ownerRegisterAgentIdentity(command: OwnerRegisterAgentIdentityCommand): Promise<void>;
|
|
25
27
|
ownerUpdateAgentIdentity(command: OwnerUpdateAgentIdentityCommand): Promise<AgentIdentityRecord>;
|
|
@@ -28,7 +30,12 @@ export declare class VaultCore {
|
|
|
28
30
|
_getCapability(vaultId: import("./contracts.js").VaultId, agentId: string, capabilityId: string): Promise<AgentCapability | null>;
|
|
29
31
|
ownerRegisterCustomFlow(command: OwnerRegisterCustomHttpFlowCommand): Promise<void>;
|
|
30
32
|
_storeCustomFlowSecret(flow: CustomHttpFlowDefinition, alias: string, plaintext: string): Promise<SecretRecord>;
|
|
33
|
+
private _getActiveSecretByAlias;
|
|
34
|
+
private _persistNewSecretRecord;
|
|
35
|
+
ownerCreateSecret(command: OwnerCreateSecretCommand): Promise<SecretRecord>;
|
|
36
|
+
ownerUpdateSecret(command: import("./contracts.js").OwnerUpdateSecretCommand): Promise<SecretRecord>;
|
|
31
37
|
ownerWriteSecret(command: VaultWriteSecretCommand): Promise<SecretRecord>;
|
|
38
|
+
ownerRemoveSecret(command: OwnerDeleteSecretCommand): Promise<void>;
|
|
32
39
|
ownerDeleteSecret(command: OwnerDeleteSecretCommand): Promise<void>;
|
|
33
40
|
agentAuthorizeDispatch(request: DispatchRequest): Promise<DispatchAuthorization>;
|
|
34
41
|
agentDispatchSecret(request: DispatchRequest): Promise<DispatchResult>;
|
|
@@ -45,6 +52,12 @@ export declare class VaultCore {
|
|
|
45
52
|
ownerListCapabilities(actor: VaultPrincipal & {
|
|
46
53
|
kind: "owner";
|
|
47
54
|
}, agentId?: string, request?: Omit<OwnerListCapabilitiesRequest, "actor" | "agentId" | "vaultId">): Promise<readonly AgentCapability[]>;
|
|
55
|
+
ownerListRequests(actor: VaultPrincipal & {
|
|
56
|
+
kind: "owner";
|
|
57
|
+
}, agentId?: string, request?: Omit<OwnerListRequestsRequest, "actor" | "agentId" | "vaultId">): Promise<readonly OwnerVisibleRequestRecord[]>;
|
|
58
|
+
ownerGetRequest(actor: VaultPrincipal & {
|
|
59
|
+
kind: "owner";
|
|
60
|
+
}, targetRequestId: string, request?: Omit<OwnerGetRequestRequest, "actor" | "targetRequestId" | "vaultId">): Promise<OwnerRequestRecord>;
|
|
48
61
|
ownerListSecrets(actor: VaultPrincipal & {
|
|
49
62
|
kind: "owner";
|
|
50
63
|
}, request?: {
|
package/dist/vault-core/core.js
CHANGED
|
@@ -4,6 +4,66 @@ import { verifySignature } from "../protocol/crypto.js";
|
|
|
4
4
|
import { getAgentToolbox } from "./tool-metadata.js";
|
|
5
5
|
import { InMemoryRequestRecordRegistry } from "./defaults.js";
|
|
6
6
|
const VAULT_MASTER_ID = "vault-master";
|
|
7
|
+
function redactResponseShapeValue(value) {
|
|
8
|
+
if (value === null || value === undefined) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
if (Array.isArray(value)) {
|
|
12
|
+
return value.map((entry) => redactResponseShapeValue(entry));
|
|
13
|
+
}
|
|
14
|
+
if (typeof value === "object") {
|
|
15
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, redactResponseShapeValue(entry)]));
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
function applyResponseReadPolicy(body, policy) {
|
|
20
|
+
if (body === undefined)
|
|
21
|
+
return body;
|
|
22
|
+
if (policy.mode === "full")
|
|
23
|
+
return body;
|
|
24
|
+
if (policy.mode === "none")
|
|
25
|
+
return undefined;
|
|
26
|
+
let parsed;
|
|
27
|
+
try {
|
|
28
|
+
parsed = JSON.parse(body);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return policy.mode === "shape_only" ? JSON.stringify(null) : undefined;
|
|
32
|
+
}
|
|
33
|
+
if (policy.mode === "shape_only") {
|
|
34
|
+
return JSON.stringify(redactResponseShapeValue(parsed));
|
|
35
|
+
}
|
|
36
|
+
if (policy.mode !== "custom") {
|
|
37
|
+
return body;
|
|
38
|
+
}
|
|
39
|
+
const result = {};
|
|
40
|
+
for (const path of policy.paths ?? []) {
|
|
41
|
+
const segments = path.split(".").filter(Boolean);
|
|
42
|
+
if (!segments.length)
|
|
43
|
+
continue;
|
|
44
|
+
let source = parsed;
|
|
45
|
+
let valid = true;
|
|
46
|
+
for (const segment of segments) {
|
|
47
|
+
if (source && typeof source === "object" && segment in source) {
|
|
48
|
+
source = source[segment];
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
valid = false;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (!valid)
|
|
56
|
+
continue;
|
|
57
|
+
let target = result;
|
|
58
|
+
for (let index = 0; index < segments.length - 1; index += 1) {
|
|
59
|
+
const segment = segments[index];
|
|
60
|
+
target[segment] ??= {};
|
|
61
|
+
target = target[segment];
|
|
62
|
+
}
|
|
63
|
+
target[segments[segments.length - 1]] = source;
|
|
64
|
+
}
|
|
65
|
+
return JSON.stringify(result);
|
|
66
|
+
}
|
|
7
67
|
function toAuditEntry(deps, actor, action, outcome, detail, options) {
|
|
8
68
|
return {
|
|
9
69
|
entryId: deps.ids.newAuditEntryId(),
|
|
@@ -22,22 +82,32 @@ function toAuditEntry(deps, actor, action, outcome, detail, options) {
|
|
|
22
82
|
agentId: options?.agentId,
|
|
23
83
|
};
|
|
24
84
|
}
|
|
25
|
-
function buildSecretRecord(deps, command) {
|
|
85
|
+
function buildSecretRecord(deps, command, previousRecord) {
|
|
26
86
|
const now = deps.clock.nowIso();
|
|
27
87
|
const source = command.source?.kind === "request" && command.source.requestId
|
|
28
88
|
? { kind: "request", requestId: command.source.requestId }
|
|
29
89
|
: { kind: "manual" };
|
|
90
|
+
const previousVersion = previousRecord ? Number.parseInt(previousRecord.version.value, 10) : 0;
|
|
91
|
+
const nextVersion = Number.isFinite(previousVersion) ? previousVersion + 1 : 1;
|
|
30
92
|
return {
|
|
31
93
|
vaultId: deps.vaultId,
|
|
32
94
|
secretId: deps.ids.newSecretId(),
|
|
33
95
|
alias: { value: command.alias },
|
|
34
|
-
version:
|
|
96
|
+
version: { value: String(nextVersion) },
|
|
97
|
+
lifecycleStatus: "ACTIVE",
|
|
98
|
+
previousSecretId: previousRecord?.secretId,
|
|
35
99
|
issuerId: command.kind === "issuer.write_secret" ? command.issuerSiteId : null,
|
|
36
100
|
source,
|
|
37
101
|
createdAt: now,
|
|
38
102
|
updatedAt: now,
|
|
39
103
|
};
|
|
40
104
|
}
|
|
105
|
+
function isSecretActive(record) {
|
|
106
|
+
if (record.lifecycleStatus) {
|
|
107
|
+
return record.lifecycleStatus === "ACTIVE";
|
|
108
|
+
}
|
|
109
|
+
return !record.retiredAt;
|
|
110
|
+
}
|
|
41
111
|
function normalizeScopeTarget(targetUrl) {
|
|
42
112
|
try {
|
|
43
113
|
const parsed = new URL(targetUrl);
|
|
@@ -203,6 +273,7 @@ export class VaultCore {
|
|
|
203
273
|
proof: pending.proof,
|
|
204
274
|
requestId: pending.requestId,
|
|
205
275
|
requestedAt: pending.requestedAt,
|
|
276
|
+
skipReplayGuard: true,
|
|
206
277
|
});
|
|
207
278
|
}
|
|
208
279
|
else if (mode === "grant") {
|
|
@@ -307,7 +378,10 @@ export class VaultCore {
|
|
|
307
378
|
const authorizedCapabilities = capabilityMap.get(record.secretId.value) ?? [];
|
|
308
379
|
return {
|
|
309
380
|
vaultId: record.vaultId,
|
|
381
|
+
secretId: record.secretId,
|
|
310
382
|
alias: record.alias,
|
|
383
|
+
version: record.version,
|
|
384
|
+
lifecycleStatus: record.lifecycleStatus ?? "ACTIVE",
|
|
311
385
|
issuerId: record.issuerId,
|
|
312
386
|
source: record.source,
|
|
313
387
|
createdAt: record.createdAt,
|
|
@@ -359,6 +433,52 @@ export class VaultCore {
|
|
|
359
433
|
resultVisible: readStatus === "APPROVED",
|
|
360
434
|
};
|
|
361
435
|
}
|
|
436
|
+
toOwnerVisibleRequestRecord(record, state) {
|
|
437
|
+
return {
|
|
438
|
+
requestId: record.requestId,
|
|
439
|
+
createdAt: record.createdAt,
|
|
440
|
+
agentId: record.agentId,
|
|
441
|
+
capabilityId: record.capabilityId,
|
|
442
|
+
operation: record.operation,
|
|
443
|
+
targetUrl: record.request.targetUrl,
|
|
444
|
+
method: record.request.method,
|
|
445
|
+
executionStatus: record.execution.status,
|
|
446
|
+
responseStatus: record.response?.status,
|
|
447
|
+
error: record.response?.error,
|
|
448
|
+
writeStatus: state?.actions.write.status ?? "PENDING",
|
|
449
|
+
readStatus: state?.actions.read.status ?? "PENDING",
|
|
450
|
+
hasResponseBody: typeof record.response?.body === "string" && record.response.body.length > 0,
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
toOwnerRequestRecord(record, state) {
|
|
454
|
+
return {
|
|
455
|
+
requestId: record.requestId,
|
|
456
|
+
createdAt: record.createdAt,
|
|
457
|
+
agentId: record.agentId,
|
|
458
|
+
capabilityId: record.capabilityId,
|
|
459
|
+
operation: record.operation,
|
|
460
|
+
request: {
|
|
461
|
+
targetUrl: record.request.targetUrl,
|
|
462
|
+
method: record.request.method,
|
|
463
|
+
headers: record.request.headers ? { ...record.request.headers } : undefined,
|
|
464
|
+
body: record.request.body,
|
|
465
|
+
secretId: record.request.secretId,
|
|
466
|
+
},
|
|
467
|
+
response: record.response
|
|
468
|
+
? {
|
|
469
|
+
status: record.response.status,
|
|
470
|
+
headers: record.response.headers ? { ...record.response.headers } : undefined,
|
|
471
|
+
body: record.response.body,
|
|
472
|
+
error: record.response.error,
|
|
473
|
+
}
|
|
474
|
+
: undefined,
|
|
475
|
+
actions: {
|
|
476
|
+
write: state?.actions.write ?? { action: "write", status: "PENDING" },
|
|
477
|
+
read: state?.actions.read ?? { action: "read", status: "PENDING" },
|
|
478
|
+
},
|
|
479
|
+
executionStatus: record.execution.status,
|
|
480
|
+
};
|
|
481
|
+
}
|
|
362
482
|
ownerOnCapabilityState(callback) {
|
|
363
483
|
this._capabilityStateObservers.add(callback);
|
|
364
484
|
return () => {
|
|
@@ -556,7 +676,7 @@ export class VaultCore {
|
|
|
556
676
|
throw new VaultCoreError("alias already bound to existing secret", "VAULT_WRITE_DENIED");
|
|
557
677
|
}
|
|
558
678
|
const record = buildSecretRecord(this._deps, {
|
|
559
|
-
kind: "owner.
|
|
679
|
+
kind: "owner.create_secret",
|
|
560
680
|
vaultId: this._deps.vaultId,
|
|
561
681
|
requestId,
|
|
562
682
|
owner: actor,
|
|
@@ -585,7 +705,36 @@ export class VaultCore {
|
|
|
585
705
|
}
|
|
586
706
|
return record;
|
|
587
707
|
}
|
|
588
|
-
async
|
|
708
|
+
async _getActiveSecretByAlias(alias) {
|
|
709
|
+
const matches = (await this._deps.secrets.list(this._deps.vaultId))
|
|
710
|
+
.filter((record) => record.alias.value === alias && isSecretActive(record));
|
|
711
|
+
if (matches.length > 1) {
|
|
712
|
+
throw new VaultCoreError(`multiple active secrets found for alias: ${alias}`, "VAULT_WRITE_DENIED");
|
|
713
|
+
}
|
|
714
|
+
return matches[0] ?? null;
|
|
715
|
+
}
|
|
716
|
+
async _persistNewSecretRecord(record, plaintext, actor, successDetail) {
|
|
717
|
+
try {
|
|
718
|
+
await this._deps.custody.store(record.secretId, plaintext);
|
|
719
|
+
await this._deps.secrets.save(record);
|
|
720
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditAction.WRITE_SECRET, AuditOutcome.SUCCEEDED, successDetail, {
|
|
721
|
+
secretAlias: record.alias.value,
|
|
722
|
+
secretId: record.secretId.value,
|
|
723
|
+
}));
|
|
724
|
+
}
|
|
725
|
+
catch (error) {
|
|
726
|
+
await Promise.allSettled([
|
|
727
|
+
this._deps.secrets.delete(record.secretId),
|
|
728
|
+
this._deps.custody.delete(record.secretId),
|
|
729
|
+
]);
|
|
730
|
+
throw error;
|
|
731
|
+
}
|
|
732
|
+
return record;
|
|
733
|
+
}
|
|
734
|
+
async ownerCreateSecret(command) {
|
|
735
|
+
return this.ownerWriteSecret(command);
|
|
736
|
+
}
|
|
737
|
+
async ownerUpdateSecret(command) {
|
|
589
738
|
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
590
739
|
throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
|
|
591
740
|
}
|
|
@@ -594,47 +743,89 @@ export class VaultCore {
|
|
|
594
743
|
}
|
|
595
744
|
catch (error) {
|
|
596
745
|
const detail = error instanceof Error ? error.message : String(error);
|
|
597
|
-
await this._appendAudit(toAuditEntry(this._deps, command.
|
|
746
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.WRITE_SECRET, AuditOutcome.DENIED, detail, {
|
|
598
747
|
secretAlias: command.alias,
|
|
599
748
|
}));
|
|
600
749
|
throw error;
|
|
601
750
|
}
|
|
602
|
-
const existing = await this.
|
|
603
|
-
if (existing) {
|
|
604
|
-
|
|
605
|
-
secretAlias: existing.alias.value,
|
|
606
|
-
secretId: existing.secretId.value,
|
|
607
|
-
}));
|
|
608
|
-
throw new VaultCoreError("alias already bound to existing secret", "VAULT_WRITE_DENIED");
|
|
751
|
+
const existing = await this._getActiveSecretByAlias(command.alias);
|
|
752
|
+
if (!existing) {
|
|
753
|
+
throw new VaultCoreError(`secret not found: ${command.alias}`, "VAULT_SECRET_NOT_FOUND");
|
|
609
754
|
}
|
|
610
|
-
const record = buildSecretRecord(this._deps, command);
|
|
755
|
+
const record = buildSecretRecord(this._deps, command, existing);
|
|
756
|
+
const supersededAt = this._deps.clock.nowIso();
|
|
757
|
+
const superseded = {
|
|
758
|
+
...existing,
|
|
759
|
+
lifecycleStatus: "SUPERSEDED",
|
|
760
|
+
supersededBySecretId: record.secretId,
|
|
761
|
+
supersededAt,
|
|
762
|
+
retiredAt: supersededAt,
|
|
763
|
+
updatedAt: supersededAt,
|
|
764
|
+
};
|
|
765
|
+
let custodyStored = false;
|
|
766
|
+
let previousSuperseded = false;
|
|
767
|
+
let newRecordSaved = false;
|
|
611
768
|
try {
|
|
612
769
|
await this._deps.custody.store(record.secretId, command.plaintext);
|
|
770
|
+
custodyStored = true;
|
|
771
|
+
await this._deps.secrets.save(superseded);
|
|
772
|
+
previousSuperseded = true;
|
|
613
773
|
await this._deps.secrets.save(record);
|
|
614
|
-
|
|
774
|
+
newRecordSaved = true;
|
|
775
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.WRITE_SECRET, AuditOutcome.SUCCEEDED, "secret updated", {
|
|
615
776
|
secretAlias: record.alias.value,
|
|
616
777
|
secretId: record.secretId.value,
|
|
617
778
|
}));
|
|
779
|
+
return record;
|
|
618
780
|
}
|
|
619
781
|
catch (error) {
|
|
782
|
+
if (previousSuperseded) {
|
|
783
|
+
await Promise.allSettled([this._deps.secrets.save(existing)]);
|
|
784
|
+
}
|
|
620
785
|
await Promise.allSettled([
|
|
621
|
-
this._deps.secrets.delete(record.secretId),
|
|
622
|
-
this._deps.custody.delete(record.secretId),
|
|
786
|
+
newRecordSaved ? this._deps.secrets.delete(record.secretId) : Promise.resolve(),
|
|
787
|
+
custodyStored ? this._deps.custody.delete(record.secretId) : Promise.resolve(),
|
|
623
788
|
]);
|
|
624
789
|
throw error;
|
|
625
790
|
}
|
|
626
|
-
return record;
|
|
627
791
|
}
|
|
628
|
-
async
|
|
629
|
-
|
|
792
|
+
async ownerWriteSecret(command) {
|
|
793
|
+
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
794
|
+
throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
|
|
795
|
+
}
|
|
796
|
+
try {
|
|
797
|
+
await this._deps.policy.authorizeWrite(command);
|
|
798
|
+
}
|
|
799
|
+
catch (error) {
|
|
800
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
801
|
+
await this._appendAudit(toAuditEntry(this._deps, command.kind === "issuer.write_secret" ? command.issuer : command.owner, AuditAction.WRITE_SECRET, AuditOutcome.DENIED, detail, {
|
|
802
|
+
secretAlias: command.alias,
|
|
803
|
+
}));
|
|
804
|
+
throw error;
|
|
805
|
+
}
|
|
806
|
+
const existing = await this._getActiveSecretByAlias(command.alias);
|
|
807
|
+
if (existing) {
|
|
808
|
+
await this._appendAudit(toAuditEntry(this._deps, command.kind === "issuer.write_secret" ? command.issuer : command.owner, AuditAction.REASSIGN_ALIAS, AuditOutcome.DENIED, "alias already bound to existing secret; explicit alias lifecycle required", {
|
|
809
|
+
secretAlias: existing.alias.value,
|
|
810
|
+
secretId: existing.secretId.value,
|
|
811
|
+
}));
|
|
812
|
+
throw new VaultCoreError("alias already bound to existing secret", "VAULT_WRITE_DENIED");
|
|
813
|
+
}
|
|
814
|
+
const record = buildSecretRecord(this._deps, command);
|
|
815
|
+
return this._persistNewSecretRecord(record, command.plaintext, command.kind === "issuer.write_secret" ? command.issuer : command.owner, "secret created");
|
|
816
|
+
}
|
|
817
|
+
async ownerRemoveSecret(command) {
|
|
818
|
+
const record = await this._getActiveSecretByAlias(command.alias);
|
|
630
819
|
if (!record) {
|
|
631
820
|
throw new VaultCoreError(`secret not found: ${command.alias}`, "VAULT_SECRET_NOT_FOUND");
|
|
632
821
|
}
|
|
633
|
-
const
|
|
822
|
+
const removedAt = this._deps.clock.nowIso();
|
|
634
823
|
await this._deps.secrets.save({
|
|
635
824
|
...record,
|
|
636
|
-
|
|
637
|
-
|
|
825
|
+
lifecycleStatus: "REMOVED",
|
|
826
|
+
updatedAt: removedAt,
|
|
827
|
+
removedAt,
|
|
828
|
+
retiredAt: removedAt,
|
|
638
829
|
});
|
|
639
830
|
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.DELETE_SECRET, AuditOutcome.SUCCEEDED, `retired secret ${command.alias}`, {
|
|
640
831
|
requestId: command.requestId,
|
|
@@ -642,6 +833,9 @@ export class VaultCore {
|
|
|
642
833
|
secretId: record.secretId.value,
|
|
643
834
|
}));
|
|
644
835
|
}
|
|
836
|
+
async ownerDeleteSecret(command) {
|
|
837
|
+
return this.ownerRemoveSecret(command);
|
|
838
|
+
}
|
|
645
839
|
async agentAuthorizeDispatch(request) {
|
|
646
840
|
if (request.vaultId.value !== this._deps.vaultId.value) {
|
|
647
841
|
throw new VaultCoreError("request vault mismatch", "VAULT_DISPATCH_DENIED");
|
|
@@ -659,7 +853,9 @@ export class VaultCore {
|
|
|
659
853
|
};
|
|
660
854
|
}
|
|
661
855
|
try {
|
|
662
|
-
|
|
856
|
+
if (!request.skipReplayGuard) {
|
|
857
|
+
await this._deps.replayGuard.assertNotReplayed(request);
|
|
858
|
+
}
|
|
663
859
|
await this._deps.agentProofVerifier.verify(request);
|
|
664
860
|
// Removed direct policy.authorizeDispatch here to handle discovery
|
|
665
861
|
}
|
|
@@ -885,6 +1081,28 @@ export class VaultCore {
|
|
|
885
1081
|
}));
|
|
886
1082
|
return capabilities;
|
|
887
1083
|
}
|
|
1084
|
+
async ownerListRequests(actor, agentId, request) {
|
|
1085
|
+
const records = await this._deps.requests.list(this._deps.vaultId, agentId);
|
|
1086
|
+
const states = await this._deps.capabilityStates.list(this._deps.vaultId, agentId);
|
|
1087
|
+
const stateByRequestId = new Map(states.filter((state) => state.requestId).map((state) => [state.requestId, state]));
|
|
1088
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditAction.LIST_REQUESTS, AuditOutcome.ALLOWED, "request records listed", {
|
|
1089
|
+
requestId: request?.requestId,
|
|
1090
|
+
agentId,
|
|
1091
|
+
}));
|
|
1092
|
+
return records.map((record) => this.toOwnerVisibleRequestRecord(record, stateByRequestId.get(record.requestId) ?? null));
|
|
1093
|
+
}
|
|
1094
|
+
async ownerGetRequest(actor, targetRequestId, request) {
|
|
1095
|
+
const record = await this._deps.requests.get(this._deps.vaultId, targetRequestId);
|
|
1096
|
+
if (!record) {
|
|
1097
|
+
throw new VaultCoreError("request record not found", "VAULT_READ_DENIED");
|
|
1098
|
+
}
|
|
1099
|
+
const state = await this._deps.capabilityStates.getByRequestId(this._deps.vaultId, targetRequestId);
|
|
1100
|
+
await this._appendAudit(toAuditEntry(this._deps, actor, AuditAction.READ_REQUEST, AuditOutcome.ALLOWED, "request record read", {
|
|
1101
|
+
requestId: request?.requestId,
|
|
1102
|
+
agentId: record.agentId,
|
|
1103
|
+
}));
|
|
1104
|
+
return this.toOwnerRequestRecord(record, state);
|
|
1105
|
+
}
|
|
888
1106
|
async ownerListSecrets(actor, request) {
|
|
889
1107
|
const records = await this._deps.secrets.list(this._deps.vaultId);
|
|
890
1108
|
await this._appendAudit(toAuditEntry(this._deps, actor, AuditAction.READ_AUDIT, AuditOutcome.ALLOWED, "secret metadata listed", {
|
|
@@ -892,7 +1110,10 @@ export class VaultCore {
|
|
|
892
1110
|
}));
|
|
893
1111
|
return records.map((record) => ({
|
|
894
1112
|
vaultId: record.vaultId,
|
|
1113
|
+
secretId: record.secretId,
|
|
895
1114
|
alias: record.alias,
|
|
1115
|
+
version: record.version,
|
|
1116
|
+
lifecycleStatus: record.lifecycleStatus ?? "ACTIVE",
|
|
896
1117
|
issuerId: record.issuerId,
|
|
897
1118
|
source: record.source,
|
|
898
1119
|
createdAt: record.createdAt,
|
|
@@ -934,11 +1155,14 @@ export class VaultCore {
|
|
|
934
1155
|
}
|
|
935
1156
|
const state = await this._deps.capabilityStates.getByRequestId(this._deps.vaultId, request.targetRequestId);
|
|
936
1157
|
const readApproved = state?.actions.read.status === "APPROVED";
|
|
1158
|
+
const responseBody = readApproved
|
|
1159
|
+
? applyResponseReadPolicy(record.response?.body, state?.read ?? { mode: "full" })
|
|
1160
|
+
: undefined;
|
|
937
1161
|
return {
|
|
938
1162
|
requestId: record.requestId,
|
|
939
1163
|
executionStatus: record.execution.status,
|
|
940
1164
|
responseStatus: record.response?.status,
|
|
941
|
-
responseBody
|
|
1165
|
+
responseBody,
|
|
942
1166
|
error: record.response?.error,
|
|
943
1167
|
};
|
|
944
1168
|
}
|
|
@@ -1105,6 +1329,12 @@ export class VaultCore {
|
|
|
1105
1329
|
const decidedAt = this._deps.clock.nowIso();
|
|
1106
1330
|
const next = {
|
|
1107
1331
|
...pending,
|
|
1332
|
+
read: command.read
|
|
1333
|
+
? {
|
|
1334
|
+
mode: command.read.mode,
|
|
1335
|
+
paths: command.read.paths ? [...command.read.paths] : undefined,
|
|
1336
|
+
}
|
|
1337
|
+
: pending.read,
|
|
1108
1338
|
decidedAt,
|
|
1109
1339
|
actions: {
|
|
1110
1340
|
...pending.actions,
|