@treeseed/sdk 0.13.0-rc.86 → 0.13.0-rc.88

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.
Files changed (26) hide show
  1. package/dist/.treeseed-build-complete.json +1 -1
  2. package/dist/configuration/secrets-capability.d.ts +2 -2
  3. package/dist/configuration/secrets-capability.js +2 -2
  4. package/dist/operator-contracts/canonical-command-tree.js +3 -2
  5. package/dist/operator-contracts/catalog/hosted-topology-operations.d.ts +0 -3
  6. package/dist/operator-contracts/catalog/hosted-topology-operations.js +3 -5
  7. package/dist/operator-contracts/catalog/services/secret-commands.d.ts +2 -0
  8. package/dist/operator-contracts/catalog/services/secret-commands.js +31 -0
  9. package/dist/operator-contracts/catalog/services/secret-operations.d.ts +62 -0
  10. package/dist/operator-contracts/catalog/services/secret-operations.js +51 -0
  11. package/dist/operator-contracts/control-plane-operations.d.ts +57 -20
  12. package/dist/operator-contracts/control-plane-operations.js +3 -4
  13. package/dist/secrets-capability/github-actions-encryption.d.ts +2 -0
  14. package/dist/secrets-capability/github-actions-encryption.js +11 -0
  15. package/dist/secrets-capability/provider-operation-contracts.d.ts +1 -1
  16. package/dist/secrets-capability/secret-contracts.d.ts +33 -0
  17. package/dist/secrets-capability/secret-contracts.js +49 -0
  18. package/dist/secrets-capability/service-provider-contracts.d.ts +2 -2
  19. package/dist/secrets-capability/service-provider-contracts.js +15 -31
  20. package/package.json +1 -1
  21. package/dist/operator-contracts/catalog/services/service-vault-operations.d.ts +0 -17
  22. package/dist/operator-contracts/catalog/services/service-vault-operations.js +0 -158
  23. package/dist/secrets-capability/service-vault-contracts.d.ts +0 -108
  24. package/dist/secrets-capability/service-vault-contracts.js +0 -74
  25. package/dist/secrets-capability/service-vault-crypto.d.ts +0 -20
  26. package/dist/secrets-capability/service-vault-crypto.js +0 -210
@@ -1 +1 @@
1
- {"completedAt":"2026-09-03T11:02:09.609Z"}
1
+ {"completedAt":"2026-09-05T04:21:52.801Z"}
@@ -1,5 +1,5 @@
1
1
  export * from '../secrets-capability/service-provider-contracts.js';
2
2
  export * from '../secrets-capability/provider-operation-contracts.js';
3
3
  export * from '../secrets-capability/workflow-operation-contracts.js';
4
- export * from '../secrets-capability/service-vault-contracts.js';
5
- export * from '../secrets-capability/service-vault-crypto.js';
4
+ export * from '../secrets-capability/secret-contracts.js';
5
+ export * from '../secrets-capability/github-actions-encryption.js';
@@ -1,5 +1,5 @@
1
1
  export * from "../secrets-capability/service-provider-contracts.js";
2
2
  export * from "../secrets-capability/provider-operation-contracts.js";
3
3
  export * from "../secrets-capability/workflow-operation-contracts.js";
4
- export * from "../secrets-capability/service-vault-contracts.js";
5
- export * from "../secrets-capability/service-vault-crypto.js";
4
+ export * from "../secrets-capability/secret-contracts.js";
5
+ export * from "../secrets-capability/github-actions-encryption.js";
@@ -1,3 +1,4 @@
1
+ import { managedSecretCommands } from "./catalog/services/secret-commands.js";
1
2
  import { hostProviderEnvironmentBranch, PROVIDER_ENVIRONMENT_COMMAND_BINDINGS, providerEnvironmentBranches } from "./catalog/provider-environment-commands.js";
2
3
  const unavailable = (reason = "This capability is not enabled until its control-plane operation is accepted.") => ({
3
4
  kind: "unavailable",
@@ -30,7 +31,6 @@ const operationBindings = {
30
31
  "secrets status": local("local.secrets.status"),
31
32
  "secrets unlock": local("local.secrets.unlock"),
32
33
  "secrets lock": local("local.secrets.lock"),
33
- "secrets rotate": local("local.secrets.rotate"),
34
34
  "platform verify": local("local.platform.verify"),
35
35
  "platform workset": local("local.platform.workset"),
36
36
  "platform project create": local("local.platform.project.create"),
@@ -356,7 +356,8 @@ const commandTree = {
356
356
  branch("auth", [authLogin(), leaf("logout", "mutation"), leaf("status")]),
357
357
  branch("users", [userCreate()]),
358
358
  branch("teams", [leaf("list"), { nodeType: "leaf", segment: "current", description: "Show the active team for this authenticated server session.", kind: "read", resultSchemaId: "treeseed.command.teams.current/v1", execution: unavailable() }, { nodeType: "leaf", segment: "use", description: "Select the active team for this authenticated server session.", kind: "mutation", arguments: [{ name: "team", description: "Team UUID or unambiguous slug.", required: true }], options: [planOption], authorization: { capability: "teams.read", confirmation: "never" }, resultSchemaId: "treeseed.command.teams.use/v1", execution: unavailable() }]),
359
- branch("secrets", [leaf("list"), leaf("status"), leaf("unlock", "mutation", void 0, "credential"), leaf("lock", "mutation"), leaf("rotate", "mutation", void 0, "credential")]),
359
+ branch("secrets", [leaf("list"), leaf("status"), leaf("unlock", "mutation", void 0, "credential"), leaf("lock", "mutation")]),
360
+ managedSecretCommands,
360
361
  branch("platform", [
361
362
  { nodeType: "leaf", segment: "verify", description: "Verify a declarative Platform repository without package checkouts or a control-plane API.", kind: "read", arguments: [{ name: "root", description: "Platform root; defaults to the current directory.", required: false }], options: [{ name: "--profile", description: "Composable profile to verify.", type: "string[]" }, { name: "--json", description: "Emit the stable command-result envelope.", type: "boolean" }], resultSchemaId: "treeseed.platform-verification/v1", execution: local("local.platform.verify") },
362
363
  { nodeType: "leaf", segment: "workset", description: "Plan or safely materialize exact primary source checkouts beneath packages/.", kind: "mutation", options: [planOption, { name: "--apply", description: "Apply the frozen workset plan.", type: "boolean" }, { name: "--yes", description: "Confirm the planned checkout mutations.", type: "boolean" }, { name: "--profile", description: "Composable source profile; repeat to union profiles.", type: "string[]" }, { name: "--project", description: "Explicit project slug; repeat to select projects.", type: "string[]" }, { name: "--exclude", description: "Project slug to exclude; repeat as needed.", type: "string[]" }, { name: "--json", description: "Emit the stable command-result envelope.", type: "boolean" }], authorization: { capability: "development.workset", confirmation: "never" }, resultSchemaId: "treeseed.platform-workset-result/v1", execution: local("local.platform.workset") },
@@ -59,7 +59,6 @@ export declare const HOSTED_TOPOLOGY_OPERATIONS: {
59
59
  connectionRef: string;
60
60
  }>>;
61
61
  };
62
- credentialLeaseIds?: string[] | undefined;
63
62
  }, Record<string, unknown>>;
64
63
  readonly apply: import("../control-plane-operation.js").ControlPlaneOperationBinding<{
65
64
  teamId: string;
@@ -150,7 +149,6 @@ export declare const HOSTED_TOPOLOGY_OPERATIONS: {
150
149
  platformCommit: string;
151
150
  authorization: "authenticated-agent";
152
151
  };
153
- credentialLeaseIds?: string[] | undefined;
154
152
  }, Record<string, unknown>>;
155
153
  readonly status: import("../control-plane-operation.js").ControlPlaneOperationBinding<{
156
154
  teamId: string;
@@ -397,6 +395,5 @@ export declare const HOSTED_TOPOLOGY_OPERATIONS: {
397
395
  platformCommit: string;
398
396
  authorization: "authenticated-agent";
399
397
  };
400
- credentialLeaseIds?: string[] | undefined;
401
398
  }, Record<string, unknown>>;
402
399
  };
@@ -5,7 +5,6 @@ const teamPath = z.object({ teamId: z.string().min(1) }).strict();
5
5
  const empty = z.object({}).strict();
6
6
  const none = z.undefined();
7
7
  const operationReceipt = z.record(z.unknown());
8
- const credentialLeaseIds = z.array(z.string().min(1)).min(1).optional();
9
8
  const HOSTED_TOPOLOGY_OPERATIONS = {
10
9
  plan: defineOperation({
11
10
  operationId: "infrastructure.topology.plan",
@@ -22,7 +21,7 @@ const HOSTED_TOPOLOGY_OPERATIONS = {
22
21
  cacheScope: "none",
23
22
  pagination: "none",
24
23
  idempotencyRequired: false
25
- }, { path: teamPath, query: empty, body: z.object({ declaration: hostedTopologyDeclarationSchema, credentialLeaseIds }).strict(), output: operationReceipt }),
24
+ }, { path: teamPath, query: empty, body: z.object({ declaration: hostedTopologyDeclarationSchema }).strict(), output: operationReceipt }),
26
25
  apply: defineOperation({
27
26
  operationId: "infrastructure.topology.apply",
28
27
  description: "Apply an exact agent-authorized hosted topology plan through the operations runner.",
@@ -38,7 +37,7 @@ const HOSTED_TOPOLOGY_OPERATIONS = {
38
37
  cacheScope: "none",
39
38
  pagination: "none",
40
39
  concurrencyRequired: true
41
- }, { path: teamPath, query: empty, body: z.object({ plan: hostedTopologyPlanSchema, credentialLeaseIds }).strict(), output: operationReceipt }),
40
+ }, { path: teamPath, query: empty, body: z.object({ plan: hostedTopologyPlanSchema }).strict(), output: operationReceipt }),
42
41
  status: defineOperation({
43
42
  operationId: "infrastructure.topology.status",
44
43
  description: "Read the latest authoritative hosted topology receipt and operation state.",
@@ -72,8 +71,7 @@ const HOSTED_TOPOLOGY_OPERATIONS = {
72
71
  }, { path: teamPath, query: empty, body: z.object({
73
72
  execution: hostedTopologyRollbackExecutionSchema,
74
73
  sourcePlan: hostedTopologyPlanSchema,
75
- targetPlan: hostedTopologyPlanSchema,
76
- credentialLeaseIds
74
+ targetPlan: hostedTopologyPlanSchema
77
75
  }).strict(), output: operationReceipt })
78
76
  };
79
77
  export {
@@ -0,0 +1,2 @@
1
+ import type { CommandNodeDescriptor } from '../../command-tree.js';
2
+ export declare const managedSecretCommands: CommandNodeDescriptor;
@@ -0,0 +1,31 @@
1
+ const managedSecretCommands = {
2
+ nodeType: "branch",
3
+ segment: "services",
4
+ description: "Team service credentials.",
5
+ children: [{
6
+ nodeType: "branch",
7
+ segment: "credentials",
8
+ description: "Manage credentials in core OpenBao.",
9
+ children: ["show", "put", "delete", "validate"].map((name) => ({
10
+ nodeType: "leaf",
11
+ segment: name,
12
+ description: `${name} an exact service credential profile.`,
13
+ kind: name === "show" ? "read" : "mutation",
14
+ arguments: [{ name: "connection", description: "Service connection ID.", required: true }, { name: "profile", description: "Credential profile ID.", required: true }],
15
+ options: [
16
+ { name: "--team", description: "Authorized team ID.", type: "string" },
17
+ ...name === "show" ? [] : [
18
+ { name: "--plan", description: "Inspect the proposed operation without mutation or secret input.", type: "boolean" },
19
+ { name: "--expected-version", description: "Exact current credential version; zero for first creation.", type: "number", required: true }
20
+ ],
21
+ ...name === "put" ? [{ name: "--stdin", description: "Read the credential field object as JSON from standard input.", type: "boolean" }] : []
22
+ ],
23
+ authorization: { capability: name === "show" ? "secrets.read" : "secrets.write", confirmation: "never" },
24
+ resultSchemaId: `treeseed.services.credentials.${name}/v1`,
25
+ execution: { kind: "local", handlerId: `local.services.credentials.${name}` }
26
+ }))
27
+ }]
28
+ };
29
+ export {
30
+ managedSecretCommands
31
+ };
@@ -0,0 +1,62 @@
1
+ export declare const SECRET_OPERATIONS: {
2
+ readonly credentialStatus: import("../../control-plane-operation.js").ControlPlaneOperationBinding<{
3
+ teamId: string;
4
+ profileId: string;
5
+ connectionId: string;
6
+ }, {}, {
7
+ expectedVersion: number;
8
+ } | undefined, {
9
+ version: number;
10
+ teamId: string;
11
+ profileId: string;
12
+ connectionId: string;
13
+ configured: boolean;
14
+ custody: "openbao";
15
+ fields: string[];
16
+ }>;
17
+ readonly putCredentials: import("../../control-plane-operation.js").ControlPlaneOperationBinding<{
18
+ teamId: string;
19
+ profileId: string;
20
+ connectionId: string;
21
+ }, {}, {
22
+ expectedVersion: number;
23
+ } | undefined, {
24
+ version: number;
25
+ teamId: string;
26
+ profileId: string;
27
+ connectionId: string;
28
+ configured: boolean;
29
+ custody: "openbao";
30
+ fields: string[];
31
+ }>;
32
+ readonly deleteCredentials: import("../../control-plane-operation.js").ControlPlaneOperationBinding<{
33
+ teamId: string;
34
+ profileId: string;
35
+ connectionId: string;
36
+ }, {}, {
37
+ expectedVersion: number;
38
+ } | undefined, {
39
+ version: number;
40
+ teamId: string;
41
+ profileId: string;
42
+ connectionId: string;
43
+ configured: boolean;
44
+ custody: "openbao";
45
+ fields: string[];
46
+ }>;
47
+ readonly validateCredentials: import("../../control-plane-operation.js").ControlPlaneOperationBinding<{
48
+ teamId: string;
49
+ profileId: string;
50
+ connectionId: string;
51
+ }, {}, {
52
+ expectedVersion: number;
53
+ } | undefined, {
54
+ version: number;
55
+ teamId: string;
56
+ profileId: string;
57
+ connectionId: string;
58
+ configured: boolean;
59
+ custody: "openbao";
60
+ fields: string[];
61
+ }>;
62
+ };
@@ -0,0 +1,51 @@
1
+ import { z } from "zod";
2
+ import { defineOperation } from "../../operation-builder.js";
3
+ const id = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/u);
4
+ const path = z.object({ teamId: id, connectionId: id, profileId: id }).strict();
5
+ const version = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER - 1);
6
+ const descriptor = z.object({
7
+ teamId: id,
8
+ connectionId: id,
9
+ profileId: id,
10
+ custody: z.literal("openbao"),
11
+ version,
12
+ configured: z.boolean(),
13
+ fields: z.array(id)
14
+ }).strict();
15
+ const put = z.object({
16
+ expectedVersion: version,
17
+ values: z.record(z.string().max(1024 * 1024)).refine((v) => Object.keys(v).length > 0 && Object.keys(v).length <= 128)
18
+ }).strict();
19
+ function operation(name, method) {
20
+ const read = method === "GET";
21
+ return defineOperation({
22
+ operationId: `services.credentials.${name}`,
23
+ description: `${name} managed service credentials.`,
24
+ rest: { method, path: `/v1/teams/{teamId}/services/{connectionId}/credentials/{profileId}${name === "validate" ? "/validate" : ""}` },
25
+ parameters: `treeseed.services.credentials.${name}.parameters/v1`,
26
+ capability: read ? "secrets.read" : "secrets.write",
27
+ authentication: "oauth",
28
+ oauthScopes: read ? ["treeseed:read"] : ["treeseed:projects:write"],
29
+ kind: read ? "read" : "mutation",
30
+ riskClass: read ? "ordinary" : "credential",
31
+ confirmation: read ? "never" : "input_required",
32
+ surfaces: ["rest"],
33
+ cacheScope: "none",
34
+ pagination: "none",
35
+ redactedPaths: ["body.values"]
36
+ }, {
37
+ path,
38
+ query: z.object({}).strict(),
39
+ body: read ? z.undefined() : name === "put" ? put : z.object({ expectedVersion: version }).strict(),
40
+ output: name === "validate" ? z.object({ ok: z.boolean() }).strict() : descriptor
41
+ });
42
+ }
43
+ const SECRET_OPERATIONS = {
44
+ credentialStatus: operation("show", "GET"),
45
+ putCredentials: operation("put", "PUT"),
46
+ deleteCredentials: operation("delete", "DELETE"),
47
+ validateCredentials: operation("validate", "POST")
48
+ };
49
+ export {
50
+ SECRET_OPERATIONS
51
+ };
@@ -62,7 +62,6 @@ export declare const CONTROL_PLANE_OPERATIONS: {
62
62
  connectionRef: string;
63
63
  }>>;
64
64
  };
65
- credentialLeaseIds?: string[] | undefined;
66
65
  }, Record<string, unknown>>;
67
66
  readonly apply: import("./control-plane-operation.js").ControlPlaneOperationBinding<{
68
67
  teamId: string;
@@ -153,7 +152,6 @@ export declare const CONTROL_PLANE_OPERATIONS: {
153
152
  platformCommit: string;
154
153
  authorization: "authenticated-agent";
155
154
  };
156
- credentialLeaseIds?: string[] | undefined;
157
155
  }, Record<string, unknown>>;
158
156
  readonly status: import("./control-plane-operation.js").ControlPlaneOperationBinding<{
159
157
  teamId: string;
@@ -400,7 +398,6 @@ export declare const CONTROL_PLANE_OPERATIONS: {
400
398
  platformCommit: string;
401
399
  authorization: "authenticated-agent";
402
400
  };
403
- credentialLeaseIds?: string[] | undefined;
404
401
  }, Record<string, unknown>>;
405
402
  };
406
403
  };
@@ -1397,26 +1394,66 @@ export declare const CONTROL_PLANE_OPERATIONS: {
1397
1394
  teamId: string;
1398
1395
  connectionId: string;
1399
1396
  }, {}, Record<string, unknown> | undefined, Record<string, unknown>>;
1400
- readonly putAuthority: import("./control-plane-operation.js").ControlPlaneOperationBinding<{
1397
+ readonly credentialStatus: import("./control-plane-operation.js").ControlPlaneOperationBinding<{
1401
1398
  teamId: string;
1402
1399
  profileId: string;
1403
1400
  connectionId: string;
1404
- }, {}, Record<string, unknown> | undefined, Record<string, unknown>>;
1405
- readonly userVaultKey: import("./control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
1406
- readonly putUserVaultKey: import("./control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
1407
- readonly teamVault: import("./control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
1408
- readonly initializeTeamVault: import("./control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
1409
- readonly resetTeamVault: import("./control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
1410
- readonly rotateTeamVault: import("./control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
1411
- readonly vaultGrantCandidates: import("./control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
1412
- readonly createVaultGrant: import("./control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
1413
- readonly deleteVaultGrant: import("./control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
1414
- readonly vaultCredentialEnvelopes: import("./control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
1415
- readonly credentialEnvelopes: import("./control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
1416
- readonly putCredentialEnvelope: import("./control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
1417
- readonly createOperationLease: import("./control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
1418
- readonly operationLease: import("./control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
1419
- readonly putOperationLeasePayload: import("./control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
1401
+ }, {}, {
1402
+ expectedVersion: number;
1403
+ } | undefined, {
1404
+ version: number;
1405
+ teamId: string;
1406
+ profileId: string;
1407
+ connectionId: string;
1408
+ configured: boolean;
1409
+ custody: "openbao";
1410
+ fields: string[];
1411
+ }>;
1412
+ readonly putCredentials: import("./control-plane-operation.js").ControlPlaneOperationBinding<{
1413
+ teamId: string;
1414
+ profileId: string;
1415
+ connectionId: string;
1416
+ }, {}, {
1417
+ expectedVersion: number;
1418
+ } | undefined, {
1419
+ version: number;
1420
+ teamId: string;
1421
+ profileId: string;
1422
+ connectionId: string;
1423
+ configured: boolean;
1424
+ custody: "openbao";
1425
+ fields: string[];
1426
+ }>;
1427
+ readonly deleteCredentials: import("./control-plane-operation.js").ControlPlaneOperationBinding<{
1428
+ teamId: string;
1429
+ profileId: string;
1430
+ connectionId: string;
1431
+ }, {}, {
1432
+ expectedVersion: number;
1433
+ } | undefined, {
1434
+ version: number;
1435
+ teamId: string;
1436
+ profileId: string;
1437
+ connectionId: string;
1438
+ configured: boolean;
1439
+ custody: "openbao";
1440
+ fields: string[];
1441
+ }>;
1442
+ readonly validateCredentials: import("./control-plane-operation.js").ControlPlaneOperationBinding<{
1443
+ teamId: string;
1444
+ profileId: string;
1445
+ connectionId: string;
1446
+ }, {}, {
1447
+ expectedVersion: number;
1448
+ } | undefined, {
1449
+ version: number;
1450
+ teamId: string;
1451
+ profileId: string;
1452
+ connectionId: string;
1453
+ configured: boolean;
1454
+ custody: "openbao";
1455
+ fields: string[];
1456
+ }>;
1420
1457
  };
1421
1458
  readonly agents: {
1422
1459
  readonly list: import("./control-plane-operation.js").ControlPlaneOperationBinding<{
@@ -8,7 +8,7 @@ import { capabilityOntologyOperations } from "./catalog/capability-ontology-oper
8
8
  import { knowledgeShareOperations } from "./catalog/knowledge-share-operations.js";
9
9
  import { PROVIDER_ENVIRONMENT_OPERATIONS } from "./catalog/provider-environment-operations.js";
10
10
  import { HOSTED_TOPOLOGY_OPERATIONS } from "./catalog/hosted-topology-operations.js";
11
- import { SERVICE_VAULT_OPERATIONS } from "./catalog/services/service-vault-operations.js";
11
+ import { SECRET_OPERATIONS } from "./catalog/services/secret-operations.js";
12
12
  import { buildControlPlaneCatalog, flattenControlPlaneOperations } from "./catalog/control-plane-catalog.js";
13
13
  const empty = z.object({}).strict(), none = z.undefined(), record = z.record(z.unknown()), payload = record;
14
14
  function read(operationId, path, capability, surfaces = ["rest"]) {
@@ -278,15 +278,14 @@ const CONTROL_PLANE_OPERATIONS = {
278
278
  deleteWorkflowVariable: resource("repositories.workflow.variables.delete", "DELETE", "/v1/projects/{projectId}/workflow-configuration/variables/{name}", { projectId: z.string().min(1), name: z.string().min(1) }, { capability: "workflows.write", risk: "destructive", concurrency: true })
279
279
  },
280
280
  services: {
281
- ...SERVICE_VAULT_OPERATIONS,
281
+ ...SECRET_OPERATIONS,
282
282
  providers: resource("services.providers.list", "GET", "/v1/service-providers", {}, { capability: "services.read", surfaces: ["rest", "cli", "mcp_tool"] }),
283
283
  connections: resource("services.connections.list", "GET", "/v1/teams/{teamId}/services", { teamId: z.string().min(1) }, { capability: "services.read", surfaces: ["rest", "cli", "mcp_tool"], pagination: "cursor" }),
284
284
  connection: resource("services.connections.show", "GET", "/v1/teams/{teamId}/services/{connectionId}", { teamId: z.string().min(1), connectionId: z.string().min(1) }, { capability: "services.read", surfaces: ["rest", "cli", "mcp_resource"] }),
285
285
  createConnection: resource("services.connections.create", "POST", "/v1/teams/{teamId}/services", { teamId: z.string().min(1) }, { capability: "services.write", surfaces: ["rest", "cli", "mcp_tool"], risk: "credential" }),
286
286
  updateConnection: resource("services.connections.update", "PUT", "/v1/teams/{teamId}/services/{connectionId}", { teamId: z.string().min(1), connectionId: z.string().min(1) }, { capability: "services.write", surfaces: ["rest", "cli", "mcp_tool"], concurrency: true }),
287
287
  disconnect: resource("services.connections.disconnect", "DELETE", "/v1/teams/{teamId}/services/{connectionId}", { teamId: z.string().min(1), connectionId: z.string().min(1) }, { capability: "services.write", surfaces: ["rest", "cli", "mcp_tool"], risk: "destructive", concurrency: true }),
288
- authorities: resource("services.credential.authorities.list", "GET", "/v1/teams/{teamId}/services/{connectionId}/credential-authorities", { teamId: z.string().min(1), connectionId: z.string().min(1) }, { capability: "secrets.read", surfaces: ["rest", "cli"], pagination: "cursor" }),
289
- putAuthority: resource("services.credential.authorities.put", "PUT", "/v1/teams/{teamId}/services/{connectionId}/credential-authorities/{profileId}", { teamId: z.string().min(1), connectionId: z.string().min(1), profileId: z.string().min(1) }, { capability: "secrets.write", surfaces: ["rest", "cli"], risk: "credential", concurrency: true, redactedPaths: ["body"] })
288
+ authorities: resource("services.credential.authorities.list", "GET", "/v1/teams/{teamId}/services/{connectionId}/credential-authorities", { teamId: z.string().min(1), connectionId: z.string().min(1) }, { capability: "secrets.read", surfaces: ["rest", "cli"], pagination: "cursor" })
290
289
  },
291
290
  agents: {
292
291
  list: resource("agents.list", "GET", "/v1/projects/{projectId}/agents", { projectId: z.string().min(1) }, { capability: "agents.read", surfaces: ["rest", "cli", "mcp_tool"], pagination: "cursor" }),
@@ -0,0 +1,2 @@
1
+ /** GitHub's upload protocol, not a TreeSeed custody implementation. */
2
+ export declare function encryptGitHubActionsSecret(value: string, providerPublicKey: string): Promise<string>;
@@ -0,0 +1,11 @@
1
+ import sodium from "libsodium-wrappers-sumo";
2
+ async function encryptGitHubActionsSecret(value, providerPublicKey) {
3
+ await sodium.ready;
4
+ if (!value) throw new Error("GitHub Actions secret value is required.");
5
+ const key = sodium.from_base64(providerPublicKey, sodium.base64_variants.ORIGINAL);
6
+ if (key.length !== sodium.crypto_box_PUBLICKEYBYTES) throw new Error("Invalid GitHub public key.");
7
+ return sodium.to_base64(sodium.crypto_box_seal(value, key), sodium.base64_variants.ORIGINAL);
8
+ }
9
+ export {
10
+ encryptGitHubActionsSecret
11
+ };
@@ -9,7 +9,7 @@ export type ProviderCredentialAuthority = {
9
9
  scheme: CredentialAuthorityScheme;
10
10
  reference: string;
11
11
  capabilities: ServiceCapabilityType[];
12
- status: 'ready' | 'interactive-only' | 'reauthorization-required' | 'revoked';
12
+ status: 'ready' | 'reauthorization-required' | 'revoked';
13
13
  version: number;
14
14
  };
15
15
  export type ProjectRemoteRepositoryBinding = {
@@ -0,0 +1,33 @@
1
+ import { z } from 'zod';
2
+ export declare const secretScopeSchema: z.ZodObject<{
3
+ team: z.ZodString;
4
+ project: z.ZodString;
5
+ environment: z.ZodString;
6
+ purpose: z.ZodString;
7
+ name: z.ZodString;
8
+ }, "strict", z.ZodTypeAny, {
9
+ project: string;
10
+ name: string;
11
+ team: string;
12
+ purpose: string;
13
+ environment: string;
14
+ }, {
15
+ project: string;
16
+ name: string;
17
+ team: string;
18
+ purpose: string;
19
+ environment: string;
20
+ }>;
21
+ export type SecretScope = z.infer<typeof secretScopeSchema>;
22
+ export declare const SECRET_CUSTODY_BACKENDS: readonly ["openbao", "os"];
23
+ export declare function canonicalSecretPath(input: SecretScope): string;
24
+ export type HostedSecretOperationBinding = {
25
+ subjectType: 'declaration' | 'plan' | 'rollback';
26
+ subjectDigest: string;
27
+ deploymentId: string;
28
+ stackId: string;
29
+ environment: 'staging' | 'production';
30
+ };
31
+ export declare function containsForbiddenPlaintextSecretMaterial(value: unknown, path?: string): string[];
32
+ export declare function canonicalHostedSecretOperationBinding(input: HostedSecretOperationBinding): string;
33
+ export declare function validateHostedSecretOperationBinding(value: unknown): value is HostedSecretOperationBinding;
@@ -0,0 +1,49 @@
1
+ import { z } from "zod";
2
+ const segment = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/u);
3
+ const secretScopeSchema = z.object({
4
+ team: segment,
5
+ project: segment,
6
+ environment: segment,
7
+ purpose: segment,
8
+ name: segment
9
+ }).strict();
10
+ const SECRET_CUSTODY_BACKENDS = ["openbao", "os"];
11
+ function canonicalSecretPath(input) {
12
+ const s = secretScopeSchema.parse(input);
13
+ return `teams/${s.team}/projects/${s.project}/environments/${s.environment}/purposes/${s.purpose}/secrets/${s.name}`;
14
+ }
15
+ const FORBIDDEN_SECRET_KEYS = /(?:passphrase|password|plaintext|derivedKey|privateKey|apiToken|accessToken|secretValue|credentialValue)$/iu;
16
+ function containsForbiddenPlaintextSecretMaterial(value, path = "") {
17
+ if (!value || typeof value !== "object") return [];
18
+ const failures = [];
19
+ for (const [key, entry] of Object.entries(value)) {
20
+ const nextPath = path ? `${path}.${key}` : key;
21
+ if (FORBIDDEN_SECRET_KEYS.test(key) && typeof entry === "string" && entry.trim()) failures.push(nextPath);
22
+ if (entry && typeof entry === "object") failures.push(...containsForbiddenPlaintextSecretMaterial(entry, nextPath));
23
+ }
24
+ return failures;
25
+ }
26
+ const digest = /^sha256:[a-f0-9]{64}$/u;
27
+ const identifier = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;
28
+ function canonicalHostedSecretOperationBinding(input) {
29
+ return JSON.stringify({
30
+ subjectType: input.subjectType,
31
+ subjectDigest: input.subjectDigest,
32
+ deploymentId: input.deploymentId,
33
+ stackId: input.stackId,
34
+ environment: input.environment
35
+ });
36
+ }
37
+ function validateHostedSecretOperationBinding(value) {
38
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
39
+ const binding = value;
40
+ return ["declaration", "plan", "rollback"].includes(String(binding.subjectType)) && digest.test(String(binding.subjectDigest)) && identifier.test(String(binding.deploymentId)) && identifier.test(String(binding.stackId)) && ["staging", "production"].includes(String(binding.environment));
41
+ }
42
+ export {
43
+ SECRET_CUSTODY_BACKENDS,
44
+ canonicalHostedSecretOperationBinding,
45
+ canonicalSecretPath,
46
+ containsForbiddenPlaintextSecretMaterial,
47
+ secretScopeSchema,
48
+ validateHostedSecretOperationBinding
49
+ };
@@ -1,4 +1,4 @@
1
- export declare const SERVICE_PROVIDER_IDS: readonly ["github", "cloudflare", "railway", "openbao"];
1
+ export declare const SERVICE_PROVIDER_IDS: readonly ["github", "cloudflare", "railway"];
2
2
  export declare const SERVICE_CAPABILITY_TYPES: readonly ["repository-hosting", "workflow-execution", "workflow-configuration", "secret-enclave", "frontend-hosting", "backend-hosting", "dns-management", "object-storage", "state-encryption", "database-hosting", "capacity-runtime-hosting", "private-knowledge-index-hosting", "artifact-hosting"];
3
3
  export declare const SERVICE_CONNECTION_STATUSES: readonly ["draft", "active", "degraded", "validation-failed", "reentry-required", "reauthorization-required", "disconnected"];
4
4
  export declare const SERVICE_CAPABILITY_STATUSES: readonly ["configured", "disabled", "blocked", "planned"];
@@ -27,7 +27,7 @@ export type CredentialProfileDefinition = {
27
27
  authoritySchemes?: CredentialAuthorityScheme[];
28
28
  knowledgePageIds: string[];
29
29
  };
30
- export declare const CREDENTIAL_AUTHORITY_SCHEMES: readonly ["app-installation", "api-token", "oauth-token", "environment-reference", "client-encrypted", "external-vault", "workload-identity"];
30
+ export declare const CREDENTIAL_AUTHORITY_SCHEMES: readonly ["app-installation", "openbao"];
31
31
  export type CredentialAuthorityScheme = (typeof CREDENTIAL_AUTHORITY_SCHEMES)[number];
32
32
  export type ServiceCapabilityDefinition = {
33
33
  type: ServiceCapabilityType;
@@ -1,4 +1,4 @@
1
- const SERVICE_PROVIDER_IDS = ["github", "cloudflare", "railway", "openbao"];
1
+ const SERVICE_PROVIDER_IDS = ["github", "cloudflare", "railway"];
2
2
  const SERVICE_CAPABILITY_TYPES = [
3
3
  "repository-hosting",
4
4
  "workflow-execution",
@@ -26,12 +26,7 @@ const SERVICE_CONNECTION_STATUSES = [
26
26
  const SERVICE_CAPABILITY_STATUSES = ["configured", "disabled", "blocked", "planned"];
27
27
  const CREDENTIAL_AUTHORITY_SCHEMES = [
28
28
  "app-installation",
29
- "api-token",
30
- "oauth-token",
31
- "environment-reference",
32
- "client-encrypted",
33
- "external-vault",
34
- "workload-identity"
29
+ "openbao"
35
30
  ];
36
31
  const field = (key, label, description, required = true, sensitive = false) => ({
37
32
  key,
@@ -76,11 +71,11 @@ const SERVICE_PROVIDER_CATALOG = [
76
71
  label: "Repository token authority",
77
72
  description: "Fine-grained token authority restricted to selected repositories and Contents access.",
78
73
  capabilities: ["repository-hosting"],
79
- fields: [field("accessToken", "Fine-grained token", "Encrypted before leaving this browser.", true, true)],
74
+ fields: [field("accessToken", "Fine-grained token", "Stored in core OpenBao; used only by authorized operations.", true, true)],
80
75
  permissions: ["Metadata: read", "Contents: read and write"],
81
76
  sharing: "capability-scoped",
82
- unattendedCompatible: false,
83
- authoritySchemes: ["api-token", "environment-reference", "client-encrypted", "external-vault"],
77
+ unattendedCompatible: true,
78
+ authoritySchemes: ["openbao"],
84
79
  knowledgePageIds: ["provider.github", "services.credentials", "vault.rotation"]
85
80
  },
86
81
  {
@@ -100,11 +95,11 @@ const SERVICE_PROVIDER_CATALOG = [
100
95
  label: "Workflow token authority",
101
96
  description: "Fine-grained token authority for Actions and explicitly enabled secret or variable scopes.",
102
97
  capabilities: ["workflow-execution", "workflow-configuration", "secret-enclave"],
103
- fields: [field("accessToken", "Fine-grained token", "Encrypted before leaving this browser.", true, true)],
98
+ fields: [field("accessToken", "Fine-grained token", "Stored in core OpenBao; used only by authorized operations.", true, true)],
104
99
  permissions: ["Metadata: read", "Contents: read", "Actions: read and write", "Secrets: read and write", "Variables: read and write"],
105
100
  sharing: "capability-scoped",
106
- unattendedCompatible: false,
107
- authoritySchemes: ["api-token", "environment-reference", "client-encrypted", "external-vault"],
101
+ unattendedCompatible: true,
102
+ authoritySchemes: ["openbao"],
108
103
  knowledgePageIds: ["provider.github", "services.credentials", "vault.rotation"]
109
104
  }
110
105
  ]
@@ -132,11 +127,11 @@ const SERVICE_PROVIDER_CATALOG = [
132
127
  { type: "state-encryption", label: "State encryption", description: "Independent encryption authority for OpenTofu state and plan files.", credentialProfileIds: ["opentofu-state-encryption"], status: "available" }
133
128
  ],
134
129
  credentialProfiles: [
135
- { id: "cloudflare-runtime", label: "Pages and Workers token", description: "A token limited to application deployment resources.", capabilities: ["frontend-hosting"], fields: [field("apiToken", "API token", "Encrypted in this browser and used only for authorized frontend operations.", true, true)], permissions: ["Account: Workers Scripts Edit", "Account: Pages Edit"], sharing: "capability-scoped", unattendedCompatible: true, authoritySchemes: ["environment-reference", "client-encrypted", "external-vault", "workload-identity"], knowledgePageIds: ["provider.cloudflare", "services.credentials", "vault.rotation"] },
136
- { id: "cloudflare-dns", label: "DNS token", description: "A token restricted to selected zones.", capabilities: ["dns-management"], fields: [field("apiToken", "DNS API token", "Encrypted separately from deployment authority.", true, true)], permissions: ["Zone: DNS Edit for only the managed zones"], sharing: "capability-scoped", unattendedCompatible: true, authoritySchemes: ["environment-reference", "client-encrypted", "external-vault", "workload-identity"], knowledgePageIds: ["provider.cloudflare", "services.credentials", "vault.rotation"] },
137
- { id: "cloudflare-storage", label: "Storage management authority", description: "Vault-custodied token used only to reconcile authorized R2 resources.", capabilities: ["object-storage"], fields: [field("apiToken", "Storage API token", "Encrypted separately and used only to reconcile authorized R2 resources.", true, true)], permissions: ["Account: Workers R2 Storage Edit"], sharing: "capability-scoped", unattendedCompatible: true, authoritySchemes: ["environment-reference", "client-encrypted", "external-vault", "workload-identity"], knowledgePageIds: ["provider.cloudflare", "services.credentials", "vault.rotation"] },
138
- { id: "s3-state-session", label: "OpenTofu state session", description: "S3-compatible credentials limited to the selected team state prefix.", capabilities: ["object-storage"], fields: [field("accessKeyId", "R2 access key ID", "Vault-custodied S3-compatible access key identifier for encrypted OpenTofu state.", true, true), field("secretAccessKey", "R2 secret access key", "Vault-custodied S3-compatible secret key for encrypted OpenTofu state.", true, true), field("sessionToken", "R2 session token", "Optional short-lived S3-compatible session token.", false, true)], permissions: ["Object read and write for only the managed team state prefix"], sharing: "capability-scoped", unattendedCompatible: true, authoritySchemes: ["environment-reference", "client-encrypted", "external-vault", "workload-identity"], knowledgePageIds: ["provider.cloudflare", "services.credentials", "vault.rotation"] },
139
- { id: "opentofu-state-encryption", label: "OpenTofu state encryption", description: "Independent encryption material that is never stored with the R2 state object.", capabilities: ["state-encryption"], fields: [field("stateEncryptionKey", "OpenTofu state encryption key", "Generated in the browser as a 32-byte hexadecimal key and encrypted separately.", true, true)], permissions: ["Encrypt and decrypt only the selected team OpenTofu state"], sharing: "capability-scoped", unattendedCompatible: true, authoritySchemes: ["environment-reference", "client-encrypted", "external-vault", "workload-identity"], knowledgePageIds: ["provider.cloudflare", "services.credentials", "vault.rotation"] }
130
+ { id: "cloudflare-runtime", label: "Pages and Workers token", description: "A token limited to application deployment resources.", capabilities: ["frontend-hosting"], fields: [field("apiToken", "API token", "Stored in core OpenBao and used only by authorized operations.", true, true)], permissions: ["Account: Workers Scripts Edit", "Account: Pages Edit"], sharing: "capability-scoped", unattendedCompatible: true, authoritySchemes: ["openbao"], knowledgePageIds: ["provider.cloudflare", "services.credentials", "vault.rotation"] },
131
+ { id: "cloudflare-dns", label: "DNS token", description: "A token restricted to selected zones.", capabilities: ["dns-management"], fields: [field("apiToken", "DNS API token", "Encrypted separately from deployment authority.", true, true)], permissions: ["Zone: DNS Edit for only the managed zones"], sharing: "capability-scoped", unattendedCompatible: true, authoritySchemes: ["openbao"], knowledgePageIds: ["provider.cloudflare", "services.credentials", "vault.rotation"] },
132
+ { id: "cloudflare-storage", label: "Storage management authority", description: "Vault-custodied token used only to reconcile authorized R2 resources.", capabilities: ["object-storage"], fields: [field("apiToken", "Storage API token", "Encrypted separately and used only to reconcile authorized R2 resources.", true, true)], permissions: ["Account: Workers R2 Storage Edit"], sharing: "capability-scoped", unattendedCompatible: true, authoritySchemes: ["openbao"], knowledgePageIds: ["provider.cloudflare", "services.credentials", "vault.rotation"] },
133
+ { id: "s3-state-session", label: "OpenTofu state session", description: "S3-compatible credentials limited to the selected team state prefix.", capabilities: ["object-storage"], fields: [field("accessKeyId", "R2 access key ID", "Vault-custodied S3-compatible access key identifier for encrypted OpenTofu state.", true, true), field("secretAccessKey", "R2 secret access key", "Vault-custodied S3-compatible secret key for encrypted OpenTofu state.", true, true), field("sessionToken", "R2 session token", "Optional short-lived S3-compatible session token.", false, true)], permissions: ["Object read and write for only the managed team state prefix"], sharing: "capability-scoped", unattendedCompatible: true, authoritySchemes: ["openbao"], knowledgePageIds: ["provider.cloudflare", "services.credentials", "vault.rotation"] },
134
+ { id: "opentofu-state-encryption", label: "OpenTofu state encryption", description: "Independent encryption material that is never stored with the R2 state object.", capabilities: ["state-encryption"], fields: [field("stateEncryptionKey", "OpenTofu state encryption key", "A 32-byte hexadecimal key stored separately in core OpenBao.", true, true)], permissions: ["Encrypt and decrypt only the selected team OpenTofu state"], sharing: "capability-scoped", unattendedCompatible: true, authoritySchemes: ["openbao"], knowledgePageIds: ["provider.cloudflare", "services.credentials", "vault.rotation"] }
140
135
  ]
141
136
  },
142
137
  {
@@ -163,24 +158,13 @@ const SERVICE_PROVIDER_CATALOG = [
163
158
  label: "Railway workspace token",
164
159
  description: "Railway currently exposes broad workspace authority. Sharing it increases the blast radius across enabled capabilities.",
165
160
  capabilities: ["backend-hosting", "database-hosting", "capacity-runtime-hosting", "private-knowledge-index-hosting"],
166
- fields: [field("apiToken", "Workspace token", "Encrypted in this browser. TreeSeed will request explicit authorization before interactive use.", true, true)],
161
+ fields: [field("apiToken", "Workspace token", "Stored in core OpenBao and used only by authorized operations.", true, true)],
167
162
  permissions: ["Workspace access required by the selected operations"],
168
163
  sharing: "provider-shared",
169
164
  unattendedCompatible: true,
170
- authoritySchemes: ["environment-reference", "client-encrypted", "external-vault", "workload-identity"],
165
+ authoritySchemes: ["openbao"],
171
166
  knowledgePageIds: ["provider.railway", "services.credentials", "vault.rotation"]
172
167
  }]
173
- },
174
- {
175
- id: "openbao",
176
- label: "OpenBao / HashiCorp Vault",
177
- logoKey: "vault",
178
- documentationUrl: "https://openbao.org/docs/auth/jwt/",
179
- description: "Reference an external vault through workload identity; no long-lived vault token is stored.",
180
- knowledgePageIds: ["provider.openbao", "services.external-vault"],
181
- connectionFields: [field("address", "Vault address", "The HTTPS endpoint for the vault."), field("mount", "Secrets mount", "The mount containing TreeSeed-managed references."), field("role", "Workload identity role", "The OIDC/JWT role used by the operations runner.")],
182
- capabilities: [{ type: "secret-enclave", label: "External secret vault", description: "Resolve approved secret references through workload identity.", credentialProfileIds: [], status: "available" }],
183
- credentialProfiles: []
184
168
  }
185
169
  ];
186
170
  function getServiceProviderDefinition(providerId) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treeseed/sdk",
3
- "version": "0.13.0-rc.86",
3
+ "version": "0.13.0-rc.88",
4
4
  "description": "Portable TreeSeed contracts, standards, and typed remote control-plane clients.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -1,17 +0,0 @@
1
- export declare const SERVICE_VAULT_OPERATIONS: {
2
- readonly userVaultKey: import("../../control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
3
- readonly putUserVaultKey: import("../../control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
4
- readonly teamVault: import("../../control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
5
- readonly initializeTeamVault: import("../../control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
6
- readonly resetTeamVault: import("../../control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
7
- readonly rotateTeamVault: import("../../control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
8
- readonly vaultGrantCandidates: import("../../control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
9
- readonly createVaultGrant: import("../../control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
10
- readonly deleteVaultGrant: import("../../control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
11
- readonly vaultCredentialEnvelopes: import("../../control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
12
- readonly credentialEnvelopes: import("../../control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
13
- readonly putCredentialEnvelope: import("../../control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
14
- readonly createOperationLease: import("../../control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
15
- readonly operationLease: import("../../control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
16
- readonly putOperationLeasePayload: import("../../control-plane-operation.js").ControlPlaneOperationBinding<any, {}, any, any>;
17
- };
@@ -1,158 +0,0 @@
1
- import { z } from "zod";
2
- import { defineOperation } from "../../operation-builder.js";
3
- const empty = z.object({}).strict();
4
- const none = z.undefined();
5
- const record = z.record(z.unknown());
6
- const teamPath = z.object({ teamId: z.string().min(1) }).strict();
7
- function vaultOperation(operationId, method, path, pathSchema, options = {}) {
8
- const read = method === "GET", risk = options.risk ?? "ordinary";
9
- return defineOperation({
10
- operationId,
11
- description: `${read ? "Read" : "Apply"} ${operationId}.`,
12
- rest: { method, path },
13
- capability: read ? "secrets.read" : "secrets.write",
14
- ...path.includes("{") ? { parameters: `treeseed.${operationId}.parameters/v1` } : {},
15
- authentication: "oauth",
16
- oauthScopes: read ? ["treeseed:read"] : ["treeseed:projects:write"],
17
- kind: read ? "read" : "mutation",
18
- riskClass: risk,
19
- confirmation: risk === "ordinary" ? "never" : "input_required",
20
- surfaces: ["rest"],
21
- cacheScope: read ? "principal" : "none",
22
- pagination: "none",
23
- redactedPaths: options.redactedPaths
24
- }, { path: pathSchema, query: read ? record : empty, body: read ? none : options.body ?? record, output: options.output ?? record });
25
- }
26
- const nullableRecord = record.nullable(), records = z.array(record);
27
- const encryptedPrivateKeyEnvelope = z.object({
28
- version: z.literal("service-vault-v1"),
29
- algorithm: z.literal("xchacha20-poly1305-ietf"),
30
- kdf: z.object({ algorithm: z.literal("argon2id"), opsLimit: z.number().int().positive(), memLimit: z.number().int().positive(), salt: z.string().min(1) }).strict(),
31
- nonce: z.string().min(1),
32
- ciphertext: z.string().min(1),
33
- publicKey: z.string().min(1)
34
- }).strict();
35
- const encryptedCredentialEnvelope = z.object({
36
- version: z.literal("service-vault-v1"),
37
- algorithm: z.literal("xchacha20-poly1305-ietf"),
38
- ciphertext: z.string().min(1),
39
- nonce: z.string().min(1),
40
- wrappedKey: z.string().min(1),
41
- wrappedKeyNonce: z.string().min(1),
42
- associatedData: z.string().min(1),
43
- associatedDataDigest: z.string().min(1),
44
- fingerprint: z.string().min(1)
45
- }).strict();
46
- const SERVICE_VAULT_OPERATIONS = {
47
- userVaultKey: vaultOperation("services.vault.user.key.show", "GET", "/v1/users/me/vault-key", empty, { output: nullableRecord }),
48
- putUserVaultKey: vaultOperation(
49
- "services.vault.user.key.put",
50
- "PUT",
51
- "/v1/users/me/vault-key",
52
- empty,
53
- { risk: "credential", redactedPaths: ["body.encryptedPrivateKeyEnvelope"], body: z.object({ publicKey: z.string().min(1), encryptedPrivateKeyEnvelope }).strict() }
54
- ),
55
- teamVault: vaultOperation("services.vault.team.show", "GET", "/v1/teams/{teamId}/vault", teamPath, { output: nullableRecord }),
56
- initializeTeamVault: vaultOperation(
57
- "services.vault.team.initialize",
58
- "POST",
59
- "/v1/teams/{teamId}/vault",
60
- teamPath,
61
- { risk: "credential", redactedPaths: ["body.wrappedTeamVaultKey"], body: z.object({ userVaultKeyId: z.string().min(1), wrappedTeamVaultKey: z.string().min(1), encryptionVersion: z.literal("service-vault-v1") }).strict() }
62
- ),
63
- resetTeamVault: vaultOperation(
64
- "services.vault.team.reset",
65
- "POST",
66
- "/v1/teams/{teamId}/vault/reset",
67
- teamPath,
68
- { risk: "destructive", redactedPaths: ["body.wrappedTeamVaultKey", "body.currentPassword"], body: z.object({ userVaultKeyId: z.string().min(1), wrappedTeamVaultKey: z.string().min(1), encryptionVersion: z.literal("service-vault-v1"), confirmation: z.string().min(1), currentPassword: z.string().min(1) }).strict() }
69
- ),
70
- rotateTeamVault: vaultOperation(
71
- "services.vault.team.rotate",
72
- "POST",
73
- "/v1/teams/{teamId}/vault/rotate",
74
- teamPath,
75
- { risk: "credential", redactedPaths: ["body.envelopes", "body.grants"], body: z.object({
76
- expectedKeyVersion: z.number().int().positive(),
77
- envelopes: z.array(z.object({ id: z.string().min(1), envelope: encryptedCredentialEnvelope }).strict()),
78
- grants: z.array(z.object({ userId: z.string().min(1), userVaultKeyId: z.string().min(1), wrappedTeamVaultKey: z.string().min(1) }).strict()).min(1)
79
- }).strict() }
80
- ),
81
- vaultGrantCandidates: vaultOperation("services.vault.grant.candidates.list", "GET", "/v1/teams/{teamId}/vault/grant-candidates", teamPath, { output: records }),
82
- createVaultGrant: vaultOperation(
83
- "services.vault.grants.create",
84
- "POST",
85
- "/v1/teams/{teamId}/vault/grants",
86
- teamPath,
87
- { risk: "credential", redactedPaths: ["body.wrappedTeamVaultKey"], body: z.object({ userId: z.string().min(1), userVaultKeyId: z.string().min(1), wrappedTeamVaultKey: z.string().min(1) }).strict() }
88
- ),
89
- deleteVaultGrant: vaultOperation(
90
- "services.vault.grants.delete",
91
- "DELETE",
92
- "/v1/teams/{teamId}/vault/grants/{grantId}",
93
- z.object({ teamId: z.string().min(1), grantId: z.string().min(1) }).strict(),
94
- { risk: "destructive" }
95
- ),
96
- vaultCredentialEnvelopes: vaultOperation(
97
- "services.vault.credential.envelopes.list",
98
- "GET",
99
- "/v1/teams/{teamId}/vault/credential-envelopes",
100
- teamPath,
101
- { output: records }
102
- ),
103
- credentialEnvelopes: vaultOperation(
104
- "services.credential.envelopes.list",
105
- "GET",
106
- "/v1/teams/{teamId}/services/{connectionId}/credential-envelopes",
107
- z.object({ teamId: z.string().min(1), connectionId: z.string().min(1) }).strict(),
108
- { output: records }
109
- ),
110
- putCredentialEnvelope: vaultOperation(
111
- "services.credential.envelopes.put",
112
- "POST",
113
- "/v1/teams/{teamId}/services/{connectionId}/credential-envelopes",
114
- z.object({ teamId: z.string().min(1), connectionId: z.string().min(1) }).strict(),
115
- { risk: "credential", redactedPaths: ["body.envelope"], body: z.object({ definitionId: z.string().min(1), fieldKey: z.string().min(1), keyVersion: z.number().int().positive(), envelope: encryptedCredentialEnvelope }).strict() }
116
- ),
117
- createOperationLease: vaultOperation(
118
- "services.operation.leases.create",
119
- "POST",
120
- "/v1/teams/{teamId}/service-operation-leases",
121
- teamPath,
122
- { risk: "authority", body: z.object({
123
- connectionId: z.string().min(1),
124
- capabilityType: z.string().min(1),
125
- credentialProfileId: z.string().min(1),
126
- purpose: z.enum([
127
- "provider-connection-validation",
128
- "remote-git-publication",
129
- "workflow-dispatch",
130
- "workflow-configuration",
131
- "hosted-topology-plan",
132
- "hosted-topology-apply",
133
- "hosted-topology-readback",
134
- "hosted-topology-rollback"
135
- ]),
136
- idempotencyKey: z.string().min(1).optional(),
137
- resourceScope: record.optional(),
138
- hostedBinding: record.optional(),
139
- authorityRequests: z.array(record).optional()
140
- }).strict() }
141
- ),
142
- operationLease: vaultOperation(
143
- "services.operation.leases.show",
144
- "GET",
145
- "/v1/teams/{teamId}/service-operation-leases/{leaseId}",
146
- z.object({ teamId: z.string().min(1), leaseId: z.string().min(1) }).strict()
147
- ),
148
- putOperationLeasePayload: vaultOperation(
149
- "services.operation.leases.payload.put",
150
- "PUT",
151
- "/v1/teams/{teamId}/service-operation-leases/{leaseId}/payload",
152
- z.object({ teamId: z.string().min(1), leaseId: z.string().min(1) }).strict(),
153
- { risk: "credential", redactedPaths: ["body.sealedPayload"], body: z.object({ sealedPayload: z.string().min(1) }).strict() }
154
- )
155
- };
156
- export {
157
- SERVICE_VAULT_OPERATIONS
158
- };
@@ -1,108 +0,0 @@
1
- export declare const SERVICE_VAULT_ENCRYPTION_VERSION: "service-vault-v1";
2
- export declare const SERVICE_VAULT_AEAD_ALGORITHM: "xchacha20-poly1305-ietf";
3
- export declare const SERVICE_VAULT_KEY_AGREEMENT: "x25519-sealed-box";
4
- export declare const SERVICE_VAULT_KDF: "argon2id";
5
- export declare const SECRET_OPERATION_PURPOSES: readonly ["provider-connection-validation", "remote-git-publication", "workflow-dispatch", "workflow-configuration", "hosted-topology-plan", "hosted-topology-apply", "hosted-topology-readback", "hosted-topology-rollback"];
6
- export type SecretOperationPurpose = (typeof SECRET_OPERATION_PURPOSES)[number];
7
- export type HostedSecretOperationPurpose = Extract<SecretOperationPurpose, `hosted-topology-${string}`>;
8
- export type EncryptedCredentialEnvelope = {
9
- version: typeof SERVICE_VAULT_ENCRYPTION_VERSION;
10
- algorithm: typeof SERVICE_VAULT_AEAD_ALGORITHM;
11
- ciphertext: string;
12
- nonce: string;
13
- wrappedKey: string;
14
- wrappedKeyNonce: string;
15
- associatedData: string;
16
- associatedDataDigest: string;
17
- fingerprint: string;
18
- };
19
- export type EncryptedPrivateKeyEnvelope = {
20
- version: typeof SERVICE_VAULT_ENCRYPTION_VERSION;
21
- algorithm: typeof SERVICE_VAULT_AEAD_ALGORITHM;
22
- kdf: {
23
- algorithm: typeof SERVICE_VAULT_KDF;
24
- opsLimit: number;
25
- memLimit: number;
26
- salt: string;
27
- };
28
- nonce: string;
29
- ciphertext: string;
30
- publicKey: string;
31
- };
32
- export type TeamVaultGrantEnvelope = {
33
- version: typeof SERVICE_VAULT_ENCRYPTION_VERSION;
34
- algorithm: typeof SERVICE_VAULT_KEY_AGREEMENT;
35
- recipientPublicKey: string;
36
- wrappedTeamVaultKey: string;
37
- };
38
- export type SecretOperationLease = {
39
- id: string;
40
- teamId: string;
41
- connectionId: string;
42
- capabilityType: string;
43
- purpose: SecretOperationPurpose;
44
- resourceScope: Record<string, string>;
45
- credentialProfileId: string;
46
- actorUserId: string;
47
- requiredFields: string[];
48
- publicKey: string;
49
- status: 'awaiting-runner' | 'pending' | 'ready' | 'consumed' | 'expired' | 'cancelled' | 'failed';
50
- expiresAt: string;
51
- consumedAt?: string | null;
52
- operationCorrelationId: string;
53
- hostedBinding?: HostedSecretOperationBinding;
54
- authorityRequests?: SecretOperationAuthorityRequest[];
55
- };
56
- export type HostedSecretOperationBinding = {
57
- subjectType: 'declaration' | 'plan' | 'rollback';
58
- subjectDigest: string;
59
- deploymentId: string;
60
- stackId: string;
61
- environment: 'staging' | 'production';
62
- };
63
- export type SecretOperationAuthorityRequest = {
64
- requestId: string;
65
- connectionId: string;
66
- credentialProfileId: string;
67
- provider: 'cloudflare' | 'railway' | 'treeseed';
68
- purpose: 'provider' | 'state-backend' | 'state-encryption';
69
- capabilities: string[];
70
- requiredFields: string[];
71
- secretRef?: string;
72
- };
73
- export type SealedSecretOperationPayload = {
74
- schemaVersion: 'treeseed.sealed-secret-operation-payload/v1';
75
- leaseId: string;
76
- teamId: string;
77
- operationCorrelationId: string;
78
- hostedBinding: HostedSecretOperationBinding;
79
- algorithm: typeof SERVICE_VAULT_KEY_AGREEMENT;
80
- ciphertext: string;
81
- };
82
- export type SecretOperationCredentialBundle = {
83
- schemaVersion: 'treeseed.secret-operation-credential-bundle/v1';
84
- leaseId: string;
85
- teamId: string;
86
- operationCorrelationId: string;
87
- hostedBinding: HostedSecretOperationBinding;
88
- materials: Array<{
89
- requestId: string;
90
- connectionId: string;
91
- credentialProfileId: string;
92
- values: Record<string, string>;
93
- }>;
94
- };
95
- export type ServiceVaultAssociatedData = {
96
- teamId: string;
97
- connectionId: string;
98
- credentialProfileId: string;
99
- field: string;
100
- purpose: string;
101
- version: number;
102
- };
103
- export declare function containsForbiddenPlaintextSecretMaterial(value: unknown, path?: string): string[];
104
- export declare function canonicalServiceVaultAssociatedData(input: ServiceVaultAssociatedData): string;
105
- export declare function canonicalHostedSecretOperationBinding(input: HostedSecretOperationBinding): string;
106
- export declare function validateHostedSecretOperationBinding(value: unknown): value is HostedSecretOperationBinding;
107
- export declare function validateSealedSecretOperationPayload(value: unknown): value is SealedSecretOperationPayload;
108
- export declare function validateEncryptedCredentialEnvelope(value: unknown): value is EncryptedCredentialEnvelope;
@@ -1,74 +0,0 @@
1
- const SERVICE_VAULT_ENCRYPTION_VERSION = "service-vault-v1";
2
- const SERVICE_VAULT_AEAD_ALGORITHM = "xchacha20-poly1305-ietf";
3
- const SERVICE_VAULT_KEY_AGREEMENT = "x25519-sealed-box";
4
- const SERVICE_VAULT_KDF = "argon2id";
5
- const SECRET_OPERATION_PURPOSES = [
6
- "provider-connection-validation",
7
- "remote-git-publication",
8
- "workflow-dispatch",
9
- "workflow-configuration",
10
- "hosted-topology-plan",
11
- "hosted-topology-apply",
12
- "hosted-topology-readback",
13
- "hosted-topology-rollback"
14
- ];
15
- const FORBIDDEN_SECRET_KEYS = /(?:passphrase|password|plaintext|derivedKey|privateKey|apiToken|accessToken|secretValue|credentialValue)$/iu;
16
- function containsForbiddenPlaintextSecretMaterial(value, path = "") {
17
- if (!value || typeof value !== "object") return [];
18
- const failures = [];
19
- for (const [key, entry] of Object.entries(value)) {
20
- const nextPath = path ? `${path}.${key}` : key;
21
- if (FORBIDDEN_SECRET_KEYS.test(key) && typeof entry === "string" && entry.trim()) failures.push(nextPath);
22
- if (entry && typeof entry === "object") failures.push(...containsForbiddenPlaintextSecretMaterial(entry, nextPath));
23
- }
24
- return failures;
25
- }
26
- function canonicalServiceVaultAssociatedData(input) {
27
- return JSON.stringify({
28
- teamId: input.teamId,
29
- connectionId: input.connectionId,
30
- credentialProfileId: input.credentialProfileId,
31
- field: input.field,
32
- purpose: input.purpose,
33
- version: input.version
34
- });
35
- }
36
- const digest = /^sha256:[a-f0-9]{64}$/u;
37
- const identifier = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;
38
- function canonicalHostedSecretOperationBinding(input) {
39
- return JSON.stringify({
40
- subjectType: input.subjectType,
41
- subjectDigest: input.subjectDigest,
42
- deploymentId: input.deploymentId,
43
- stackId: input.stackId,
44
- environment: input.environment
45
- });
46
- }
47
- function validateHostedSecretOperationBinding(value) {
48
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
49
- const binding = value;
50
- return ["declaration", "plan", "rollback"].includes(String(binding.subjectType)) && digest.test(String(binding.subjectDigest)) && identifier.test(String(binding.deploymentId)) && identifier.test(String(binding.stackId)) && ["staging", "production"].includes(String(binding.environment));
51
- }
52
- function validateSealedSecretOperationPayload(value) {
53
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
54
- const payload = value;
55
- return payload.schemaVersion === "treeseed.sealed-secret-operation-payload/v1" && payload.algorithm === SERVICE_VAULT_KEY_AGREEMENT && [payload.leaseId, payload.teamId, payload.operationCorrelationId, payload.ciphertext].every((field) => typeof field === "string" && field.length > 0) && validateHostedSecretOperationBinding(payload.hostedBinding);
56
- }
57
- function validateEncryptedCredentialEnvelope(value) {
58
- if (!value || typeof value !== "object") return false;
59
- const envelope = value;
60
- return envelope.version === SERVICE_VAULT_ENCRYPTION_VERSION && envelope.algorithm === SERVICE_VAULT_AEAD_ALGORITHM && [envelope.ciphertext, envelope.nonce, envelope.wrappedKey, envelope.wrappedKeyNonce, envelope.associatedData, envelope.associatedDataDigest, envelope.fingerprint].every((field) => typeof field === "string" && field.length > 0);
61
- }
62
- export {
63
- SECRET_OPERATION_PURPOSES,
64
- SERVICE_VAULT_AEAD_ALGORITHM,
65
- SERVICE_VAULT_ENCRYPTION_VERSION,
66
- SERVICE_VAULT_KDF,
67
- SERVICE_VAULT_KEY_AGREEMENT,
68
- canonicalHostedSecretOperationBinding,
69
- canonicalServiceVaultAssociatedData,
70
- containsForbiddenPlaintextSecretMaterial,
71
- validateEncryptedCredentialEnvelope,
72
- validateHostedSecretOperationBinding,
73
- validateSealedSecretOperationPayload
74
- };
@@ -1,20 +0,0 @@
1
- import { type EncryptedCredentialEnvelope, type EncryptedPrivateKeyEnvelope, type TeamVaultGrantEnvelope } from '@treeseed/sdk/secrets-capability';
2
- export declare function createServiceVaultKey(): Promise<Uint8Array>;
3
- export declare function createServiceVaultUserKeyPair(): Promise<{
4
- publicKey: string;
5
- privateKey: Uint8Array;
6
- }>;
7
- export declare function encryptServiceVaultPrivateKey(privateKey: Uint8Array, publicKey: string, passphrase: string, options?: {
8
- opsLimit?: number;
9
- memLimit?: number;
10
- }): Promise<EncryptedPrivateKeyEnvelope>;
11
- export declare function decryptServiceVaultPrivateKey(envelope: EncryptedPrivateKeyEnvelope, passphrase: string): Promise<Uint8Array>;
12
- export declare function createTeamVaultGrant(teamVaultKey: Uint8Array, recipientPublicKey: string): Promise<TeamVaultGrantEnvelope>;
13
- export declare function openTeamVaultGrant(grant: TeamVaultGrantEnvelope, recipientPrivateKey: Uint8Array): Promise<Uint8Array>;
14
- export declare function encryptServiceCredential(plaintext: string, teamVaultKey: Uint8Array, associatedData: string): Promise<EncryptedCredentialEnvelope>;
15
- export declare function decryptServiceCredential(envelope: EncryptedCredentialEnvelope, teamVaultKey: Uint8Array, expectedAssociatedData: string): Promise<string>;
16
- export declare function rewrapServiceCredential(envelope: EncryptedCredentialEnvelope, currentTeamVaultKey: Uint8Array, replacementTeamVaultKey: Uint8Array): Promise<EncryptedCredentialEnvelope>;
17
- export declare function sealSecretOperationPayload(values: Record<string, string>, operationPublicKey: string): Promise<string>;
18
- export declare function encryptGitHubActionsSecret(value: string, providerPublicKey: string): Promise<string>;
19
- export declare function openSecretOperationPayload(sealedPayload: string, operationPublicKey: string, operationPrivateKey: Uint8Array): Promise<Record<string, string>>;
20
- export declare function clearServiceVaultKey(value: Uint8Array | undefined): void;
@@ -1,210 +0,0 @@
1
- import sodium from "libsodium-wrappers-sumo";
2
- import {
3
- SERVICE_VAULT_AEAD_ALGORITHM,
4
- SERVICE_VAULT_ENCRYPTION_VERSION,
5
- SERVICE_VAULT_KDF,
6
- SERVICE_VAULT_KEY_AGREEMENT
7
- } from "@treeseed/sdk/secrets-capability";
8
- const b64 = () => sodium.base64_variants.ORIGINAL;
9
- const encode = (value) => sodium.to_base64(value, b64());
10
- const decode = (value) => sodium.from_base64(value, b64());
11
- const hash = (value, key = null) => encode(sodium.crypto_generichash(32, value, key));
12
- async function ready() {
13
- await sodium.ready;
14
- return sodium;
15
- }
16
- function validKey(value, length, label) {
17
- if (!(value instanceof Uint8Array) || value.length !== length) throw new Error(`${label} is invalid.`);
18
- return value;
19
- }
20
- async function createServiceVaultKey() {
21
- const crypto = await ready();
22
- return crypto.randombytes_buf(crypto.crypto_aead_xchacha20poly1305_ietf_KEYBYTES);
23
- }
24
- async function createServiceVaultUserKeyPair() {
25
- const crypto = await ready(), pair = crypto.crypto_box_keypair();
26
- return { publicKey: encode(pair.publicKey), privateKey: pair.privateKey };
27
- }
28
- async function encryptServiceVaultPrivateKey(privateKey, publicKey, passphrase, options = {}) {
29
- const crypto = await ready();
30
- validKey(privateKey, crypto.crypto_box_SECRETKEYBYTES, "Service-vault private key");
31
- validKey(decode(publicKey), crypto.crypto_box_PUBLICKEYBYTES, "Service-vault public key");
32
- if (passphrase.length < 12) throw new Error("Service-vault passphrase must contain at least 12 characters.");
33
- const salt = crypto.randombytes_buf(crypto.crypto_pwhash_SALTBYTES);
34
- const opsLimit = options.opsLimit ?? crypto.crypto_pwhash_OPSLIMIT_MODERATE;
35
- const memLimit = options.memLimit ?? crypto.crypto_pwhash_MEMLIMIT_MODERATE;
36
- const key = crypto.crypto_pwhash(
37
- crypto.crypto_aead_xchacha20poly1305_ietf_KEYBYTES,
38
- passphrase,
39
- salt,
40
- opsLimit,
41
- memLimit,
42
- crypto.crypto_pwhash_ALG_ARGON2ID13
43
- );
44
- const nonce = crypto.randombytes_buf(crypto.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES);
45
- try {
46
- return {
47
- version: SERVICE_VAULT_ENCRYPTION_VERSION,
48
- algorithm: SERVICE_VAULT_AEAD_ALGORITHM,
49
- kdf: { algorithm: SERVICE_VAULT_KDF, opsLimit, memLimit, salt: encode(salt) },
50
- nonce: encode(nonce),
51
- ciphertext: encode(crypto.crypto_aead_xchacha20poly1305_ietf_encrypt(privateKey, publicKey, null, nonce, key)),
52
- publicKey
53
- };
54
- } finally {
55
- crypto.memzero(key);
56
- }
57
- }
58
- async function decryptServiceVaultPrivateKey(envelope, passphrase) {
59
- const crypto = await ready();
60
- if (envelope.version !== SERVICE_VAULT_ENCRYPTION_VERSION || envelope.algorithm !== SERVICE_VAULT_AEAD_ALGORITHM || envelope.kdf.algorithm !== SERVICE_VAULT_KDF) throw new Error("Service-vault private-key envelope is unsupported.");
61
- if (!Number.isSafeInteger(envelope.kdf.opsLimit) || !Number.isSafeInteger(envelope.kdf.memLimit) || envelope.kdf.opsLimit < crypto.crypto_pwhash_OPSLIMIT_INTERACTIVE || envelope.kdf.opsLimit > crypto.crypto_pwhash_OPSLIMIT_SENSITIVE || envelope.kdf.memLimit < crypto.crypto_pwhash_MEMLIMIT_INTERACTIVE || envelope.kdf.memLimit > crypto.crypto_pwhash_MEMLIMIT_SENSITIVE)
62
- throw new Error("Service-vault private-key KDF parameters are invalid.");
63
- const key = crypto.crypto_pwhash(
64
- crypto.crypto_aead_xchacha20poly1305_ietf_KEYBYTES,
65
- passphrase,
66
- decode(envelope.kdf.salt),
67
- envelope.kdf.opsLimit,
68
- envelope.kdf.memLimit,
69
- crypto.crypto_pwhash_ALG_ARGON2ID13
70
- );
71
- try {
72
- return validKey(crypto.crypto_aead_xchacha20poly1305_ietf_decrypt(
73
- null,
74
- decode(envelope.ciphertext),
75
- envelope.publicKey,
76
- decode(envelope.nonce),
77
- key
78
- ), crypto.crypto_box_SECRETKEYBYTES, "Decrypted service-vault private key");
79
- } catch {
80
- throw new Error("Service-vault passphrase or private-key envelope is invalid.");
81
- } finally {
82
- crypto.memzero(key);
83
- }
84
- }
85
- async function createTeamVaultGrant(teamVaultKey, recipientPublicKey) {
86
- const crypto = await ready();
87
- validKey(teamVaultKey, crypto.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, "Team vault key");
88
- const publicKey = validKey(decode(recipientPublicKey), crypto.crypto_box_PUBLICKEYBYTES, "Recipient public key");
89
- return {
90
- version: SERVICE_VAULT_ENCRYPTION_VERSION,
91
- algorithm: SERVICE_VAULT_KEY_AGREEMENT,
92
- recipientPublicKey,
93
- wrappedTeamVaultKey: encode(crypto.crypto_box_seal(teamVaultKey, publicKey))
94
- };
95
- }
96
- async function openTeamVaultGrant(grant, recipientPrivateKey) {
97
- const crypto = await ready();
98
- if (grant.version !== SERVICE_VAULT_ENCRYPTION_VERSION || grant.algorithm !== SERVICE_VAULT_KEY_AGREEMENT)
99
- throw new Error("Team vault grant is unsupported.");
100
- const publicKey = validKey(decode(grant.recipientPublicKey), crypto.crypto_box_PUBLICKEYBYTES, "Recipient public key");
101
- validKey(recipientPrivateKey, crypto.crypto_box_SECRETKEYBYTES, "Recipient private key");
102
- try {
103
- return validKey(
104
- crypto.crypto_box_seal_open(decode(grant.wrappedTeamVaultKey), publicKey, recipientPrivateKey),
105
- crypto.crypto_aead_xchacha20poly1305_ietf_KEYBYTES,
106
- "Unwrapped team vault key"
107
- );
108
- } catch {
109
- throw new Error("Team vault grant cannot be opened by this user key.");
110
- }
111
- }
112
- async function encryptServiceCredential(plaintext, teamVaultKey, associatedData) {
113
- const crypto = await ready();
114
- validKey(teamVaultKey, crypto.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, "Team vault key");
115
- if (!plaintext || !associatedData) throw new Error("Credential value and associated data are required.");
116
- const dataKey = crypto.crypto_aead_xchacha20poly1305_ietf_keygen();
117
- const nonce = crypto.randombytes_buf(crypto.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES);
118
- const wrappedKeyNonce = crypto.randombytes_buf(crypto.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES);
119
- try {
120
- return {
121
- version: SERVICE_VAULT_ENCRYPTION_VERSION,
122
- algorithm: SERVICE_VAULT_AEAD_ALGORITHM,
123
- ciphertext: encode(crypto.crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, associatedData, null, nonce, dataKey)),
124
- nonce: encode(nonce),
125
- wrappedKey: encode(crypto.crypto_aead_xchacha20poly1305_ietf_encrypt(dataKey, associatedData, null, wrappedKeyNonce, teamVaultKey)),
126
- wrappedKeyNonce: encode(wrappedKeyNonce),
127
- associatedData,
128
- associatedDataDigest: hash(associatedData),
129
- fingerprint: hash(plaintext, teamVaultKey)
130
- };
131
- } finally {
132
- crypto.memzero(dataKey);
133
- }
134
- }
135
- async function decryptServiceCredential(envelope, teamVaultKey, expectedAssociatedData) {
136
- const crypto = await ready();
137
- validKey(teamVaultKey, crypto.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, "Team vault key");
138
- if (envelope.version !== SERVICE_VAULT_ENCRYPTION_VERSION || envelope.algorithm !== SERVICE_VAULT_AEAD_ALGORITHM || envelope.associatedData !== expectedAssociatedData || envelope.associatedDataDigest !== hash(expectedAssociatedData))
139
- throw new Error("Credential envelope binding is invalid.");
140
- let dataKey;
141
- try {
142
- dataKey = crypto.crypto_aead_xchacha20poly1305_ietf_decrypt(
143
- null,
144
- decode(envelope.wrappedKey),
145
- expectedAssociatedData,
146
- decode(envelope.wrappedKeyNonce),
147
- teamVaultKey
148
- );
149
- return crypto.crypto_aead_xchacha20poly1305_ietf_decrypt(
150
- null,
151
- decode(envelope.ciphertext),
152
- expectedAssociatedData,
153
- decode(envelope.nonce),
154
- dataKey,
155
- "text"
156
- );
157
- } catch {
158
- throw new Error("Credential envelope cannot be decrypted.");
159
- } finally {
160
- if (dataKey) crypto.memzero(dataKey);
161
- }
162
- }
163
- async function rewrapServiceCredential(envelope, currentTeamVaultKey, replacementTeamVaultKey) {
164
- const plaintext = await decryptServiceCredential(envelope, currentTeamVaultKey, envelope.associatedData);
165
- return encryptServiceCredential(plaintext, replacementTeamVaultKey, envelope.associatedData);
166
- }
167
- async function sealSecretOperationPayload(values, operationPublicKey) {
168
- const crypto = await ready();
169
- if (!Object.keys(values).length || Object.values(values).some((value) => typeof value !== "string" || !value))
170
- throw new Error("Secret operation payload must contain only non-empty string values.");
171
- const payload = JSON.stringify(Object.fromEntries(Object.entries(values).sort(([left], [right]) => left.localeCompare(right))));
172
- return encode(crypto.crypto_box_seal(payload, validKey(decode(operationPublicKey), crypto.crypto_box_PUBLICKEYBYTES, "Operation public key")));
173
- }
174
- async function encryptGitHubActionsSecret(value, providerPublicKey) {
175
- const crypto = await ready();
176
- if (!value) throw new Error("GitHub Actions secret value is required.");
177
- return encode(crypto.crypto_box_seal(
178
- value,
179
- validKey(decode(providerPublicKey), crypto.crypto_box_PUBLICKEYBYTES, "GitHub public key")
180
- ));
181
- }
182
- async function openSecretOperationPayload(sealedPayload, operationPublicKey, operationPrivateKey) {
183
- const crypto = await ready();
184
- try {
185
- const plaintext = crypto.crypto_box_seal_open(decode(sealedPayload), decode(operationPublicKey), operationPrivateKey, "text");
186
- const parsed = JSON.parse(plaintext);
187
- if (!parsed || Array.isArray(parsed) || Object.values(parsed).some((value) => typeof value !== "string" || !value)) throw new Error();
188
- return parsed;
189
- } catch {
190
- throw new Error("Secret operation payload cannot be opened.");
191
- }
192
- }
193
- function clearServiceVaultKey(value) {
194
- value?.fill(0);
195
- }
196
- export {
197
- clearServiceVaultKey,
198
- createServiceVaultKey,
199
- createServiceVaultUserKeyPair,
200
- createTeamVaultGrant,
201
- decryptServiceCredential,
202
- decryptServiceVaultPrivateKey,
203
- encryptGitHubActionsSecret,
204
- encryptServiceCredential,
205
- encryptServiceVaultPrivateKey,
206
- openSecretOperationPayload,
207
- openTeamVaultGrant,
208
- rewrapServiceCredential,
209
- sealSecretOperationPayload
210
- };