@agent-native/dispatch 0.8.3 → 0.8.5
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/dist/server/lib/vault-store.d.ts +1 -0
- package/dist/server/lib/vault-store.d.ts.map +1 -1
- package/dist/server/lib/vault-store.js +47 -21
- package/dist/server/lib/vault-store.js.map +1 -1
- package/package.json +1 -1
- package/src/server/lib/vault-store.spec.ts +93 -0
- package/src/server/lib/vault-store.ts +68 -22
|
@@ -198,6 +198,7 @@ export declare function syncSecretsToCredentialStore(secrets: VaultSecretRow[],
|
|
|
198
198
|
scope: Extract<SecretScope, "org" | "workspace">;
|
|
199
199
|
scopeId: string;
|
|
200
200
|
}>;
|
|
201
|
+
export declare function cleanupSyncedCredentialKeysIfUnused(ctx: VaultCtx, candidateKeys?: string[]): Promise<void>;
|
|
201
202
|
export declare function syncGrantsToApp(appId: string, ctx?: VaultCtx): Promise<{
|
|
202
203
|
appId: string;
|
|
203
204
|
accessMode: VaultAccessMode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vault-store.d.ts","sourceRoot":"","sources":["../../../src/server/lib/vault-store.ts"],"names":[],"mappings":"AAGA,OAAO,
|
|
1
|
+
{"version":3,"file":"vault-store.d.ts","sourceRoot":"","sources":["../../../src/server/lib/vault-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
import { and, desc, eq, isNull, or } from "drizzle-orm";
|
|
3
3
|
import { discoverAgents } from "@agent-native/core/server/agent-discovery";
|
|
4
|
-
import { deleteAppSecret, writeAppSecret, } from "@agent-native/core/secrets";
|
|
4
|
+
import { deleteAppSecret, listAppSecretsForScope, writeAppSecret, } from "@agent-native/core/secrets";
|
|
5
5
|
import { getOrgSetting, getUserSetting, putOrgSetting, putUserSetting, } from "@agent-native/core/settings";
|
|
6
6
|
import { getDb, schema } from "../../db/index.js";
|
|
7
7
|
import { currentOwnerEmail, currentOrgId, recordAudit, } from "./dispatch-store.js";
|
|
8
8
|
const VAULT_ACCESS_SETTINGS_KEY = "dispatch-vault-access-settings";
|
|
9
|
+
const VAULT_SYNC_DESCRIPTION_PREFIX = "Synced from Dispatch vault:";
|
|
9
10
|
/**
|
|
10
11
|
* Build a VaultCtx from the current request. Throws if the request is
|
|
11
12
|
* unauthenticated — the previous behavior of falling back to "local@localhost"
|
|
@@ -31,6 +32,9 @@ function ctxScope(table, ctx) {
|
|
|
31
32
|
function ctxForRow(row) {
|
|
32
33
|
return { ownerEmail: row.ownerEmail, orgId: row.orgId };
|
|
33
34
|
}
|
|
35
|
+
function ctxForSecretRow(row, fallback) {
|
|
36
|
+
return row.ownerEmail ? ctxForRow(row) : fallback;
|
|
37
|
+
}
|
|
34
38
|
function id() {
|
|
35
39
|
return crypto.randomUUID();
|
|
36
40
|
}
|
|
@@ -165,8 +169,9 @@ export async function createSecret(input, ctx = requireVaultCtx()) {
|
|
|
165
169
|
summary: `Updated vault secret "${input.name}" (${credentialKey})`,
|
|
166
170
|
});
|
|
167
171
|
const updated = await getSecret(existing[0].id, ctx);
|
|
168
|
-
if (updated)
|
|
169
|
-
await syncSecretsToCredentialStore([updated], ctx);
|
|
172
|
+
if (updated) {
|
|
173
|
+
await syncSecretsToCredentialStore([updated], ctxForSecretRow(updated, ctx));
|
|
174
|
+
}
|
|
170
175
|
return updated;
|
|
171
176
|
}
|
|
172
177
|
const secretId = id();
|
|
@@ -197,8 +202,9 @@ export async function createSecret(input, ctx = requireVaultCtx()) {
|
|
|
197
202
|
summary: `Created vault secret "${input.name}" (${credentialKey})`,
|
|
198
203
|
});
|
|
199
204
|
const created = await getSecret(secretId, ctx);
|
|
200
|
-
if (created)
|
|
201
|
-
await syncSecretsToCredentialStore([created], ctx);
|
|
205
|
+
if (created) {
|
|
206
|
+
await syncSecretsToCredentialStore([created], ctxForSecretRow(created, ctx));
|
|
207
|
+
}
|
|
202
208
|
return created;
|
|
203
209
|
}
|
|
204
210
|
export async function updateSecret(secretId, input, ctx = requireVaultCtx()) {
|
|
@@ -270,22 +276,16 @@ export async function updateSecret(secretId, input, ctx = requireVaultCtx()) {
|
|
|
270
276
|
metadata: auditMetadata,
|
|
271
277
|
});
|
|
272
278
|
const updated = await getSecret(secretId, ctx);
|
|
273
|
-
if (updated)
|
|
274
|
-
await syncSecretsToCredentialStore([updated], ctx);
|
|
279
|
+
if (updated) {
|
|
280
|
+
await syncSecretsToCredentialStore([updated], ctxForSecretRow(updated, ctx));
|
|
281
|
+
}
|
|
275
282
|
if (updated && credentialKey !== existing.credentialKey) {
|
|
276
|
-
|
|
277
|
-
.
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const target = credentialStoreScopeForVaultCtx(ctx);
|
|
283
|
-
await deleteAppSecret({
|
|
284
|
-
key: existing.credentialKey,
|
|
285
|
-
scope: target.scope,
|
|
286
|
-
scopeId: target.scopeId,
|
|
287
|
-
});
|
|
288
|
-
}
|
|
283
|
+
await cleanupSyncedCredentialKeysIfUnused(ctxForRow(existing), [
|
|
284
|
+
existing.credentialKey,
|
|
285
|
+
]);
|
|
286
|
+
}
|
|
287
|
+
else if (updated && patch.credentialKey !== undefined) {
|
|
288
|
+
await cleanupSyncedCredentialKeysIfUnused(ctxForSecretRow(updated, ctx));
|
|
289
289
|
}
|
|
290
290
|
return updated;
|
|
291
291
|
}
|
|
@@ -304,6 +304,9 @@ export async function deleteSecret(secretId, ctx = requireVaultCtx()) {
|
|
|
304
304
|
await db
|
|
305
305
|
.delete(schema.vaultSecrets)
|
|
306
306
|
.where(and(eq(schema.vaultSecrets.id, secretId), ctxScope(schema.vaultSecrets, ctx)));
|
|
307
|
+
await cleanupSyncedCredentialKeysIfUnused(ctxForRow(existing), [
|
|
308
|
+
existing.credentialKey,
|
|
309
|
+
]);
|
|
307
310
|
await recordVaultAudit({
|
|
308
311
|
action: "secret.deleted",
|
|
309
312
|
secretId,
|
|
@@ -446,12 +449,35 @@ export async function syncSecretsToCredentialStore(secrets, ctx) {
|
|
|
446
449
|
value: secret.value,
|
|
447
450
|
scope: target.scope,
|
|
448
451
|
scopeId: target.scopeId,
|
|
449
|
-
description:
|
|
452
|
+
description: `${VAULT_SYNC_DESCRIPTION_PREFIX} ${secret.name}`,
|
|
450
453
|
});
|
|
451
454
|
syncedKeys.push(secret.credentialKey);
|
|
452
455
|
}
|
|
453
456
|
return { ...target, keys: syncedKeys };
|
|
454
457
|
}
|
|
458
|
+
export async function cleanupSyncedCredentialKeysIfUnused(ctx, candidateKeys) {
|
|
459
|
+
const db = getDb();
|
|
460
|
+
const target = credentialStoreScopeForVaultCtx(ctx);
|
|
461
|
+
const keys = candidateKeys
|
|
462
|
+
? candidateKeys
|
|
463
|
+
: (await listAppSecretsForScope(target.scope, target.scopeId))
|
|
464
|
+
.filter((secret) => secret.description?.startsWith(VAULT_SYNC_DESCRIPTION_PREFIX))
|
|
465
|
+
.map((secret) => secret.key);
|
|
466
|
+
for (const key of new Set(keys.filter(Boolean))) {
|
|
467
|
+
const stillUsesKey = await db
|
|
468
|
+
.select({ id: schema.vaultSecrets.id })
|
|
469
|
+
.from(schema.vaultSecrets)
|
|
470
|
+
.where(and(eq(schema.vaultSecrets.credentialKey, key), ctxScope(schema.vaultSecrets, ctx)))
|
|
471
|
+
.limit(1);
|
|
472
|
+
if (!stillUsesKey[0]) {
|
|
473
|
+
await deleteAppSecret({
|
|
474
|
+
key,
|
|
475
|
+
scope: target.scope,
|
|
476
|
+
scopeId: target.scopeId,
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
455
481
|
// ─── Sync ──────────────────────────────────────────────────────
|
|
456
482
|
export async function syncGrantsToApp(appId, ctx = requireVaultCtx()) {
|
|
457
483
|
const db = getDb();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vault-store.js","sourceRoot":"","sources":["../../../src/server/lib/vault-store.ts"],"names":[],"mappings":"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","sourcesContent":["import crypto from \"node:crypto\";\nimport { and, desc, eq, isNull, or } from \"drizzle-orm\";\nimport { discoverAgents } from \"@agent-native/core/server/agent-discovery\";\nimport {\n deleteAppSecret,\n writeAppSecret,\n type SecretScope,\n} from \"@agent-native/core/secrets\";\nimport {\n getOrgSetting,\n getUserSetting,\n putOrgSetting,\n putUserSetting,\n} from \"@agent-native/core/settings\";\nimport { getDb, schema } from \"../../db/index.js\";\nimport {\n currentOwnerEmail,\n currentOrgId,\n recordAudit,\n} from \"./dispatch-store.js\";\n\nconst VAULT_ACCESS_SETTINGS_KEY = \"dispatch-vault-access-settings\";\n\nexport type VaultAccessMode = \"all-apps\" | \"manual\";\n\nexport interface VaultAccessSettings {\n mode: VaultAccessMode;\n scope: \"org\" | \"user\";\n scopeId: string;\n}\n\n/**\n * Caller-supplied access context for vault operations.\n *\n * Every getSecret / updateSecret / deleteSecret / createGrant call must\n * pass the ctx of the *current request* so the row is scoped to that\n * caller's tenant. Looking up a vault secret by id alone is unsafe — UUIDs\n * are not authorization. A row matches the ctx if either the caller owns\n * it or it lives in the caller's active org.\n */\nexport interface VaultCtx {\n ownerEmail: string;\n orgId: string | null;\n}\n\n/**\n * Build a VaultCtx from the current request. Throws if the request is\n * unauthenticated — the previous behavior of falling back to \"local@localhost\"\n * leaked rows across tenants when a misconfigured environment skipped auth.\n */\nexport function requireVaultCtx(): VaultCtx {\n const ownerEmail = currentOwnerEmail();\n if (!ownerEmail) {\n throw new Error(\"Vault operation requires an authenticated user\");\n }\n return { ownerEmail, orgId: currentOrgId() };\n}\n\n/** WHERE clause that limits a vault row to the caller's ownership scope. */\nfunction ctxScope<T extends { ownerEmail: any; orgId: any }>(\n table: T,\n ctx: VaultCtx,\n) {\n if (!ctx.orgId) {\n return and(eq(table.ownerEmail, ctx.ownerEmail), isNull(table.orgId));\n }\n return or(eq(table.ownerEmail, ctx.ownerEmail), eq(table.orgId, ctx.orgId));\n}\n\n/** Build a ctx that scopes to a specific row's owner/org (used when a\n * request approver acts on behalf of the original requester so the\n * created secret lands in the request's org). */\nfunction ctxForRow(row: {\n ownerEmail: string;\n orgId: string | null;\n}): VaultCtx {\n return { ownerEmail: row.ownerEmail, orgId: row.orgId };\n}\n\nfunction id() {\n return crypto.randomUUID();\n}\n\nfunction now() {\n return Date.now();\n}\n\nfunction safeJson(value: unknown) {\n return JSON.stringify(value ?? null);\n}\n\nfunction scopedFilter<T extends { ownerEmail: any; orgId: any }>(table: T) {\n return ctxScope(table, requireVaultCtx());\n}\n\nfunction normalizeCredentialKey(value: string) {\n return value.trim();\n}\n\nfunction vaultAccessScope() {\n const orgId = currentOrgId();\n if (orgId) return { scope: \"org\" as const, scopeId: orgId };\n return { scope: \"user\" as const, scopeId: currentOwnerEmail() };\n}\n\nfunction parseVaultAccessMode(value: unknown): VaultAccessMode {\n return value === \"manual\" ? \"manual\" : \"all-apps\";\n}\n\nexport async function getVaultAccessSettings(): Promise<VaultAccessSettings> {\n const scope = vaultAccessScope();\n const raw =\n scope.scope === \"org\"\n ? await getOrgSetting(scope.scopeId, VAULT_ACCESS_SETTINGS_KEY)\n : await getUserSetting(scope.scopeId, VAULT_ACCESS_SETTINGS_KEY);\n return {\n ...scope,\n mode: parseVaultAccessMode(raw?.mode),\n };\n}\n\nexport async function setVaultAccessSettings(input: {\n mode: VaultAccessMode;\n}): Promise<VaultAccessSettings> {\n const scope = vaultAccessScope();\n const next = { mode: parseVaultAccessMode(input.mode) };\n if (scope.scope === \"org\") {\n await putOrgSetting(scope.scopeId, VAULT_ACCESS_SETTINGS_KEY, next);\n } else {\n await putUserSetting(scope.scopeId, VAULT_ACCESS_SETTINGS_KEY, next);\n }\n await recordAudit({\n action: \"vault.access-settings.updated\",\n targetType: \"vault-settings\",\n targetId: VAULT_ACCESS_SETTINGS_KEY,\n summary:\n next.mode === \"all-apps\"\n ? \"Set vault access to all workspace apps\"\n : \"Set vault access to manual per-app grants\",\n metadata: next,\n });\n return getVaultAccessSettings();\n}\n\n// ─── Vault Audit ──────────────────────────────────────────────────\n\nexport async function recordVaultAudit(input: {\n action: string;\n secretId?: string | null;\n appId?: string | null;\n summary: string;\n metadata?: unknown;\n actor?: string;\n}) {\n const db = getDb();\n await db.insert(schema.vaultAuditLog).values({\n id: id(),\n ownerEmail: currentOwnerEmail(),\n orgId: currentOrgId(),\n secretId: input.secretId || null,\n appId: input.appId || null,\n action: input.action,\n actor: input.actor || currentOwnerEmail(),\n summary: input.summary,\n metadata: input.metadata ? safeJson(input.metadata) : null,\n createdAt: now(),\n });\n}\n\nexport async function listVaultAudit(limit = 50) {\n const db = getDb();\n return db\n .select()\n .from(schema.vaultAuditLog)\n .where(scopedFilter(schema.vaultAuditLog))\n .orderBy(desc(schema.vaultAuditLog.createdAt))\n .limit(limit);\n}\n\n// ─── Secrets ──────────────────────────────────────────────────────\n\nexport async function listSecrets() {\n const db = getDb();\n return db\n .select()\n .from(schema.vaultSecrets)\n .where(scopedFilter(schema.vaultSecrets))\n .orderBy(desc(schema.vaultSecrets.updatedAt));\n}\n\nexport async function getSecret(secretId: string, ctx: VaultCtx) {\n const db = getDb();\n const [row] = await db\n .select()\n .from(schema.vaultSecrets)\n .where(\n and(\n eq(schema.vaultSecrets.id, secretId),\n ctxScope(schema.vaultSecrets, ctx),\n ),\n )\n .limit(1);\n return row ?? null;\n}\n\nexport async function createSecret(\n input: {\n credentialKey: string;\n value: string;\n name: string;\n provider?: string | null;\n description?: string | null;\n },\n ctx: VaultCtx = requireVaultCtx(),\n) {\n const db = getDb();\n const timestamp = now();\n const credentialKey = normalizeCredentialKey(input.credentialKey);\n if (!credentialKey) throw new Error(\"Credential key is required\");\n const existing = await db\n .select()\n .from(schema.vaultSecrets)\n .where(\n and(\n eq(schema.vaultSecrets.credentialKey, credentialKey),\n ctxScope(schema.vaultSecrets, ctx),\n ),\n )\n .orderBy(desc(schema.vaultSecrets.updatedAt))\n .limit(1);\n\n if (existing[0]) {\n await db\n .update(schema.vaultSecrets)\n .set({\n name: input.name,\n credentialKey,\n value: input.value,\n provider: input.provider || null,\n description: input.description || null,\n updatedAt: timestamp,\n })\n .where(\n and(\n eq(schema.vaultSecrets.id, existing[0].id),\n ctxScope(schema.vaultSecrets, ctx),\n ),\n );\n\n await recordVaultAudit({\n action: \"secret.updated\",\n secretId: existing[0].id,\n summary: `Updated secret \"${input.name}\" (${credentialKey})`,\n metadata: { credentialKey, provider: input.provider },\n });\n\n await recordAudit({\n action: \"vault.secret.updated\",\n targetType: \"vault-secret\",\n targetId: existing[0].id,\n summary: `Updated vault secret \"${input.name}\" (${credentialKey})`,\n });\n\n const updated = await getSecret(existing[0].id, ctx);\n if (updated) await syncSecretsToCredentialStore([updated], ctx);\n return updated;\n }\n\n const secretId = id();\n const actor = ctx.ownerEmail;\n\n await db.insert(schema.vaultSecrets).values({\n id: secretId,\n ownerEmail: actor,\n orgId: ctx.orgId,\n name: input.name,\n credentialKey,\n value: input.value,\n provider: input.provider || null,\n description: input.description || null,\n createdBy: actor,\n createdAt: timestamp,\n updatedAt: timestamp,\n });\n\n await recordVaultAudit({\n action: \"secret.created\",\n secretId,\n summary: `Created secret \"${input.name}\" (${credentialKey})`,\n metadata: { credentialKey, provider: input.provider },\n });\n\n await recordAudit({\n action: \"vault.secret.created\",\n targetType: \"vault-secret\",\n targetId: secretId,\n summary: `Created vault secret \"${input.name}\" (${credentialKey})`,\n });\n\n const created = await getSecret(secretId, ctx);\n if (created) await syncSecretsToCredentialStore([created], ctx);\n return created;\n}\n\nexport async function updateSecret(\n secretId: string,\n input:\n | string\n | {\n credentialKey?: string;\n value?: string;\n name?: string;\n provider?: string | null;\n description?: string | null;\n },\n ctx: VaultCtx = requireVaultCtx(),\n) {\n const db = getDb();\n const existing = await getSecret(secretId, ctx);\n if (!existing) throw new Error(\"Secret not found\");\n const patch = typeof input === \"string\" ? { value: input } : input;\n const credentialKey =\n patch.credentialKey !== undefined\n ? normalizeCredentialKey(patch.credentialKey)\n : existing.credentialKey;\n if (!credentialKey) throw new Error(\"Credential key is required\");\n const name = patch.name !== undefined ? patch.name.trim() : existing.name;\n if (!name) throw new Error(\"Secret name is required\");\n const value = patch.value !== undefined ? patch.value : existing.value;\n if (!value) throw new Error(\"Secret value is required\");\n const provider =\n patch.provider !== undefined ? patch.provider || null : existing.provider;\n const description =\n patch.description !== undefined\n ? patch.description || null\n : existing.description;\n\n if (credentialKey !== existing.credentialKey) {\n const conflict = await db\n .select({ id: schema.vaultSecrets.id })\n .from(schema.vaultSecrets)\n .where(\n and(\n eq(schema.vaultSecrets.credentialKey, credentialKey),\n ctxScope(schema.vaultSecrets, ctx),\n ),\n )\n .limit(1);\n if (conflict[0] && conflict[0].id !== secretId) {\n throw new Error(`Credential key \"${credentialKey}\" is already in use`);\n }\n }\n\n await db\n .update(schema.vaultSecrets)\n .set({\n name,\n credentialKey,\n value,\n provider,\n description,\n updatedAt: now(),\n })\n .where(\n and(\n eq(schema.vaultSecrets.id, secretId),\n ctxScope(schema.vaultSecrets, ctx),\n ),\n );\n\n const auditMetadata = {\n name,\n previousName: name !== existing.name ? existing.name : undefined,\n credentialKey,\n previousCredentialKey:\n credentialKey !== existing.credentialKey\n ? existing.credentialKey\n : undefined,\n provider,\n previousProvider:\n provider !== existing.provider ? existing.provider : undefined,\n description,\n previousDescription:\n description !== existing.description ? existing.description : undefined,\n valueChanged: value !== existing.value ? true : undefined,\n };\n\n await recordVaultAudit({\n action: \"secret.updated\",\n secretId,\n summary: `Updated secret \"${name}\" (${credentialKey})`,\n metadata: auditMetadata,\n });\n\n await recordAudit({\n action: \"vault.secret.updated\",\n targetType: \"vault-secret\",\n targetId: secretId,\n summary: `Updated vault secret \"${name}\" (${credentialKey})`,\n metadata: auditMetadata,\n });\n\n const updated = await getSecret(secretId, ctx);\n if (updated) await syncSecretsToCredentialStore([updated], ctx);\n if (updated && credentialKey !== existing.credentialKey) {\n const stillUsesOldKey = await db\n .select({ id: schema.vaultSecrets.id })\n .from(schema.vaultSecrets)\n .where(\n and(\n eq(schema.vaultSecrets.credentialKey, existing.credentialKey),\n ctxScope(schema.vaultSecrets, ctx),\n ),\n )\n .limit(1);\n if (!stillUsesOldKey[0]) {\n const target = credentialStoreScopeForVaultCtx(ctx);\n await deleteAppSecret({\n key: existing.credentialKey,\n scope: target.scope,\n scopeId: target.scopeId,\n });\n }\n }\n return updated;\n}\n\nexport async function deleteSecret(\n secretId: string,\n ctx: VaultCtx = requireVaultCtx(),\n) {\n const db = getDb();\n const existing = await getSecret(secretId, ctx);\n if (!existing) throw new Error(\"Secret not found\");\n\n // Revoke all active grants first\n const grants = await listGrants({ secretId });\n for (const grant of grants) {\n if (grant.status === \"active\") {\n await revokeGrant(grant.id, ctx);\n }\n }\n\n await db\n .delete(schema.vaultSecrets)\n .where(\n and(\n eq(schema.vaultSecrets.id, secretId),\n ctxScope(schema.vaultSecrets, ctx),\n ),\n );\n\n await recordVaultAudit({\n action: \"secret.deleted\",\n secretId,\n summary: `Deleted secret \"${existing.name}\" (${existing.credentialKey})`,\n });\n\n await recordAudit({\n action: \"vault.secret.deleted\",\n targetType: \"vault-secret\",\n targetId: secretId,\n summary: `Deleted vault secret \"${existing.name}\" (${existing.credentialKey})`,\n });\n\n return existing;\n}\n\n// ─── Grants ──────────────────────────────────────────────────────\n\nexport async function listGrants(filter?: {\n secretId?: string;\n appId?: string;\n}) {\n const db = getDb();\n const conditions = [scopedFilter(schema.vaultGrants)];\n if (filter?.secretId) {\n conditions.push(eq(schema.vaultGrants.secretId, filter.secretId) as any);\n }\n if (filter?.appId) {\n conditions.push(eq(schema.vaultGrants.appId, filter.appId) as any);\n }\n return db\n .select()\n .from(schema.vaultGrants)\n .where(and(...conditions))\n .orderBy(desc(schema.vaultGrants.updatedAt));\n}\n\nexport async function getGrant(\n grantId: string,\n ctx: VaultCtx = requireVaultCtx(),\n) {\n const db = getDb();\n const [row] = await db\n .select()\n .from(schema.vaultGrants)\n .where(\n and(\n eq(schema.vaultGrants.id, grantId),\n ctxScope(schema.vaultGrants, ctx),\n ),\n )\n .limit(1);\n return row ?? null;\n}\n\nexport async function createGrant(\n secretId: string,\n appId: string,\n ctx: VaultCtx = requireVaultCtx(),\n) {\n const db = getDb();\n const secret = await getSecret(secretId, ctx);\n if (!secret) throw new Error(\"Secret not found\");\n\n const timestamp = now();\n const grantId = id();\n const actor = ctx.ownerEmail;\n\n await db.insert(schema.vaultGrants).values({\n id: grantId,\n ownerEmail: actor,\n orgId: ctx.orgId,\n secretId,\n appId,\n grantedBy: actor,\n status: \"active\",\n syncedAt: null,\n createdAt: timestamp,\n updatedAt: timestamp,\n });\n\n await recordVaultAudit({\n action: \"grant.created\",\n secretId,\n appId,\n summary: `Granted \"${secret.name}\" (${secret.credentialKey}) to ${appId}`,\n metadata: { grantId },\n });\n\n await recordAudit({\n action: \"vault.grant.created\",\n targetType: \"vault-grant\",\n targetId: grantId,\n summary: `Granted vault secret \"${secret.name}\" to ${appId}`,\n });\n\n return getGrant(grantId);\n}\n\nexport async function grantSecretsToApp(\n secretIds: string[],\n appId: string,\n ctx: VaultCtx = requireVaultCtx(),\n) {\n const access = await getVaultAccessSettings();\n const uniqueSecretIds = Array.from(new Set(secretIds));\n if (access.mode === \"all-apps\") {\n return {\n appId,\n accessMode: access.mode,\n created: [],\n skipped: uniqueSecretIds,\n };\n }\n const existingActive = (await listGrants({ appId })).filter(\n (grant) => grant.status === \"active\",\n );\n const existingSecretIds = new Set(\n existingActive.map((grant) => grant.secretId),\n );\n const created = [];\n const skipped: string[] = [];\n\n for (const secretId of uniqueSecretIds) {\n if (existingSecretIds.has(secretId)) {\n skipped.push(secretId);\n continue;\n }\n const grant = await createGrant(secretId, appId, ctx);\n if (grant) {\n created.push(grant);\n existingSecretIds.add(secretId);\n }\n }\n\n return { appId, accessMode: access.mode, created, skipped };\n}\n\nexport async function revokeGrant(\n grantId: string,\n ctx: VaultCtx = requireVaultCtx(),\n) {\n const db = getDb();\n const grant = await getGrant(grantId, ctx);\n if (!grant) throw new Error(\"Grant not found\");\n\n const secret = await getSecret(grant.secretId, ctx);\n\n await db\n .update(schema.vaultGrants)\n .set({ status: \"revoked\", updatedAt: now() })\n .where(\n and(\n eq(schema.vaultGrants.id, grantId),\n ctxScope(schema.vaultGrants, ctx),\n ),\n );\n\n await recordVaultAudit({\n action: \"grant.revoked\",\n secretId: grant.secretId,\n appId: grant.appId,\n summary: `Revoked ${secret?.credentialKey || grant.secretId} from ${grant.appId}`,\n metadata: { grantId },\n });\n\n await recordAudit({\n action: \"vault.grant.revoked\",\n targetType: \"vault-grant\",\n targetId: grantId,\n summary: `Revoked vault secret \"${secret?.name || grant.secretId}\" from ${grant.appId}`,\n });\n\n return getGrant(grantId, ctx);\n}\n\n// ─── Shared Credential Store Sync ─────────────────────────────────\n\ntype VaultSecretRow = typeof schema.vaultSecrets.$inferSelect;\n\nexport function credentialStoreScopeForVaultCtx(ctx: VaultCtx): {\n scope: Extract<SecretScope, \"org\" | \"workspace\">;\n scopeId: string;\n} {\n if (ctx.orgId) return { scope: \"org\", scopeId: ctx.orgId };\n return { scope: \"workspace\", scopeId: `solo:${ctx.ownerEmail}` };\n}\n\nexport async function syncSecretsToCredentialStore(\n secrets: VaultSecretRow[],\n ctx: VaultCtx,\n) {\n const target = credentialStoreScopeForVaultCtx(ctx);\n const syncedKeys: string[] = [];\n\n for (const secret of secrets) {\n if (!secret.credentialKey || !secret.value) continue;\n await writeAppSecret({\n key: secret.credentialKey,\n value: secret.value,\n scope: target.scope,\n scopeId: target.scopeId,\n description: `Synced from Dispatch vault: ${secret.name}`,\n });\n syncedKeys.push(secret.credentialKey);\n }\n\n return { ...target, keys: syncedKeys };\n}\n\n// ─── Sync ──────────────────────────────────────────────────────\n\nexport async function syncGrantsToApp(\n appId: string,\n ctx: VaultCtx = requireVaultCtx(),\n) {\n const db = getDb();\n const access = await getVaultAccessSettings();\n const agents = await discoverAgents(\"dispatch\");\n const agent = agents.find((a) => a.id === appId);\n if (!agent) throw new Error(`App \"${appId}\" not found in agent registry`);\n\n const secretsToSync: VaultSecretRow[] = [];\n const activeGrants =\n access.mode === \"manual\"\n ? (await listGrants({ appId })).filter((g) => g.status === \"active\")\n : [];\n\n if (access.mode === \"all-apps\") {\n const secrets = await listSecrets();\n for (const secret of secrets) {\n secretsToSync.push(secret);\n }\n } else {\n for (const grant of activeGrants) {\n const secret = await getSecret(grant.secretId, ctx);\n if (secret) {\n secretsToSync.push(secret);\n }\n }\n }\n\n if (secretsToSync.length === 0) {\n return { appId, accessMode: access.mode, synced: 0, keys: [] };\n }\n\n const credentialStoreSync = await syncSecretsToCredentialStore(\n secretsToSync,\n ctx,\n );\n const vars = secretsToSync.map((secret) => ({\n key: secret.credentialKey,\n value: secret.value,\n }));\n let envVarSync:\n | { status: \"synced\"; keys: string[] }\n | { status: \"skipped\"; reason: string }\n | { status: \"failed\"; reason: string };\n\n // Best-effort push to the app's env-vars endpoint for local/dev apps that\n // still read process.env directly. Production/shared-DB apps intentionally\n // reject env writes; the encrypted app_secrets sync above is the canonical\n // path for request-scoped credentials.\n try {\n const res = await fetch(`${agent.url}/_agent-native/env-vars`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ vars }),\n });\n\n if (res.ok) {\n const result = await res.json();\n envVarSync = { status: \"synced\", keys: result.saved || [] };\n } else {\n const err = await res.text().catch(() => \"Unknown error\");\n envVarSync = { status: \"skipped\", reason: err };\n }\n } catch (err) {\n envVarSync = {\n status: \"failed\",\n reason: err instanceof Error ? err.message : String(err),\n };\n }\n\n const syncedKeys = credentialStoreSync.keys;\n const timestamp = now();\n\n // Update syncedAt on grants that were successfully pushed to the shared\n // credential store. All-apps mode has no explicit grant rows to update.\n for (const grant of activeGrants) {\n const secret = await getSecret(grant.secretId, ctx);\n if (secret && syncedKeys.includes(secret.credentialKey)) {\n await db\n .update(schema.vaultGrants)\n .set({ syncedAt: timestamp, updatedAt: timestamp })\n .where(eq(schema.vaultGrants.id, grant.id));\n }\n }\n\n await recordVaultAudit({\n action: \"secret.synced\",\n appId,\n summary: `Synced ${syncedKeys.length} secret(s) to ${appId}: ${syncedKeys.join(\", \")}`,\n metadata: {\n syncedKeys,\n accessMode: access.mode,\n credentialStore: {\n scope: credentialStoreSync.scope,\n scopeId: credentialStoreSync.scopeId,\n },\n envVars: envVarSync,\n },\n });\n\n return {\n appId,\n accessMode: access.mode,\n synced: syncedKeys.length,\n keys: syncedKeys,\n credentialStore: {\n scope: credentialStoreSync.scope,\n scopeId: credentialStoreSync.scopeId,\n synced: credentialStoreSync.keys.length,\n },\n envVars: envVarSync,\n };\n}\n\n// ─── Requests ──────────────────────────────────────────────────────\n\nexport async function listRequests(filter?: { status?: string }) {\n const db = getDb();\n const conditions = [scopedFilter(schema.vaultRequests)];\n if (filter?.status) {\n conditions.push(eq(schema.vaultRequests.status, filter.status) as any);\n }\n return db\n .select()\n .from(schema.vaultRequests)\n .where(and(...conditions))\n .orderBy(desc(schema.vaultRequests.updatedAt));\n}\n\nexport async function getRequest(\n requestId: string,\n ctx: VaultCtx = requireVaultCtx(),\n) {\n const db = getDb();\n const [row] = await db\n .select()\n .from(schema.vaultRequests)\n .where(\n and(\n eq(schema.vaultRequests.id, requestId),\n ctxScope(schema.vaultRequests, ctx),\n ),\n )\n .limit(1);\n return row ?? null;\n}\n\nexport async function createRequest(input: {\n credentialKey: string;\n appId: string;\n reason?: string | null;\n}) {\n const db = getDb();\n const timestamp = now();\n const requestId = id();\n const actor = currentOwnerEmail();\n\n await db.insert(schema.vaultRequests).values({\n id: requestId,\n ownerEmail: actor,\n orgId: currentOrgId(),\n credentialKey: input.credentialKey,\n appId: input.appId,\n reason: input.reason || null,\n requestedBy: actor,\n status: \"pending\",\n reviewedBy: null,\n reviewedAt: null,\n createdAt: timestamp,\n updatedAt: timestamp,\n });\n\n await recordVaultAudit({\n action: \"request.created\",\n appId: input.appId,\n summary: `${actor} requested ${input.credentialKey} for ${input.appId}`,\n metadata: { requestId, reason: input.reason },\n });\n\n await notifyAdminsOfRequest(requestId, input);\n\n return getRequest(requestId);\n}\n\nexport async function approveRequest(\n requestId: string,\n secretValue: string,\n secretName?: string,\n ctx: VaultCtx = requireVaultCtx(),\n) {\n const db = getDb();\n const request = await getRequest(requestId, ctx);\n if (!request) throw new Error(\"Request not found\");\n if (request.status !== \"pending\") {\n throw new Error(\"Only pending requests can be approved\");\n }\n\n const timestamp = now();\n const reviewer = ctx.ownerEmail;\n\n // Update request status — scoped to caller's tenant.\n await db\n .update(schema.vaultRequests)\n .set({\n status: \"approved\",\n reviewedBy: reviewer,\n reviewedAt: timestamp,\n updatedAt: timestamp,\n })\n .where(\n and(\n eq(schema.vaultRequests.id, requestId),\n ctxScope(schema.vaultRequests, ctx),\n ),\n );\n\n // Secret + grant must land in the REQUEST's tenant, not the approver's\n // (the approver may be acting on behalf of another user in the same org).\n const requestCtx = ctxForRow(request);\n\n // Check if secret already exists in the request's tenant for this key.\n const existingSecrets = await db\n .select()\n .from(schema.vaultSecrets)\n .where(\n and(\n eq(schema.vaultSecrets.credentialKey, request.credentialKey),\n ctxScope(schema.vaultSecrets, requestCtx),\n ),\n );\n let secret = existingSecrets[0] ?? null;\n\n if (!secret) {\n secret = await createSecret(\n {\n credentialKey: request.credentialKey,\n value: secretValue,\n name: secretName || request.credentialKey,\n },\n requestCtx,\n );\n }\n\n if (secret) {\n // Create the grant in the request's tenant as well.\n await createGrant(secret.id, request.appId, requestCtx);\n }\n\n await recordVaultAudit({\n action: \"request.approved\",\n appId: request.appId,\n summary: `Approved ${request.credentialKey} for ${request.appId} (requested by ${request.requestedBy})`,\n metadata: { requestId, reviewer },\n });\n\n return getRequest(requestId, ctx);\n}\n\nexport async function denyRequest(\n requestId: string,\n reason?: string | null,\n ctx: VaultCtx = requireVaultCtx(),\n) {\n const db = getDb();\n const request = await getRequest(requestId, ctx);\n if (!request) throw new Error(\"Request not found\");\n if (request.status !== \"pending\") {\n throw new Error(\"Only pending requests can be denied\");\n }\n\n const timestamp = now();\n const reviewer = ctx.ownerEmail;\n\n await db\n .update(schema.vaultRequests)\n .set({\n status: \"denied\",\n reviewedBy: reviewer,\n reviewedAt: timestamp,\n updatedAt: timestamp,\n })\n .where(\n and(\n eq(schema.vaultRequests.id, requestId),\n ctxScope(schema.vaultRequests, ctx),\n ),\n );\n\n await recordVaultAudit({\n action: \"request.denied\",\n appId: request.appId,\n summary: `Denied ${request.credentialKey} for ${request.appId} (requested by ${request.requestedBy})`,\n metadata: { requestId, reviewer, reason },\n });\n\n return getRequest(requestId, ctx);\n}\n\n// ─── Integrations Catalog ────────────────────────────────────────\n\nexport interface IntegrationEntry {\n key: string;\n label: string;\n required: boolean;\n configured: boolean;\n vaultGranted: boolean;\n vaultSecretId?: string;\n}\n\nexport interface AppIntegrations {\n appId: string;\n appName: string;\n url: string;\n color: string;\n integrations: IntegrationEntry[];\n vaultAccessMode: VaultAccessMode;\n reachable: boolean;\n}\n\nexport async function listIntegrationsCatalog(): Promise<AppIntegrations[]> {\n const access = await getVaultAccessSettings();\n const agents = await discoverAgents(\"dispatch\");\n const grants = await listGrants();\n const secrets = await listSecrets();\n\n const secretByKey = new Map(secrets.map((s) => [s.credentialKey, s]));\n\n const results: AppIntegrations[] = [];\n\n for (const agent of agents) {\n try {\n const res = await fetch(`${agent.url}/_agent-native/env-status`, {\n signal: AbortSignal.timeout(3000),\n });\n if (!res.ok) {\n results.push({\n appId: agent.id,\n appName: agent.name,\n url: agent.url,\n color: agent.color,\n integrations: [],\n vaultAccessMode: access.mode,\n reachable: false,\n });\n continue;\n }\n\n const envStatus: Array<{\n key: string;\n label: string;\n required: boolean;\n configured: boolean;\n }> = await res.json();\n\n const appGrants = grants.filter(\n (g) => g.appId === agent.id && g.status === \"active\",\n );\n const grantedSecretIds = new Set(appGrants.map((g) => g.secretId));\n\n const integrations: IntegrationEntry[] = envStatus.map((env) => {\n const matchingSecret = secretByKey.get(env.key);\n return {\n key: env.key,\n label: env.label,\n required: env.required,\n configured: env.configured,\n vaultGranted:\n !!matchingSecret &&\n (access.mode === \"all-apps\" ||\n grantedSecretIds.has(matchingSecret.id)),\n vaultSecretId: matchingSecret?.id,\n };\n });\n\n results.push({\n appId: agent.id,\n appName: agent.name,\n url: agent.url,\n color: agent.color,\n integrations,\n vaultAccessMode: access.mode,\n reachable: true,\n });\n } catch {\n results.push({\n appId: agent.id,\n appName: agent.name,\n url: agent.url,\n color: agent.color,\n integrations: [],\n vaultAccessMode: access.mode,\n reachable: false,\n });\n }\n }\n\n return results;\n}\n\n// ─── Vault Overview (for dashboard) ──────────────────────────────\n\nexport async function listVaultOverview() {\n const [secrets, grants, requests, access] = await Promise.all([\n listSecrets(),\n listGrants(),\n listRequests(),\n getVaultAccessSettings(),\n ]);\n const manualGrantCount = grants.filter((g) => g.status === \"active\").length;\n\n return {\n accessMode: access.mode,\n secretCount: secrets.length,\n activeGrantCount:\n access.mode === \"all-apps\" ? secrets.length : manualGrantCount,\n manualGrantCount,\n pendingRequestCount: requests.filter((r) => r.status === \"pending\").length,\n };\n}\n\n// ─── SendGrid Notifications ──────────────────────────────────────\n\nasync function notifyAdminsOfRequest(\n requestId: string,\n input: { credentialKey: string; appId: string; reason?: string | null },\n) {\n const apiKey = process.env.SENDGRID_API_KEY;\n const from = process.env.SENDGRID_FROM_EMAIL;\n const appUrl = process.env.APP_URL;\n if (!apiKey || !from || !appUrl) return;\n\n // Use approval policy approver emails as admin notification targets\n const { getApprovalPolicy } = await import(\"./dispatch-store.js\");\n const policy = await getApprovalPolicy();\n if (policy.approverEmails.length === 0) return;\n\n const body = [\n `Secret request: ${input.credentialKey} for ${input.appId}`,\n input.reason ? `Reason: ${input.reason}` : \"\",\n `Requested by: ${currentOwnerEmail()}`,\n \"\",\n `Review it here: ${appUrl}/vault`,\n ]\n .filter(Boolean)\n .join(\"\\n\");\n\n await fetch(\"https://api.sendgrid.com/v3/mail/send\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n personalizations: [\n {\n to: policy.approverEmails.map((email) => ({ email })),\n subject: `Vault request: ${input.credentialKey} for ${input.appId}`,\n },\n ],\n from: { email: from },\n content: [{ type: \"text/plain\", value: body }],\n custom_args: { requestId },\n }),\n }).catch(() => {});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"vault-store.js","sourceRoot":"","sources":["../../../src/server/lib/vault-store.ts"],"names":[],"mappings":"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","sourcesContent":["import crypto from \"node:crypto\";\nimport { and, desc, eq, isNull, or } from \"drizzle-orm\";\nimport { discoverAgents } from \"@agent-native/core/server/agent-discovery\";\nimport {\n deleteAppSecret,\n listAppSecretsForScope,\n writeAppSecret,\n type SecretScope,\n} from \"@agent-native/core/secrets\";\nimport {\n getOrgSetting,\n getUserSetting,\n putOrgSetting,\n putUserSetting,\n} from \"@agent-native/core/settings\";\nimport { getDb, schema } from \"../../db/index.js\";\nimport {\n currentOwnerEmail,\n currentOrgId,\n recordAudit,\n} from \"./dispatch-store.js\";\n\nconst VAULT_ACCESS_SETTINGS_KEY = \"dispatch-vault-access-settings\";\nconst VAULT_SYNC_DESCRIPTION_PREFIX = \"Synced from Dispatch vault:\";\n\nexport type VaultAccessMode = \"all-apps\" | \"manual\";\n\nexport interface VaultAccessSettings {\n mode: VaultAccessMode;\n scope: \"org\" | \"user\";\n scopeId: string;\n}\n\n/**\n * Caller-supplied access context for vault operations.\n *\n * Every getSecret / updateSecret / deleteSecret / createGrant call must\n * pass the ctx of the *current request* so the row is scoped to that\n * caller's tenant. Looking up a vault secret by id alone is unsafe — UUIDs\n * are not authorization. A row matches the ctx if either the caller owns\n * it or it lives in the caller's active org.\n */\nexport interface VaultCtx {\n ownerEmail: string;\n orgId: string | null;\n}\n\n/**\n * Build a VaultCtx from the current request. Throws if the request is\n * unauthenticated — the previous behavior of falling back to \"local@localhost\"\n * leaked rows across tenants when a misconfigured environment skipped auth.\n */\nexport function requireVaultCtx(): VaultCtx {\n const ownerEmail = currentOwnerEmail();\n if (!ownerEmail) {\n throw new Error(\"Vault operation requires an authenticated user\");\n }\n return { ownerEmail, orgId: currentOrgId() };\n}\n\n/** WHERE clause that limits a vault row to the caller's ownership scope. */\nfunction ctxScope<T extends { ownerEmail: any; orgId: any }>(\n table: T,\n ctx: VaultCtx,\n) {\n if (!ctx.orgId) {\n return and(eq(table.ownerEmail, ctx.ownerEmail), isNull(table.orgId));\n }\n return or(eq(table.ownerEmail, ctx.ownerEmail), eq(table.orgId, ctx.orgId));\n}\n\n/** Build a ctx that scopes to a specific row's owner/org (used when a\n * request approver acts on behalf of the original requester so the\n * created secret lands in the request's org). */\nfunction ctxForRow(row: {\n ownerEmail: string;\n orgId: string | null;\n}): VaultCtx {\n return { ownerEmail: row.ownerEmail, orgId: row.orgId };\n}\n\nfunction ctxForSecretRow(row: VaultSecretRow, fallback: VaultCtx): VaultCtx {\n return row.ownerEmail ? ctxForRow(row) : fallback;\n}\n\nfunction id() {\n return crypto.randomUUID();\n}\n\nfunction now() {\n return Date.now();\n}\n\nfunction safeJson(value: unknown) {\n return JSON.stringify(value ?? null);\n}\n\nfunction scopedFilter<T extends { ownerEmail: any; orgId: any }>(table: T) {\n return ctxScope(table, requireVaultCtx());\n}\n\nfunction normalizeCredentialKey(value: string) {\n return value.trim();\n}\n\nfunction vaultAccessScope() {\n const orgId = currentOrgId();\n if (orgId) return { scope: \"org\" as const, scopeId: orgId };\n return { scope: \"user\" as const, scopeId: currentOwnerEmail() };\n}\n\nfunction parseVaultAccessMode(value: unknown): VaultAccessMode {\n return value === \"manual\" ? \"manual\" : \"all-apps\";\n}\n\nexport async function getVaultAccessSettings(): Promise<VaultAccessSettings> {\n const scope = vaultAccessScope();\n const raw =\n scope.scope === \"org\"\n ? await getOrgSetting(scope.scopeId, VAULT_ACCESS_SETTINGS_KEY)\n : await getUserSetting(scope.scopeId, VAULT_ACCESS_SETTINGS_KEY);\n return {\n ...scope,\n mode: parseVaultAccessMode(raw?.mode),\n };\n}\n\nexport async function setVaultAccessSettings(input: {\n mode: VaultAccessMode;\n}): Promise<VaultAccessSettings> {\n const scope = vaultAccessScope();\n const next = { mode: parseVaultAccessMode(input.mode) };\n if (scope.scope === \"org\") {\n await putOrgSetting(scope.scopeId, VAULT_ACCESS_SETTINGS_KEY, next);\n } else {\n await putUserSetting(scope.scopeId, VAULT_ACCESS_SETTINGS_KEY, next);\n }\n await recordAudit({\n action: \"vault.access-settings.updated\",\n targetType: \"vault-settings\",\n targetId: VAULT_ACCESS_SETTINGS_KEY,\n summary:\n next.mode === \"all-apps\"\n ? \"Set vault access to all workspace apps\"\n : \"Set vault access to manual per-app grants\",\n metadata: next,\n });\n return getVaultAccessSettings();\n}\n\n// ─── Vault Audit ──────────────────────────────────────────────────\n\nexport async function recordVaultAudit(input: {\n action: string;\n secretId?: string | null;\n appId?: string | null;\n summary: string;\n metadata?: unknown;\n actor?: string;\n}) {\n const db = getDb();\n await db.insert(schema.vaultAuditLog).values({\n id: id(),\n ownerEmail: currentOwnerEmail(),\n orgId: currentOrgId(),\n secretId: input.secretId || null,\n appId: input.appId || null,\n action: input.action,\n actor: input.actor || currentOwnerEmail(),\n summary: input.summary,\n metadata: input.metadata ? safeJson(input.metadata) : null,\n createdAt: now(),\n });\n}\n\nexport async function listVaultAudit(limit = 50) {\n const db = getDb();\n return db\n .select()\n .from(schema.vaultAuditLog)\n .where(scopedFilter(schema.vaultAuditLog))\n .orderBy(desc(schema.vaultAuditLog.createdAt))\n .limit(limit);\n}\n\n// ─── Secrets ──────────────────────────────────────────────────────\n\nexport async function listSecrets() {\n const db = getDb();\n return db\n .select()\n .from(schema.vaultSecrets)\n .where(scopedFilter(schema.vaultSecrets))\n .orderBy(desc(schema.vaultSecrets.updatedAt));\n}\n\nexport async function getSecret(secretId: string, ctx: VaultCtx) {\n const db = getDb();\n const [row] = await db\n .select()\n .from(schema.vaultSecrets)\n .where(\n and(\n eq(schema.vaultSecrets.id, secretId),\n ctxScope(schema.vaultSecrets, ctx),\n ),\n )\n .limit(1);\n return row ?? null;\n}\n\nexport async function createSecret(\n input: {\n credentialKey: string;\n value: string;\n name: string;\n provider?: string | null;\n description?: string | null;\n },\n ctx: VaultCtx = requireVaultCtx(),\n) {\n const db = getDb();\n const timestamp = now();\n const credentialKey = normalizeCredentialKey(input.credentialKey);\n if (!credentialKey) throw new Error(\"Credential key is required\");\n const existing = await db\n .select()\n .from(schema.vaultSecrets)\n .where(\n and(\n eq(schema.vaultSecrets.credentialKey, credentialKey),\n ctxScope(schema.vaultSecrets, ctx),\n ),\n )\n .orderBy(desc(schema.vaultSecrets.updatedAt))\n .limit(1);\n\n if (existing[0]) {\n await db\n .update(schema.vaultSecrets)\n .set({\n name: input.name,\n credentialKey,\n value: input.value,\n provider: input.provider || null,\n description: input.description || null,\n updatedAt: timestamp,\n })\n .where(\n and(\n eq(schema.vaultSecrets.id, existing[0].id),\n ctxScope(schema.vaultSecrets, ctx),\n ),\n );\n\n await recordVaultAudit({\n action: \"secret.updated\",\n secretId: existing[0].id,\n summary: `Updated secret \"${input.name}\" (${credentialKey})`,\n metadata: { credentialKey, provider: input.provider },\n });\n\n await recordAudit({\n action: \"vault.secret.updated\",\n targetType: \"vault-secret\",\n targetId: existing[0].id,\n summary: `Updated vault secret \"${input.name}\" (${credentialKey})`,\n });\n\n const updated = await getSecret(existing[0].id, ctx);\n if (updated) {\n await syncSecretsToCredentialStore(\n [updated],\n ctxForSecretRow(updated, ctx),\n );\n }\n return updated;\n }\n\n const secretId = id();\n const actor = ctx.ownerEmail;\n\n await db.insert(schema.vaultSecrets).values({\n id: secretId,\n ownerEmail: actor,\n orgId: ctx.orgId,\n name: input.name,\n credentialKey,\n value: input.value,\n provider: input.provider || null,\n description: input.description || null,\n createdBy: actor,\n createdAt: timestamp,\n updatedAt: timestamp,\n });\n\n await recordVaultAudit({\n action: \"secret.created\",\n secretId,\n summary: `Created secret \"${input.name}\" (${credentialKey})`,\n metadata: { credentialKey, provider: input.provider },\n });\n\n await recordAudit({\n action: \"vault.secret.created\",\n targetType: \"vault-secret\",\n targetId: secretId,\n summary: `Created vault secret \"${input.name}\" (${credentialKey})`,\n });\n\n const created = await getSecret(secretId, ctx);\n if (created) {\n await syncSecretsToCredentialStore(\n [created],\n ctxForSecretRow(created, ctx),\n );\n }\n return created;\n}\n\nexport async function updateSecret(\n secretId: string,\n input:\n | string\n | {\n credentialKey?: string;\n value?: string;\n name?: string;\n provider?: string | null;\n description?: string | null;\n },\n ctx: VaultCtx = requireVaultCtx(),\n) {\n const db = getDb();\n const existing = await getSecret(secretId, ctx);\n if (!existing) throw new Error(\"Secret not found\");\n const patch = typeof input === \"string\" ? { value: input } : input;\n const credentialKey =\n patch.credentialKey !== undefined\n ? normalizeCredentialKey(patch.credentialKey)\n : existing.credentialKey;\n if (!credentialKey) throw new Error(\"Credential key is required\");\n const name = patch.name !== undefined ? patch.name.trim() : existing.name;\n if (!name) throw new Error(\"Secret name is required\");\n const value = patch.value !== undefined ? patch.value : existing.value;\n if (!value) throw new Error(\"Secret value is required\");\n const provider =\n patch.provider !== undefined ? patch.provider || null : existing.provider;\n const description =\n patch.description !== undefined\n ? patch.description || null\n : existing.description;\n\n if (credentialKey !== existing.credentialKey) {\n const conflict = await db\n .select({ id: schema.vaultSecrets.id })\n .from(schema.vaultSecrets)\n .where(\n and(\n eq(schema.vaultSecrets.credentialKey, credentialKey),\n ctxScope(schema.vaultSecrets, ctx),\n ),\n )\n .limit(1);\n if (conflict[0] && conflict[0].id !== secretId) {\n throw new Error(`Credential key \"${credentialKey}\" is already in use`);\n }\n }\n\n await db\n .update(schema.vaultSecrets)\n .set({\n name,\n credentialKey,\n value,\n provider,\n description,\n updatedAt: now(),\n })\n .where(\n and(\n eq(schema.vaultSecrets.id, secretId),\n ctxScope(schema.vaultSecrets, ctx),\n ),\n );\n\n const auditMetadata = {\n name,\n previousName: name !== existing.name ? existing.name : undefined,\n credentialKey,\n previousCredentialKey:\n credentialKey !== existing.credentialKey\n ? existing.credentialKey\n : undefined,\n provider,\n previousProvider:\n provider !== existing.provider ? existing.provider : undefined,\n description,\n previousDescription:\n description !== existing.description ? existing.description : undefined,\n valueChanged: value !== existing.value ? true : undefined,\n };\n\n await recordVaultAudit({\n action: \"secret.updated\",\n secretId,\n summary: `Updated secret \"${name}\" (${credentialKey})`,\n metadata: auditMetadata,\n });\n\n await recordAudit({\n action: \"vault.secret.updated\",\n targetType: \"vault-secret\",\n targetId: secretId,\n summary: `Updated vault secret \"${name}\" (${credentialKey})`,\n metadata: auditMetadata,\n });\n\n const updated = await getSecret(secretId, ctx);\n if (updated) {\n await syncSecretsToCredentialStore(\n [updated],\n ctxForSecretRow(updated, ctx),\n );\n }\n if (updated && credentialKey !== existing.credentialKey) {\n await cleanupSyncedCredentialKeysIfUnused(ctxForRow(existing), [\n existing.credentialKey,\n ]);\n } else if (updated && patch.credentialKey !== undefined) {\n await cleanupSyncedCredentialKeysIfUnused(ctxForSecretRow(updated, ctx));\n }\n return updated;\n}\n\nexport async function deleteSecret(\n secretId: string,\n ctx: VaultCtx = requireVaultCtx(),\n) {\n const db = getDb();\n const existing = await getSecret(secretId, ctx);\n if (!existing) throw new Error(\"Secret not found\");\n\n // Revoke all active grants first\n const grants = await listGrants({ secretId });\n for (const grant of grants) {\n if (grant.status === \"active\") {\n await revokeGrant(grant.id, ctx);\n }\n }\n\n await db\n .delete(schema.vaultSecrets)\n .where(\n and(\n eq(schema.vaultSecrets.id, secretId),\n ctxScope(schema.vaultSecrets, ctx),\n ),\n );\n await cleanupSyncedCredentialKeysIfUnused(ctxForRow(existing), [\n existing.credentialKey,\n ]);\n\n await recordVaultAudit({\n action: \"secret.deleted\",\n secretId,\n summary: `Deleted secret \"${existing.name}\" (${existing.credentialKey})`,\n });\n\n await recordAudit({\n action: \"vault.secret.deleted\",\n targetType: \"vault-secret\",\n targetId: secretId,\n summary: `Deleted vault secret \"${existing.name}\" (${existing.credentialKey})`,\n });\n\n return existing;\n}\n\n// ─── Grants ──────────────────────────────────────────────────────\n\nexport async function listGrants(filter?: {\n secretId?: string;\n appId?: string;\n}) {\n const db = getDb();\n const conditions = [scopedFilter(schema.vaultGrants)];\n if (filter?.secretId) {\n conditions.push(eq(schema.vaultGrants.secretId, filter.secretId) as any);\n }\n if (filter?.appId) {\n conditions.push(eq(schema.vaultGrants.appId, filter.appId) as any);\n }\n return db\n .select()\n .from(schema.vaultGrants)\n .where(and(...conditions))\n .orderBy(desc(schema.vaultGrants.updatedAt));\n}\n\nexport async function getGrant(\n grantId: string,\n ctx: VaultCtx = requireVaultCtx(),\n) {\n const db = getDb();\n const [row] = await db\n .select()\n .from(schema.vaultGrants)\n .where(\n and(\n eq(schema.vaultGrants.id, grantId),\n ctxScope(schema.vaultGrants, ctx),\n ),\n )\n .limit(1);\n return row ?? null;\n}\n\nexport async function createGrant(\n secretId: string,\n appId: string,\n ctx: VaultCtx = requireVaultCtx(),\n) {\n const db = getDb();\n const secret = await getSecret(secretId, ctx);\n if (!secret) throw new Error(\"Secret not found\");\n\n const timestamp = now();\n const grantId = id();\n const actor = ctx.ownerEmail;\n\n await db.insert(schema.vaultGrants).values({\n id: grantId,\n ownerEmail: actor,\n orgId: ctx.orgId,\n secretId,\n appId,\n grantedBy: actor,\n status: \"active\",\n syncedAt: null,\n createdAt: timestamp,\n updatedAt: timestamp,\n });\n\n await recordVaultAudit({\n action: \"grant.created\",\n secretId,\n appId,\n summary: `Granted \"${secret.name}\" (${secret.credentialKey}) to ${appId}`,\n metadata: { grantId },\n });\n\n await recordAudit({\n action: \"vault.grant.created\",\n targetType: \"vault-grant\",\n targetId: grantId,\n summary: `Granted vault secret \"${secret.name}\" to ${appId}`,\n });\n\n return getGrant(grantId);\n}\n\nexport async function grantSecretsToApp(\n secretIds: string[],\n appId: string,\n ctx: VaultCtx = requireVaultCtx(),\n) {\n const access = await getVaultAccessSettings();\n const uniqueSecretIds = Array.from(new Set(secretIds));\n if (access.mode === \"all-apps\") {\n return {\n appId,\n accessMode: access.mode,\n created: [],\n skipped: uniqueSecretIds,\n };\n }\n const existingActive = (await listGrants({ appId })).filter(\n (grant) => grant.status === \"active\",\n );\n const existingSecretIds = new Set(\n existingActive.map((grant) => grant.secretId),\n );\n const created = [];\n const skipped: string[] = [];\n\n for (const secretId of uniqueSecretIds) {\n if (existingSecretIds.has(secretId)) {\n skipped.push(secretId);\n continue;\n }\n const grant = await createGrant(secretId, appId, ctx);\n if (grant) {\n created.push(grant);\n existingSecretIds.add(secretId);\n }\n }\n\n return { appId, accessMode: access.mode, created, skipped };\n}\n\nexport async function revokeGrant(\n grantId: string,\n ctx: VaultCtx = requireVaultCtx(),\n) {\n const db = getDb();\n const grant = await getGrant(grantId, ctx);\n if (!grant) throw new Error(\"Grant not found\");\n\n const secret = await getSecret(grant.secretId, ctx);\n\n await db\n .update(schema.vaultGrants)\n .set({ status: \"revoked\", updatedAt: now() })\n .where(\n and(\n eq(schema.vaultGrants.id, grantId),\n ctxScope(schema.vaultGrants, ctx),\n ),\n );\n\n await recordVaultAudit({\n action: \"grant.revoked\",\n secretId: grant.secretId,\n appId: grant.appId,\n summary: `Revoked ${secret?.credentialKey || grant.secretId} from ${grant.appId}`,\n metadata: { grantId },\n });\n\n await recordAudit({\n action: \"vault.grant.revoked\",\n targetType: \"vault-grant\",\n targetId: grantId,\n summary: `Revoked vault secret \"${secret?.name || grant.secretId}\" from ${grant.appId}`,\n });\n\n return getGrant(grantId, ctx);\n}\n\n// ─── Shared Credential Store Sync ─────────────────────────────────\n\ntype VaultSecretRow = typeof schema.vaultSecrets.$inferSelect;\n\nexport function credentialStoreScopeForVaultCtx(ctx: VaultCtx): {\n scope: Extract<SecretScope, \"org\" | \"workspace\">;\n scopeId: string;\n} {\n if (ctx.orgId) return { scope: \"org\", scopeId: ctx.orgId };\n return { scope: \"workspace\", scopeId: `solo:${ctx.ownerEmail}` };\n}\n\nexport async function syncSecretsToCredentialStore(\n secrets: VaultSecretRow[],\n ctx: VaultCtx,\n) {\n const target = credentialStoreScopeForVaultCtx(ctx);\n const syncedKeys: string[] = [];\n\n for (const secret of secrets) {\n if (!secret.credentialKey || !secret.value) continue;\n await writeAppSecret({\n key: secret.credentialKey,\n value: secret.value,\n scope: target.scope,\n scopeId: target.scopeId,\n description: `${VAULT_SYNC_DESCRIPTION_PREFIX} ${secret.name}`,\n });\n syncedKeys.push(secret.credentialKey);\n }\n\n return { ...target, keys: syncedKeys };\n}\n\nexport async function cleanupSyncedCredentialKeysIfUnused(\n ctx: VaultCtx,\n candidateKeys?: string[],\n) {\n const db = getDb();\n const target = credentialStoreScopeForVaultCtx(ctx);\n const keys = candidateKeys\n ? candidateKeys\n : (await listAppSecretsForScope(target.scope, target.scopeId))\n .filter((secret) =>\n secret.description?.startsWith(VAULT_SYNC_DESCRIPTION_PREFIX),\n )\n .map((secret) => secret.key);\n\n for (const key of new Set(keys.filter(Boolean))) {\n const stillUsesKey = await db\n .select({ id: schema.vaultSecrets.id })\n .from(schema.vaultSecrets)\n .where(\n and(\n eq(schema.vaultSecrets.credentialKey, key),\n ctxScope(schema.vaultSecrets, ctx),\n ),\n )\n .limit(1);\n if (!stillUsesKey[0]) {\n await deleteAppSecret({\n key,\n scope: target.scope,\n scopeId: target.scopeId,\n });\n }\n }\n}\n\n// ─── Sync ──────────────────────────────────────────────────────\n\nexport async function syncGrantsToApp(\n appId: string,\n ctx: VaultCtx = requireVaultCtx(),\n) {\n const db = getDb();\n const access = await getVaultAccessSettings();\n const agents = await discoverAgents(\"dispatch\");\n const agent = agents.find((a) => a.id === appId);\n if (!agent) throw new Error(`App \"${appId}\" not found in agent registry`);\n\n const secretsToSync: VaultSecretRow[] = [];\n const activeGrants =\n access.mode === \"manual\"\n ? (await listGrants({ appId })).filter((g) => g.status === \"active\")\n : [];\n\n if (access.mode === \"all-apps\") {\n const secrets = await listSecrets();\n for (const secret of secrets) {\n secretsToSync.push(secret);\n }\n } else {\n for (const grant of activeGrants) {\n const secret = await getSecret(grant.secretId, ctx);\n if (secret) {\n secretsToSync.push(secret);\n }\n }\n }\n\n if (secretsToSync.length === 0) {\n return { appId, accessMode: access.mode, synced: 0, keys: [] };\n }\n\n const credentialStoreSync = await syncSecretsToCredentialStore(\n secretsToSync,\n ctx,\n );\n const vars = secretsToSync.map((secret) => ({\n key: secret.credentialKey,\n value: secret.value,\n }));\n let envVarSync:\n | { status: \"synced\"; keys: string[] }\n | { status: \"skipped\"; reason: string }\n | { status: \"failed\"; reason: string };\n\n // Best-effort push to the app's env-vars endpoint for local/dev apps that\n // still read process.env directly. Production/shared-DB apps intentionally\n // reject env writes; the encrypted app_secrets sync above is the canonical\n // path for request-scoped credentials.\n try {\n const res = await fetch(`${agent.url}/_agent-native/env-vars`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ vars }),\n });\n\n if (res.ok) {\n const result = await res.json();\n envVarSync = { status: \"synced\", keys: result.saved || [] };\n } else {\n const err = await res.text().catch(() => \"Unknown error\");\n envVarSync = { status: \"skipped\", reason: err };\n }\n } catch (err) {\n envVarSync = {\n status: \"failed\",\n reason: err instanceof Error ? err.message : String(err),\n };\n }\n\n const syncedKeys = credentialStoreSync.keys;\n const timestamp = now();\n\n // Update syncedAt on grants that were successfully pushed to the shared\n // credential store. All-apps mode has no explicit grant rows to update.\n for (const grant of activeGrants) {\n const secret = await getSecret(grant.secretId, ctx);\n if (secret && syncedKeys.includes(secret.credentialKey)) {\n await db\n .update(schema.vaultGrants)\n .set({ syncedAt: timestamp, updatedAt: timestamp })\n .where(eq(schema.vaultGrants.id, grant.id));\n }\n }\n\n await recordVaultAudit({\n action: \"secret.synced\",\n appId,\n summary: `Synced ${syncedKeys.length} secret(s) to ${appId}: ${syncedKeys.join(\", \")}`,\n metadata: {\n syncedKeys,\n accessMode: access.mode,\n credentialStore: {\n scope: credentialStoreSync.scope,\n scopeId: credentialStoreSync.scopeId,\n },\n envVars: envVarSync,\n },\n });\n\n return {\n appId,\n accessMode: access.mode,\n synced: syncedKeys.length,\n keys: syncedKeys,\n credentialStore: {\n scope: credentialStoreSync.scope,\n scopeId: credentialStoreSync.scopeId,\n synced: credentialStoreSync.keys.length,\n },\n envVars: envVarSync,\n };\n}\n\n// ─── Requests ──────────────────────────────────────────────────────\n\nexport async function listRequests(filter?: { status?: string }) {\n const db = getDb();\n const conditions = [scopedFilter(schema.vaultRequests)];\n if (filter?.status) {\n conditions.push(eq(schema.vaultRequests.status, filter.status) as any);\n }\n return db\n .select()\n .from(schema.vaultRequests)\n .where(and(...conditions))\n .orderBy(desc(schema.vaultRequests.updatedAt));\n}\n\nexport async function getRequest(\n requestId: string,\n ctx: VaultCtx = requireVaultCtx(),\n) {\n const db = getDb();\n const [row] = await db\n .select()\n .from(schema.vaultRequests)\n .where(\n and(\n eq(schema.vaultRequests.id, requestId),\n ctxScope(schema.vaultRequests, ctx),\n ),\n )\n .limit(1);\n return row ?? null;\n}\n\nexport async function createRequest(input: {\n credentialKey: string;\n appId: string;\n reason?: string | null;\n}) {\n const db = getDb();\n const timestamp = now();\n const requestId = id();\n const actor = currentOwnerEmail();\n\n await db.insert(schema.vaultRequests).values({\n id: requestId,\n ownerEmail: actor,\n orgId: currentOrgId(),\n credentialKey: input.credentialKey,\n appId: input.appId,\n reason: input.reason || null,\n requestedBy: actor,\n status: \"pending\",\n reviewedBy: null,\n reviewedAt: null,\n createdAt: timestamp,\n updatedAt: timestamp,\n });\n\n await recordVaultAudit({\n action: \"request.created\",\n appId: input.appId,\n summary: `${actor} requested ${input.credentialKey} for ${input.appId}`,\n metadata: { requestId, reason: input.reason },\n });\n\n await notifyAdminsOfRequest(requestId, input);\n\n return getRequest(requestId);\n}\n\nexport async function approveRequest(\n requestId: string,\n secretValue: string,\n secretName?: string,\n ctx: VaultCtx = requireVaultCtx(),\n) {\n const db = getDb();\n const request = await getRequest(requestId, ctx);\n if (!request) throw new Error(\"Request not found\");\n if (request.status !== \"pending\") {\n throw new Error(\"Only pending requests can be approved\");\n }\n\n const timestamp = now();\n const reviewer = ctx.ownerEmail;\n\n // Update request status — scoped to caller's tenant.\n await db\n .update(schema.vaultRequests)\n .set({\n status: \"approved\",\n reviewedBy: reviewer,\n reviewedAt: timestamp,\n updatedAt: timestamp,\n })\n .where(\n and(\n eq(schema.vaultRequests.id, requestId),\n ctxScope(schema.vaultRequests, ctx),\n ),\n );\n\n // Secret + grant must land in the REQUEST's tenant, not the approver's\n // (the approver may be acting on behalf of another user in the same org).\n const requestCtx = ctxForRow(request);\n\n // Check if secret already exists in the request's tenant for this key.\n const existingSecrets = await db\n .select()\n .from(schema.vaultSecrets)\n .where(\n and(\n eq(schema.vaultSecrets.credentialKey, request.credentialKey),\n ctxScope(schema.vaultSecrets, requestCtx),\n ),\n );\n let secret = existingSecrets[0] ?? null;\n\n if (!secret) {\n secret = await createSecret(\n {\n credentialKey: request.credentialKey,\n value: secretValue,\n name: secretName || request.credentialKey,\n },\n requestCtx,\n );\n }\n\n if (secret) {\n // Create the grant in the request's tenant as well.\n await createGrant(secret.id, request.appId, requestCtx);\n }\n\n await recordVaultAudit({\n action: \"request.approved\",\n appId: request.appId,\n summary: `Approved ${request.credentialKey} for ${request.appId} (requested by ${request.requestedBy})`,\n metadata: { requestId, reviewer },\n });\n\n return getRequest(requestId, ctx);\n}\n\nexport async function denyRequest(\n requestId: string,\n reason?: string | null,\n ctx: VaultCtx = requireVaultCtx(),\n) {\n const db = getDb();\n const request = await getRequest(requestId, ctx);\n if (!request) throw new Error(\"Request not found\");\n if (request.status !== \"pending\") {\n throw new Error(\"Only pending requests can be denied\");\n }\n\n const timestamp = now();\n const reviewer = ctx.ownerEmail;\n\n await db\n .update(schema.vaultRequests)\n .set({\n status: \"denied\",\n reviewedBy: reviewer,\n reviewedAt: timestamp,\n updatedAt: timestamp,\n })\n .where(\n and(\n eq(schema.vaultRequests.id, requestId),\n ctxScope(schema.vaultRequests, ctx),\n ),\n );\n\n await recordVaultAudit({\n action: \"request.denied\",\n appId: request.appId,\n summary: `Denied ${request.credentialKey} for ${request.appId} (requested by ${request.requestedBy})`,\n metadata: { requestId, reviewer, reason },\n });\n\n return getRequest(requestId, ctx);\n}\n\n// ─── Integrations Catalog ────────────────────────────────────────\n\nexport interface IntegrationEntry {\n key: string;\n label: string;\n required: boolean;\n configured: boolean;\n vaultGranted: boolean;\n vaultSecretId?: string;\n}\n\nexport interface AppIntegrations {\n appId: string;\n appName: string;\n url: string;\n color: string;\n integrations: IntegrationEntry[];\n vaultAccessMode: VaultAccessMode;\n reachable: boolean;\n}\n\nexport async function listIntegrationsCatalog(): Promise<AppIntegrations[]> {\n const access = await getVaultAccessSettings();\n const agents = await discoverAgents(\"dispatch\");\n const grants = await listGrants();\n const secrets = await listSecrets();\n\n const secretByKey = new Map(secrets.map((s) => [s.credentialKey, s]));\n\n const results: AppIntegrations[] = [];\n\n for (const agent of agents) {\n try {\n const res = await fetch(`${agent.url}/_agent-native/env-status`, {\n signal: AbortSignal.timeout(3000),\n });\n if (!res.ok) {\n results.push({\n appId: agent.id,\n appName: agent.name,\n url: agent.url,\n color: agent.color,\n integrations: [],\n vaultAccessMode: access.mode,\n reachable: false,\n });\n continue;\n }\n\n const envStatus: Array<{\n key: string;\n label: string;\n required: boolean;\n configured: boolean;\n }> = await res.json();\n\n const appGrants = grants.filter(\n (g) => g.appId === agent.id && g.status === \"active\",\n );\n const grantedSecretIds = new Set(appGrants.map((g) => g.secretId));\n\n const integrations: IntegrationEntry[] = envStatus.map((env) => {\n const matchingSecret = secretByKey.get(env.key);\n return {\n key: env.key,\n label: env.label,\n required: env.required,\n configured: env.configured,\n vaultGranted:\n !!matchingSecret &&\n (access.mode === \"all-apps\" ||\n grantedSecretIds.has(matchingSecret.id)),\n vaultSecretId: matchingSecret?.id,\n };\n });\n\n results.push({\n appId: agent.id,\n appName: agent.name,\n url: agent.url,\n color: agent.color,\n integrations,\n vaultAccessMode: access.mode,\n reachable: true,\n });\n } catch {\n results.push({\n appId: agent.id,\n appName: agent.name,\n url: agent.url,\n color: agent.color,\n integrations: [],\n vaultAccessMode: access.mode,\n reachable: false,\n });\n }\n }\n\n return results;\n}\n\n// ─── Vault Overview (for dashboard) ──────────────────────────────\n\nexport async function listVaultOverview() {\n const [secrets, grants, requests, access] = await Promise.all([\n listSecrets(),\n listGrants(),\n listRequests(),\n getVaultAccessSettings(),\n ]);\n const manualGrantCount = grants.filter((g) => g.status === \"active\").length;\n\n return {\n accessMode: access.mode,\n secretCount: secrets.length,\n activeGrantCount:\n access.mode === \"all-apps\" ? secrets.length : manualGrantCount,\n manualGrantCount,\n pendingRequestCount: requests.filter((r) => r.status === \"pending\").length,\n };\n}\n\n// ─── SendGrid Notifications ──────────────────────────────────────\n\nasync function notifyAdminsOfRequest(\n requestId: string,\n input: { credentialKey: string; appId: string; reason?: string | null },\n) {\n const apiKey = process.env.SENDGRID_API_KEY;\n const from = process.env.SENDGRID_FROM_EMAIL;\n const appUrl = process.env.APP_URL;\n if (!apiKey || !from || !appUrl) return;\n\n // Use approval policy approver emails as admin notification targets\n const { getApprovalPolicy } = await import(\"./dispatch-store.js\");\n const policy = await getApprovalPolicy();\n if (policy.approverEmails.length === 0) return;\n\n const body = [\n `Secret request: ${input.credentialKey} for ${input.appId}`,\n input.reason ? `Reason: ${input.reason}` : \"\",\n `Requested by: ${currentOwnerEmail()}`,\n \"\",\n `Review it here: ${appUrl}/vault`,\n ]\n .filter(Boolean)\n .join(\"\\n\");\n\n await fetch(\"https://api.sendgrid.com/v3/mail/send\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n personalizations: [\n {\n to: policy.approverEmails.map((email) => ({ email })),\n subject: `Vault request: ${input.credentialKey} for ${input.appId}`,\n },\n ],\n from: { email: from },\n content: [{ type: \"text/plain\", value: body }],\n custom_args: { requestId },\n }),\n }).catch(() => {});\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/dispatch",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Dispatch — workspace control plane for agent-native apps. Vault, integrations, destinations, scheduled jobs, and cross-app delegation, shipped as a single drop-in package.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -2,15 +2,27 @@ import { afterEach, describe, expect, it, vi } from "vitest";
|
|
|
2
2
|
|
|
3
3
|
const mocks = vi.hoisted(() => ({
|
|
4
4
|
deleteAppSecret: vi.fn(),
|
|
5
|
+
getDb: vi.fn(),
|
|
6
|
+
listAppSecretsForScope: vi.fn(),
|
|
5
7
|
writeAppSecret: vi.fn(),
|
|
6
8
|
}));
|
|
7
9
|
|
|
8
10
|
vi.mock("@agent-native/core/secrets", () => ({
|
|
9
11
|
deleteAppSecret: mocks.deleteAppSecret,
|
|
12
|
+
listAppSecretsForScope: mocks.listAppSecretsForScope,
|
|
10
13
|
writeAppSecret: mocks.writeAppSecret,
|
|
11
14
|
}));
|
|
12
15
|
|
|
16
|
+
vi.mock("../../db/index.js", async (importOriginal) => {
|
|
17
|
+
const actual = await importOriginal<typeof import("../../db/index.js")>();
|
|
18
|
+
return {
|
|
19
|
+
...actual,
|
|
20
|
+
getDb: mocks.getDb,
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
|
|
13
24
|
import {
|
|
25
|
+
cleanupSyncedCredentialKeysIfUnused,
|
|
14
26
|
credentialStoreScopeForVaultCtx,
|
|
15
27
|
syncSecretsToCredentialStore,
|
|
16
28
|
} from "./vault-store.js";
|
|
@@ -69,3 +81,84 @@ describe("syncSecretsToCredentialStore", () => {
|
|
|
69
81
|
});
|
|
70
82
|
});
|
|
71
83
|
});
|
|
84
|
+
|
|
85
|
+
describe("cleanupSyncedCredentialKeysIfUnused", () => {
|
|
86
|
+
function mockVaultSecretLookup(rows: Array<{ id: string }> = []) {
|
|
87
|
+
const query = {
|
|
88
|
+
select: vi.fn(() => query),
|
|
89
|
+
from: vi.fn(() => query),
|
|
90
|
+
where: vi.fn(() => query),
|
|
91
|
+
limit: vi.fn(async () => rows),
|
|
92
|
+
};
|
|
93
|
+
mocks.getDb.mockReturnValue(query);
|
|
94
|
+
return query;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
it("deletes a candidate synced credential when no vault secret still uses it", async () => {
|
|
98
|
+
mockVaultSecretLookup([]);
|
|
99
|
+
|
|
100
|
+
await cleanupSyncedCredentialKeysIfUnused(
|
|
101
|
+
{ ownerEmail: "admin@example.test", orgId: "org_123" },
|
|
102
|
+
["OLD_API_KEY"],
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
expect(mocks.deleteAppSecret).toHaveBeenCalledWith({
|
|
106
|
+
key: "OLD_API_KEY",
|
|
107
|
+
scope: "org",
|
|
108
|
+
scopeId: "org_123",
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("uses the secret row scope for personal vault cleanup", async () => {
|
|
113
|
+
mockVaultSecretLookup([]);
|
|
114
|
+
|
|
115
|
+
await cleanupSyncedCredentialKeysIfUnused(
|
|
116
|
+
{ ownerEmail: "owner@example.test", orgId: null },
|
|
117
|
+
["PERSONAL_API_KEY"],
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
expect(mocks.deleteAppSecret).toHaveBeenCalledWith({
|
|
121
|
+
key: "PERSONAL_API_KEY",
|
|
122
|
+
scope: "workspace",
|
|
123
|
+
scopeId: "solo:owner@example.test",
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("keeps a candidate synced credential when another vault secret still uses it", async () => {
|
|
128
|
+
mockVaultSecretLookup([{ id: "secret_1" }]);
|
|
129
|
+
|
|
130
|
+
await cleanupSyncedCredentialKeysIfUnused(
|
|
131
|
+
{ ownerEmail: "admin@example.test", orgId: "org_123" },
|
|
132
|
+
["SHARED_API_KEY"],
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
expect(mocks.deleteAppSecret).not.toHaveBeenCalled();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("can scan synced app secrets to recover stale keys after a retry", async () => {
|
|
139
|
+
mockVaultSecretLookup([]);
|
|
140
|
+
mocks.listAppSecretsForScope.mockResolvedValue([
|
|
141
|
+
{
|
|
142
|
+
key: "STALE_KEY",
|
|
143
|
+
description: "Synced from Dispatch vault: Old key",
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
key: "HAND_WRITTEN_KEY",
|
|
147
|
+
description: "Created manually",
|
|
148
|
+
},
|
|
149
|
+
]);
|
|
150
|
+
|
|
151
|
+
await cleanupSyncedCredentialKeysIfUnused({
|
|
152
|
+
ownerEmail: "admin@example.test",
|
|
153
|
+
orgId: "org_123",
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
expect(mocks.listAppSecretsForScope).toHaveBeenCalledWith("org", "org_123");
|
|
157
|
+
expect(mocks.deleteAppSecret).toHaveBeenCalledTimes(1);
|
|
158
|
+
expect(mocks.deleteAppSecret).toHaveBeenCalledWith({
|
|
159
|
+
key: "STALE_KEY",
|
|
160
|
+
scope: "org",
|
|
161
|
+
scopeId: "org_123",
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
});
|
|
@@ -3,6 +3,7 @@ import { and, desc, eq, isNull, or } from "drizzle-orm";
|
|
|
3
3
|
import { discoverAgents } from "@agent-native/core/server/agent-discovery";
|
|
4
4
|
import {
|
|
5
5
|
deleteAppSecret,
|
|
6
|
+
listAppSecretsForScope,
|
|
6
7
|
writeAppSecret,
|
|
7
8
|
type SecretScope,
|
|
8
9
|
} from "@agent-native/core/secrets";
|
|
@@ -20,6 +21,7 @@ import {
|
|
|
20
21
|
} from "./dispatch-store.js";
|
|
21
22
|
|
|
22
23
|
const VAULT_ACCESS_SETTINGS_KEY = "dispatch-vault-access-settings";
|
|
24
|
+
const VAULT_SYNC_DESCRIPTION_PREFIX = "Synced from Dispatch vault:";
|
|
23
25
|
|
|
24
26
|
export type VaultAccessMode = "all-apps" | "manual";
|
|
25
27
|
|
|
@@ -77,6 +79,10 @@ function ctxForRow(row: {
|
|
|
77
79
|
return { ownerEmail: row.ownerEmail, orgId: row.orgId };
|
|
78
80
|
}
|
|
79
81
|
|
|
82
|
+
function ctxForSecretRow(row: VaultSecretRow, fallback: VaultCtx): VaultCtx {
|
|
83
|
+
return row.ownerEmail ? ctxForRow(row) : fallback;
|
|
84
|
+
}
|
|
85
|
+
|
|
80
86
|
function id() {
|
|
81
87
|
return crypto.randomUUID();
|
|
82
88
|
}
|
|
@@ -262,7 +268,12 @@ export async function createSecret(
|
|
|
262
268
|
});
|
|
263
269
|
|
|
264
270
|
const updated = await getSecret(existing[0].id, ctx);
|
|
265
|
-
if (updated)
|
|
271
|
+
if (updated) {
|
|
272
|
+
await syncSecretsToCredentialStore(
|
|
273
|
+
[updated],
|
|
274
|
+
ctxForSecretRow(updated, ctx),
|
|
275
|
+
);
|
|
276
|
+
}
|
|
266
277
|
return updated;
|
|
267
278
|
}
|
|
268
279
|
|
|
@@ -298,7 +309,12 @@ export async function createSecret(
|
|
|
298
309
|
});
|
|
299
310
|
|
|
300
311
|
const created = await getSecret(secretId, ctx);
|
|
301
|
-
if (created)
|
|
312
|
+
if (created) {
|
|
313
|
+
await syncSecretsToCredentialStore(
|
|
314
|
+
[created],
|
|
315
|
+
ctxForSecretRow(created, ctx),
|
|
316
|
+
);
|
|
317
|
+
}
|
|
302
318
|
return created;
|
|
303
319
|
}
|
|
304
320
|
|
|
@@ -401,26 +417,18 @@ export async function updateSecret(
|
|
|
401
417
|
});
|
|
402
418
|
|
|
403
419
|
const updated = await getSecret(secretId, ctx);
|
|
404
|
-
if (updated)
|
|
420
|
+
if (updated) {
|
|
421
|
+
await syncSecretsToCredentialStore(
|
|
422
|
+
[updated],
|
|
423
|
+
ctxForSecretRow(updated, ctx),
|
|
424
|
+
);
|
|
425
|
+
}
|
|
405
426
|
if (updated && credentialKey !== existing.credentialKey) {
|
|
406
|
-
|
|
407
|
-
.
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
eq(schema.vaultSecrets.credentialKey, existing.credentialKey),
|
|
412
|
-
ctxScope(schema.vaultSecrets, ctx),
|
|
413
|
-
),
|
|
414
|
-
)
|
|
415
|
-
.limit(1);
|
|
416
|
-
if (!stillUsesOldKey[0]) {
|
|
417
|
-
const target = credentialStoreScopeForVaultCtx(ctx);
|
|
418
|
-
await deleteAppSecret({
|
|
419
|
-
key: existing.credentialKey,
|
|
420
|
-
scope: target.scope,
|
|
421
|
-
scopeId: target.scopeId,
|
|
422
|
-
});
|
|
423
|
-
}
|
|
427
|
+
await cleanupSyncedCredentialKeysIfUnused(ctxForRow(existing), [
|
|
428
|
+
existing.credentialKey,
|
|
429
|
+
]);
|
|
430
|
+
} else if (updated && patch.credentialKey !== undefined) {
|
|
431
|
+
await cleanupSyncedCredentialKeysIfUnused(ctxForSecretRow(updated, ctx));
|
|
424
432
|
}
|
|
425
433
|
return updated;
|
|
426
434
|
}
|
|
@@ -449,6 +457,9 @@ export async function deleteSecret(
|
|
|
449
457
|
ctxScope(schema.vaultSecrets, ctx),
|
|
450
458
|
),
|
|
451
459
|
);
|
|
460
|
+
await cleanupSyncedCredentialKeysIfUnused(ctxForRow(existing), [
|
|
461
|
+
existing.credentialKey,
|
|
462
|
+
]);
|
|
452
463
|
|
|
453
464
|
await recordVaultAudit({
|
|
454
465
|
action: "secret.deleted",
|
|
@@ -652,7 +663,7 @@ export async function syncSecretsToCredentialStore(
|
|
|
652
663
|
value: secret.value,
|
|
653
664
|
scope: target.scope,
|
|
654
665
|
scopeId: target.scopeId,
|
|
655
|
-
description:
|
|
666
|
+
description: `${VAULT_SYNC_DESCRIPTION_PREFIX} ${secret.name}`,
|
|
656
667
|
});
|
|
657
668
|
syncedKeys.push(secret.credentialKey);
|
|
658
669
|
}
|
|
@@ -660,6 +671,41 @@ export async function syncSecretsToCredentialStore(
|
|
|
660
671
|
return { ...target, keys: syncedKeys };
|
|
661
672
|
}
|
|
662
673
|
|
|
674
|
+
export async function cleanupSyncedCredentialKeysIfUnused(
|
|
675
|
+
ctx: VaultCtx,
|
|
676
|
+
candidateKeys?: string[],
|
|
677
|
+
) {
|
|
678
|
+
const db = getDb();
|
|
679
|
+
const target = credentialStoreScopeForVaultCtx(ctx);
|
|
680
|
+
const keys = candidateKeys
|
|
681
|
+
? candidateKeys
|
|
682
|
+
: (await listAppSecretsForScope(target.scope, target.scopeId))
|
|
683
|
+
.filter((secret) =>
|
|
684
|
+
secret.description?.startsWith(VAULT_SYNC_DESCRIPTION_PREFIX),
|
|
685
|
+
)
|
|
686
|
+
.map((secret) => secret.key);
|
|
687
|
+
|
|
688
|
+
for (const key of new Set(keys.filter(Boolean))) {
|
|
689
|
+
const stillUsesKey = await db
|
|
690
|
+
.select({ id: schema.vaultSecrets.id })
|
|
691
|
+
.from(schema.vaultSecrets)
|
|
692
|
+
.where(
|
|
693
|
+
and(
|
|
694
|
+
eq(schema.vaultSecrets.credentialKey, key),
|
|
695
|
+
ctxScope(schema.vaultSecrets, ctx),
|
|
696
|
+
),
|
|
697
|
+
)
|
|
698
|
+
.limit(1);
|
|
699
|
+
if (!stillUsesKey[0]) {
|
|
700
|
+
await deleteAppSecret({
|
|
701
|
+
key,
|
|
702
|
+
scope: target.scope,
|
|
703
|
+
scopeId: target.scopeId,
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
663
709
|
// ─── Sync ──────────────────────────────────────────────────────
|
|
664
710
|
|
|
665
711
|
export async function syncGrantsToApp(
|