@shipfox/api-secrets 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (288) hide show
  1. package/.turbo/turbo-build.log +2 -0
  2. package/.turbo/turbo-type$colon$emit.log +1 -0
  3. package/.turbo/turbo-type.log +1 -0
  4. package/CHANGELOG.md +116 -0
  5. package/LICENSE +21 -0
  6. package/README.md +207 -0
  7. package/dist/config.d.ts +8 -0
  8. package/dist/config.d.ts.map +1 -0
  9. package/dist/config.js +38 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/core/crypto.d.ts +25 -0
  12. package/dist/core/crypto.d.ts.map +1 -0
  13. package/dist/core/crypto.js +85 -0
  14. package/dist/core/crypto.js.map +1 -0
  15. package/dist/core/dek-manager.d.ts +16 -0
  16. package/dist/core/dek-manager.d.ts.map +1 -0
  17. package/dist/core/dek-manager.js +85 -0
  18. package/dist/core/dek-manager.js.map +1 -0
  19. package/dist/core/errors.d.ts +53 -0
  20. package/dist/core/errors.d.ts.map +1 -0
  21. package/dist/core/errors.js +86 -0
  22. package/dist/core/errors.js.map +1 -0
  23. package/dist/core/fingerprint.d.ts +2 -0
  24. package/dist/core/fingerprint.d.ts.map +1 -0
  25. package/dist/core/fingerprint.js +9 -0
  26. package/dist/core/fingerprint.js.map +1 -0
  27. package/dist/core/index.d.ts +30 -0
  28. package/dist/core/index.d.ts.map +1 -0
  29. package/dist/core/index.js +97 -0
  30. package/dist/core/index.js.map +1 -0
  31. package/dist/core/key-provider.d.ts +17 -0
  32. package/dist/core/key-provider.d.ts.map +1 -0
  33. package/dist/core/key-provider.js +45 -0
  34. package/dist/core/key-provider.js.map +1 -0
  35. package/dist/core/local-secret-store.d.ts +19 -0
  36. package/dist/core/local-secret-store.d.ts.map +1 -0
  37. package/dist/core/local-secret-store.js +40 -0
  38. package/dist/core/local-secret-store.js.map +1 -0
  39. package/dist/core/management.d.ts +37 -0
  40. package/dist/core/management.d.ts.map +1 -0
  41. package/dist/core/management.js +403 -0
  42. package/dist/core/management.js.map +1 -0
  43. package/dist/core/rotate-kek.d.ts +10 -0
  44. package/dist/core/rotate-kek.d.ts.map +1 -0
  45. package/dist/core/rotate-kek.js +94 -0
  46. package/dist/core/rotate-kek.js.map +1 -0
  47. package/dist/core/secret-store.d.ts +33 -0
  48. package/dist/core/secret-store.d.ts.map +1 -0
  49. package/dist/core/secret-store.js +209 -0
  50. package/dist/core/secret-store.js.map +1 -0
  51. package/dist/core/store-resolver.d.ts +18 -0
  52. package/dist/core/store-resolver.d.ts.map +1 -0
  53. package/dist/core/store-resolver.js +10 -0
  54. package/dist/core/store-resolver.js.map +1 -0
  55. package/dist/core/store-validation.d.ts +11 -0
  56. package/dist/core/store-validation.d.ts.map +1 -0
  57. package/dist/core/store-validation.js +30 -0
  58. package/dist/core/store-validation.js.map +1 -0
  59. package/dist/core/value-cipher.d.ts +10 -0
  60. package/dist/core/value-cipher.d.ts.map +1 -0
  61. package/dist/core/value-cipher.js +22 -0
  62. package/dist/core/value-cipher.js.map +1 -0
  63. package/dist/core/variable-store.d.ts +24 -0
  64. package/dist/core/variable-store.d.ts.map +1 -0
  65. package/dist/core/variable-store.js +194 -0
  66. package/dist/core/variable-store.js.map +1 -0
  67. package/dist/db/cap.d.ts +8 -0
  68. package/dist/db/cap.d.ts.map +1 -0
  69. package/dist/db/cap.js +39 -0
  70. package/dist/db/cap.js.map +1 -0
  71. package/dist/db/data-keys.d.ts +24 -0
  72. package/dist/db/data-keys.d.ts.map +1 -0
  73. package/dist/db/data-keys.js +61 -0
  74. package/dist/db/data-keys.js.map +1 -0
  75. package/dist/db/db.d.ts +1254 -0
  76. package/dist/db/db.d.ts.map +1 -0
  77. package/dist/db/db.js +24 -0
  78. package/dist/db/db.js.map +1 -0
  79. package/dist/db/index.d.ts +13 -0
  80. package/dist/db/index.d.ts.map +1 -0
  81. package/dist/db/index.js +15 -0
  82. package/dist/db/index.js.map +1 -0
  83. package/dist/db/management.d.ts +50 -0
  84. package/dist/db/management.d.ts.map +1 -0
  85. package/dist/db/management.js +127 -0
  86. package/dist/db/management.js.map +1 -0
  87. package/dist/db/schema/common.d.ts +3 -0
  88. package/dist/db/schema/common.d.ts.map +1 -0
  89. package/dist/db/schema/common.js +8 -0
  90. package/dist/db/schema/common.js.map +1 -0
  91. package/dist/db/schema/data-keys.d.ts +103 -0
  92. package/dist/db/schema/data-keys.d.ts.map +1 -0
  93. package/dist/db/schema/data-keys.js +24 -0
  94. package/dist/db/schema/data-keys.js.map +1 -0
  95. package/dist/db/schema/outbox.d.ts +195 -0
  96. package/dist/db/schema/outbox.d.ts.map +1 -0
  97. package/dist/db/schema/outbox.js +5 -0
  98. package/dist/db/schema/outbox.js.map +1 -0
  99. package/dist/db/schema/values.d.ts +193 -0
  100. package/dist/db/schema/values.d.ts.map +1 -0
  101. package/dist/db/schema/values.js +43 -0
  102. package/dist/db/schema/values.js.map +1 -0
  103. package/dist/db/schema/variables.d.ts +175 -0
  104. package/dist/db/schema/variables.d.ts.map +1 -0
  105. package/dist/db/schema/variables.js +41 -0
  106. package/dist/db/schema/variables.js.map +1 -0
  107. package/dist/db/scope.d.ts +20 -0
  108. package/dist/db/scope.d.ts.map +1 -0
  109. package/dist/db/scope.js +19 -0
  110. package/dist/db/scope.js.map +1 -0
  111. package/dist/db/values.d.ts +33 -0
  112. package/dist/db/values.d.ts.map +1 -0
  113. package/dist/db/values.js +119 -0
  114. package/dist/db/values.js.map +1 -0
  115. package/dist/db/variables.d.ts +32 -0
  116. package/dist/db/variables.d.ts.map +1 -0
  117. package/dist/db/variables.js +117 -0
  118. package/dist/db/variables.js.map +1 -0
  119. package/dist/index.d.ts +4 -0
  120. package/dist/index.d.ts.map +1 -0
  121. package/dist/index.js +28 -0
  122. package/dist/index.js.map +1 -0
  123. package/dist/metrics/index.d.ts +3 -0
  124. package/dist/metrics/index.d.ts.map +1 -0
  125. package/dist/metrics/index.js +4 -0
  126. package/dist/metrics/index.js.map +1 -0
  127. package/dist/metrics/instance.d.ts +39 -0
  128. package/dist/metrics/instance.d.ts.map +1 -0
  129. package/dist/metrics/instance.js +151 -0
  130. package/dist/metrics/instance.js.map +1 -0
  131. package/dist/metrics/service.d.ts +2 -0
  132. package/dist/metrics/service.d.ts.map +1 -0
  133. package/dist/metrics/service.js +21 -0
  134. package/dist/metrics/service.js.map +1 -0
  135. package/dist/presentation/dto/index.d.ts +2 -0
  136. package/dist/presentation/dto/index.d.ts.map +1 -0
  137. package/dist/presentation/dto/index.js +3 -0
  138. package/dist/presentation/dto/index.js.map +1 -0
  139. package/dist/presentation/dto/management.d.ts +7 -0
  140. package/dist/presentation/dto/management.d.ts.map +1 -0
  141. package/dist/presentation/dto/management.js +32 -0
  142. package/dist/presentation/dto/management.js.map +1 -0
  143. package/dist/presentation/e2eRoutes/create-secret.d.ts +2 -0
  144. package/dist/presentation/e2eRoutes/create-secret.d.ts.map +1 -0
  145. package/dist/presentation/e2eRoutes/create-secret.js +34 -0
  146. package/dist/presentation/e2eRoutes/create-secret.js.map +1 -0
  147. package/dist/presentation/e2eRoutes/create-variable.d.ts +2 -0
  148. package/dist/presentation/e2eRoutes/create-variable.d.ts.map +1 -0
  149. package/dist/presentation/e2eRoutes/create-variable.js +34 -0
  150. package/dist/presentation/e2eRoutes/create-variable.js.map +1 -0
  151. package/dist/presentation/e2eRoutes/index.d.ts +3 -0
  152. package/dist/presentation/e2eRoutes/index.d.ts.map +1 -0
  153. package/dist/presentation/e2eRoutes/index.js +11 -0
  154. package/dist/presentation/e2eRoutes/index.js.map +1 -0
  155. package/dist/presentation/routes/auth.d.ts +16 -0
  156. package/dist/presentation/routes/auth.d.ts.map +1 -0
  157. package/dist/presentation/routes/auth.js +42 -0
  158. package/dist/presentation/routes/auth.js.map +1 -0
  159. package/dist/presentation/routes/batch-secrets.d.ts +2 -0
  160. package/dist/presentation/routes/batch-secrets.d.ts.map +1 -0
  161. package/dist/presentation/routes/batch-secrets.js +44 -0
  162. package/dist/presentation/routes/batch-secrets.js.map +1 -0
  163. package/dist/presentation/routes/batch-variables.d.ts +2 -0
  164. package/dist/presentation/routes/batch-variables.d.ts.map +1 -0
  165. package/dist/presentation/routes/batch-variables.js +44 -0
  166. package/dist/presentation/routes/batch-variables.js.map +1 -0
  167. package/dist/presentation/routes/cursor.d.ts +3 -0
  168. package/dist/presentation/routes/cursor.d.ts.map +1 -0
  169. package/dist/presentation/routes/cursor.js +19 -0
  170. package/dist/presentation/routes/cursor.js.map +1 -0
  171. package/dist/presentation/routes/delete-secret.d.ts +2 -0
  172. package/dist/presentation/routes/delete-secret.d.ts.map +1 -0
  173. package/dist/presentation/routes/delete-secret.js +40 -0
  174. package/dist/presentation/routes/delete-secret.js.map +1 -0
  175. package/dist/presentation/routes/delete-variable.d.ts +2 -0
  176. package/dist/presentation/routes/delete-variable.d.ts.map +1 -0
  177. package/dist/presentation/routes/delete-variable.js +40 -0
  178. package/dist/presentation/routes/delete-variable.js.map +1 -0
  179. package/dist/presentation/routes/errors.d.ts +2 -0
  180. package/dist/presentation/routes/errors.d.ts.map +1 -0
  181. package/dist/presentation/routes/errors.js +73 -0
  182. package/dist/presentation/routes/errors.js.map +1 -0
  183. package/dist/presentation/routes/get-variable.d.ts +2 -0
  184. package/dist/presentation/routes/get-variable.d.ts.map +1 -0
  185. package/dist/presentation/routes/get-variable.js +42 -0
  186. package/dist/presentation/routes/get-variable.js.map +1 -0
  187. package/dist/presentation/routes/index.d.ts +3 -0
  188. package/dist/presentation/routes/index.d.ts.map +1 -0
  189. package/dist/presentation/routes/index.js +29 -0
  190. package/dist/presentation/routes/index.js.map +1 -0
  191. package/dist/presentation/routes/list-secrets.d.ts +2 -0
  192. package/dist/presentation/routes/list-secrets.d.ts.map +1 -0
  193. package/dist/presentation/routes/list-secrets.js +50 -0
  194. package/dist/presentation/routes/list-secrets.js.map +1 -0
  195. package/dist/presentation/routes/list-variables.d.ts +2 -0
  196. package/dist/presentation/routes/list-variables.d.ts.map +1 -0
  197. package/dist/presentation/routes/list-variables.js +50 -0
  198. package/dist/presentation/routes/list-variables.js.map +1 -0
  199. package/dist/presentation/routes/put-secret.d.ts +2 -0
  200. package/dist/presentation/routes/put-secret.d.ts.map +1 -0
  201. package/dist/presentation/routes/put-secret.js +52 -0
  202. package/dist/presentation/routes/put-secret.js.map +1 -0
  203. package/dist/presentation/routes/put-variable.d.ts +2 -0
  204. package/dist/presentation/routes/put-variable.d.ts.map +1 -0
  205. package/dist/presentation/routes/put-variable.js +52 -0
  206. package/dist/presentation/routes/put-variable.js.map +1 -0
  207. package/dist/presentation/routes/warnings.d.ts +5 -0
  208. package/dist/presentation/routes/warnings.d.ts.map +1 -0
  209. package/dist/presentation/routes/warnings.js +16 -0
  210. package/dist/presentation/routes/warnings.js.map +1 -0
  211. package/dist/tsconfig.test.tsbuildinfo +1 -0
  212. package/drizzle/0000_lowly_ogun.sql +59 -0
  213. package/drizzle/meta/0000_snapshot.json +547 -0
  214. package/drizzle/meta/_journal.json +13 -0
  215. package/drizzle.config.ts +7 -0
  216. package/package.json +77 -0
  217. package/src/config.test.ts +41 -0
  218. package/src/config.ts +41 -0
  219. package/src/core/crypto.test.ts +115 -0
  220. package/src/core/crypto.ts +105 -0
  221. package/src/core/dek-manager.test.ts +120 -0
  222. package/src/core/dek-manager.ts +98 -0
  223. package/src/core/errors.ts +100 -0
  224. package/src/core/fingerprint.test.ts +27 -0
  225. package/src/core/fingerprint.ts +14 -0
  226. package/src/core/index.ts +153 -0
  227. package/src/core/key-provider.test.ts +44 -0
  228. package/src/core/key-provider.ts +70 -0
  229. package/src/core/local-secret-store.ts +49 -0
  230. package/src/core/management.test.ts +37 -0
  231. package/src/core/management.ts +503 -0
  232. package/src/core/rotate-kek.test.ts +183 -0
  233. package/src/core/rotate-kek.ts +100 -0
  234. package/src/core/secret-store.test.ts +301 -0
  235. package/src/core/secret-store.ts +251 -0
  236. package/src/core/store-resolver.test.ts +20 -0
  237. package/src/core/store-resolver.ts +28 -0
  238. package/src/core/store-validation.ts +43 -0
  239. package/src/core/value-cipher.ts +26 -0
  240. package/src/core/variable-store.test.ts +106 -0
  241. package/src/core/variable-store.ts +232 -0
  242. package/src/db/cap.ts +58 -0
  243. package/src/db/data-keys.ts +99 -0
  244. package/src/db/db.ts +26 -0
  245. package/src/db/index.ts +45 -0
  246. package/src/db/management.ts +252 -0
  247. package/src/db/schema/common.ts +8 -0
  248. package/src/db/schema/data-keys.ts +31 -0
  249. package/src/db/schema/outbox.ts +4 -0
  250. package/src/db/schema/values.ts +74 -0
  251. package/src/db/schema/variables.ts +71 -0
  252. package/src/db/scope.ts +42 -0
  253. package/src/db/values.ts +191 -0
  254. package/src/db/variables.ts +187 -0
  255. package/src/index.test.ts +11 -0
  256. package/src/index.ts +50 -0
  257. package/src/metrics/index.ts +19 -0
  258. package/src/metrics/instance.ts +219 -0
  259. package/src/metrics/service.ts +19 -0
  260. package/src/presentation/dto/index.ts +1 -0
  261. package/src/presentation/dto/management.ts +36 -0
  262. package/src/presentation/e2eRoutes/create-secret.ts +31 -0
  263. package/src/presentation/e2eRoutes/create-variable.ts +34 -0
  264. package/src/presentation/e2eRoutes/index.test.ts +94 -0
  265. package/src/presentation/e2eRoutes/index.ts +8 -0
  266. package/src/presentation/routes/auth.ts +50 -0
  267. package/src/presentation/routes/batch-secrets.ts +33 -0
  268. package/src/presentation/routes/batch-variables.ts +33 -0
  269. package/src/presentation/routes/cursor.ts +23 -0
  270. package/src/presentation/routes/delete-secret.ts +26 -0
  271. package/src/presentation/routes/delete-variable.ts +26 -0
  272. package/src/presentation/routes/errors.ts +63 -0
  273. package/src/presentation/routes/get-variable.ts +32 -0
  274. package/src/presentation/routes/index.ts +29 -0
  275. package/src/presentation/routes/list-secrets.ts +41 -0
  276. package/src/presentation/routes/list-variables.ts +41 -0
  277. package/src/presentation/routes/management.test.ts +452 -0
  278. package/src/presentation/routes/put-secret.ts +39 -0
  279. package/src/presentation/routes/put-variable.ts +39 -0
  280. package/src/presentation/routes/warnings.ts +23 -0
  281. package/test/env.ts +8 -0
  282. package/test/globalSetup.ts +18 -0
  283. package/test/setup.ts +17 -0
  284. package/tsconfig.build.json +10 -0
  285. package/tsconfig.build.tsbuildinfo +1 -0
  286. package/tsconfig.json +7 -0
  287. package/tsconfig.test.json +8 -0
  288. package/vitest.config.ts +12 -0
@@ -0,0 +1,403 @@
1
+ import { SECRET_CREATED, SECRET_DELETED, SECRET_UPDATED, VARIABLE_CREATED, VARIABLE_DELETED, VARIABLE_UPDATED } from '@shipfox/api-secrets-dto';
2
+ import { writeOutboxEvent } from '@shipfox/node-outbox';
3
+ import { db, deleteSecretManagementRows, deleteVariableManagementRows, getSecretManagementRow, getVariableManagementRow, listExistingSecretManagementKeys, listExistingVariableManagementKeys, listSecretManagementRows, listVariableManagementRows, lockWorkspaceEntries, secretsOutbox, upsertSecretValueRows, upsertSecretVariableRows } from '#db/index.js';
4
+ import { normalizedProjectId } from '#db/scope.js';
5
+ import { classifySecretsOperationError, operationScope, recordSecretsEntriesMutated, recordSecretsOperation } from '#metrics/instance.js';
6
+ import { SecretBatchDuplicateKeyError, SecretNotFoundError, VariableNotFoundError } from './errors.js';
7
+ import { fingerprintSecretValue } from './fingerprint.js';
8
+ import { assertWorkspaceCap, validateSecretKeys, validateValueBytes } from './store-validation.js';
9
+ import { encryptSecretValue } from './value-cipher.js';
10
+ export function createSecretsManagementApi(params) {
11
+ return {
12
+ async listSecrets (input) {
13
+ const startedAt = Date.now();
14
+ const scope = operationScope(input);
15
+ try {
16
+ const result = await listSecretManagementRows(input);
17
+ recordSecretsOperation({
18
+ resource: 'secret',
19
+ operation: 'list',
20
+ surface: 'management',
21
+ scope,
22
+ outcome: 'success',
23
+ durationMs: Date.now() - startedAt
24
+ });
25
+ return result;
26
+ } catch (error) {
27
+ recordSecretsOperation({
28
+ resource: 'secret',
29
+ operation: 'list',
30
+ surface: 'management',
31
+ scope,
32
+ outcome: classifySecretsOperationError(error),
33
+ durationMs: Date.now() - startedAt
34
+ });
35
+ throw error;
36
+ }
37
+ },
38
+ async listVariables (input) {
39
+ const startedAt = Date.now();
40
+ const scope = operationScope(input);
41
+ try {
42
+ const result = await listVariableManagementRows(input);
43
+ recordSecretsOperation({
44
+ resource: 'variable',
45
+ operation: 'list',
46
+ surface: 'management',
47
+ scope,
48
+ outcome: 'success',
49
+ durationMs: Date.now() - startedAt
50
+ });
51
+ return result;
52
+ } catch (error) {
53
+ recordSecretsOperation({
54
+ resource: 'variable',
55
+ operation: 'list',
56
+ surface: 'management',
57
+ scope,
58
+ outcome: classifySecretsOperationError(error),
59
+ durationMs: Date.now() - startedAt
60
+ });
61
+ throw error;
62
+ }
63
+ },
64
+ async getVariable (input) {
65
+ const startedAt = Date.now();
66
+ const scope = operationScope(input);
67
+ try {
68
+ validateSecretKeys([
69
+ input.key
70
+ ]);
71
+ const variable = await getVariableManagementRow(input);
72
+ if (!variable) throw new VariableNotFoundError(input.key);
73
+ recordSecretsOperation({
74
+ resource: 'variable',
75
+ operation: 'get',
76
+ surface: 'management',
77
+ scope,
78
+ outcome: 'success',
79
+ durationMs: Date.now() - startedAt
80
+ });
81
+ return variable;
82
+ } catch (error) {
83
+ recordSecretsOperation({
84
+ resource: 'variable',
85
+ operation: 'get',
86
+ surface: 'management',
87
+ scope,
88
+ outcome: classifySecretsOperationError(error),
89
+ durationMs: Date.now() - startedAt
90
+ });
91
+ throw error;
92
+ }
93
+ },
94
+ async setSecrets (input) {
95
+ const startedAt = Date.now();
96
+ const scope = operationScope(input);
97
+ try {
98
+ const entries = normalizeEntries(input.entries);
99
+ validateSecretKeys(entries.map((entry)=>entry.key));
100
+ validateValueBytes(entries.map((entry)=>entry.value));
101
+ if (entries.length === 0) {
102
+ recordSecretsOperation({
103
+ resource: 'secret',
104
+ operation: 'set',
105
+ surface: 'management',
106
+ scope,
107
+ outcome: 'success',
108
+ durationMs: Date.now() - startedAt
109
+ });
110
+ return [];
111
+ }
112
+ const projectId = normalizedProjectId(input);
113
+ const dek = await params.dekManager.getPlaintextDek(input.workspaceId);
114
+ const rows = await writeManagementEntries({
115
+ resource: 'secret',
116
+ input,
117
+ keys: entries.map((entry)=>entry.key),
118
+ listExistingKeys: listExistingSecretManagementKeys,
119
+ writeRows: async (tx)=>{
120
+ await upsertSecretValueRows(entries.map((entry)=>({
121
+ workspaceId: input.workspaceId,
122
+ projectId,
123
+ namespace: '',
124
+ key: entry.key,
125
+ ciphertext: encryptSecretValue({
126
+ dek,
127
+ workspaceId: input.workspaceId,
128
+ scope: {
129
+ projectId
130
+ },
131
+ namespace: '',
132
+ key: entry.key,
133
+ value: entry.value
134
+ }),
135
+ fingerprint: fingerprintSecretValue(entry.value, dek),
136
+ lastEditedBy: input.actorId
137
+ })), tx);
138
+ const rows = await Promise.all(entries.map((entry)=>getSecretManagementRow({
139
+ workspaceId: input.workspaceId,
140
+ projectId,
141
+ key: entry.key
142
+ }, tx)));
143
+ return rows.map(assertFound);
144
+ },
145
+ eventTypes: {
146
+ created: SECRET_CREATED,
147
+ updated: SECRET_UPDATED
148
+ }
149
+ });
150
+ recordSecretsOperation({
151
+ resource: 'secret',
152
+ operation: 'set',
153
+ surface: 'management',
154
+ scope,
155
+ outcome: 'success',
156
+ durationMs: Date.now() - startedAt
157
+ });
158
+ return rows;
159
+ } catch (error) {
160
+ recordSecretsOperation({
161
+ resource: 'secret',
162
+ operation: 'set',
163
+ surface: 'management',
164
+ scope,
165
+ outcome: classifySecretsOperationError(error),
166
+ durationMs: Date.now() - startedAt
167
+ });
168
+ throw error;
169
+ }
170
+ },
171
+ async setVariables (input) {
172
+ const startedAt = Date.now();
173
+ const scope = operationScope(input);
174
+ try {
175
+ const entries = normalizeEntries(input.entries);
176
+ validateSecretKeys(entries.map((entry)=>entry.key));
177
+ validateValueBytes(entries.map((entry)=>entry.value));
178
+ if (entries.length === 0) {
179
+ recordSecretsOperation({
180
+ resource: 'variable',
181
+ operation: 'set',
182
+ surface: 'management',
183
+ scope,
184
+ outcome: 'success',
185
+ durationMs: Date.now() - startedAt
186
+ });
187
+ return [];
188
+ }
189
+ const projectId = normalizedProjectId(input);
190
+ const rows = await writeManagementEntries({
191
+ resource: 'variable',
192
+ input,
193
+ keys: entries.map((entry)=>entry.key),
194
+ listExistingKeys: listExistingVariableManagementKeys,
195
+ writeRows: async (tx)=>{
196
+ await upsertSecretVariableRows(entries.map((entry)=>({
197
+ workspaceId: input.workspaceId,
198
+ projectId,
199
+ namespace: '',
200
+ key: entry.key,
201
+ value: entry.value,
202
+ lastEditedBy: input.actorId
203
+ })), tx);
204
+ const rows = await Promise.all(entries.map((entry)=>getVariableManagementRow({
205
+ workspaceId: input.workspaceId,
206
+ projectId,
207
+ key: entry.key
208
+ }, tx)));
209
+ return rows.map(assertFound);
210
+ },
211
+ eventTypes: {
212
+ created: VARIABLE_CREATED,
213
+ updated: VARIABLE_UPDATED
214
+ }
215
+ });
216
+ recordSecretsOperation({
217
+ resource: 'variable',
218
+ operation: 'set',
219
+ surface: 'management',
220
+ scope,
221
+ outcome: 'success',
222
+ durationMs: Date.now() - startedAt
223
+ });
224
+ return rows;
225
+ } catch (error) {
226
+ recordSecretsOperation({
227
+ resource: 'variable',
228
+ operation: 'set',
229
+ surface: 'management',
230
+ scope,
231
+ outcome: classifySecretsOperationError(error),
232
+ durationMs: Date.now() - startedAt
233
+ });
234
+ throw error;
235
+ }
236
+ },
237
+ async deleteSecret (input) {
238
+ const startedAt = Date.now();
239
+ const scope = operationScope(input);
240
+ try {
241
+ validateSecretKeys([
242
+ input.key
243
+ ]);
244
+ const deleted = await deleteManagementEntries({
245
+ resource: 'secret',
246
+ input,
247
+ keys: [
248
+ input.key
249
+ ],
250
+ deleteRows: deleteSecretManagementRows,
251
+ eventType: SECRET_DELETED
252
+ });
253
+ if (deleted === 0) throw new SecretNotFoundError(input.key);
254
+ recordSecretsOperation({
255
+ resource: 'secret',
256
+ operation: 'delete',
257
+ surface: 'management',
258
+ scope,
259
+ outcome: 'success',
260
+ durationMs: Date.now() - startedAt
261
+ });
262
+ } catch (error) {
263
+ recordSecretsOperation({
264
+ resource: 'secret',
265
+ operation: 'delete',
266
+ surface: 'management',
267
+ scope,
268
+ outcome: classifySecretsOperationError(error),
269
+ durationMs: Date.now() - startedAt
270
+ });
271
+ throw error;
272
+ }
273
+ },
274
+ async deleteVariable (input) {
275
+ const startedAt = Date.now();
276
+ const scope = operationScope(input);
277
+ try {
278
+ validateSecretKeys([
279
+ input.key
280
+ ]);
281
+ const deleted = await deleteManagementEntries({
282
+ resource: 'variable',
283
+ input,
284
+ keys: [
285
+ input.key
286
+ ],
287
+ deleteRows: deleteVariableManagementRows,
288
+ eventType: VARIABLE_DELETED
289
+ });
290
+ if (deleted === 0) throw new VariableNotFoundError(input.key);
291
+ recordSecretsOperation({
292
+ resource: 'variable',
293
+ operation: 'delete',
294
+ surface: 'management',
295
+ scope,
296
+ outcome: 'success',
297
+ durationMs: Date.now() - startedAt
298
+ });
299
+ } catch (error) {
300
+ recordSecretsOperation({
301
+ resource: 'variable',
302
+ operation: 'delete',
303
+ surface: 'management',
304
+ scope,
305
+ outcome: classifySecretsOperationError(error),
306
+ durationMs: Date.now() - startedAt
307
+ });
308
+ throw error;
309
+ }
310
+ }
311
+ };
312
+ }
313
+ async function writeManagementEntries(params) {
314
+ let createdCount = 0;
315
+ let updatedCount = 0;
316
+ const rows = await db().transaction(async (tx)=>{
317
+ await lockWorkspaceEntries(params.input.workspaceId, tx);
318
+ const existingKeys = await params.listExistingKeys({
319
+ workspaceId: params.input.workspaceId,
320
+ projectId: normalizedProjectId(params.input),
321
+ keys: params.keys
322
+ }, tx);
323
+ createdCount = params.keys.length - existingKeys.size;
324
+ updatedCount = existingKeys.size;
325
+ await assertWorkspaceCap({
326
+ workspaceId: params.input.workspaceId,
327
+ namespace: '',
328
+ incomingEntries: createdCount,
329
+ tx
330
+ });
331
+ const rows = await params.writeRows(tx);
332
+ await Promise.all(params.keys.map((key)=>writeOutboxEvent(tx, secretsOutbox, {
333
+ type: existingKeys.has(key) ? params.eventTypes.updated : params.eventTypes.created,
334
+ payload: managementEventPayload(params.input, key)
335
+ })));
336
+ return rows;
337
+ });
338
+ recordSecretsEntriesMutated({
339
+ resource: params.resource,
340
+ operation: 'set',
341
+ effect: 'created',
342
+ surface: 'management',
343
+ count: createdCount
344
+ });
345
+ recordSecretsEntriesMutated({
346
+ resource: params.resource,
347
+ operation: 'set',
348
+ effect: 'updated',
349
+ surface: 'management',
350
+ count: updatedCount
351
+ });
352
+ return rows;
353
+ }
354
+ async function deleteManagementEntries(params) {
355
+ const deleted = await db().transaction(async (tx)=>{
356
+ await lockWorkspaceEntries(params.input.workspaceId, tx);
357
+ const rows = await params.deleteRows({
358
+ workspaceId: params.input.workspaceId,
359
+ projectId: normalizedProjectId(params.input),
360
+ keys: params.keys
361
+ }, tx);
362
+ await Promise.all(rows.map((row)=>writeOutboxEvent(tx, secretsOutbox, {
363
+ type: params.eventType,
364
+ payload: managementEventPayload(params.input, row.key)
365
+ })));
366
+ return rows.length;
367
+ });
368
+ recordSecretsEntriesMutated({
369
+ resource: params.resource,
370
+ operation: 'delete',
371
+ effect: 'deleted',
372
+ surface: 'management',
373
+ count: deleted
374
+ });
375
+ return deleted;
376
+ }
377
+ function normalizeEntries(entries) {
378
+ const valuesByKey = new Map();
379
+ for (const entry of entries){
380
+ if (valuesByKey.has(entry.key)) throw new SecretBatchDuplicateKeyError(entry.key);
381
+ valuesByKey.set(entry.key, entry.value);
382
+ }
383
+ return [
384
+ ...valuesByKey
385
+ ].map(([key, value])=>({
386
+ key,
387
+ value
388
+ }));
389
+ }
390
+ function managementEventPayload(input, key) {
391
+ return {
392
+ actorId: input.actorId,
393
+ workspaceId: input.workspaceId,
394
+ projectId: normalizedProjectId(input),
395
+ key
396
+ };
397
+ }
398
+ function assertFound(row) {
399
+ if (!row) throw new Error('Management write returned no row');
400
+ return row;
401
+ }
402
+
403
+ //# sourceMappingURL=management.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/management.ts"],"sourcesContent":["import {\n SECRET_CREATED,\n SECRET_DELETED,\n SECRET_UPDATED,\n type SecretsEventMap,\n VARIABLE_CREATED,\n VARIABLE_DELETED,\n VARIABLE_UPDATED,\n} from '@shipfox/api-secrets-dto';\nimport {writeOutboxEvent} from '@shipfox/node-outbox';\nimport {\n db,\n deleteSecretManagementRows,\n deleteVariableManagementRows,\n getSecretManagementRow,\n getVariableManagementRow,\n listExistingSecretManagementKeys,\n listExistingVariableManagementKeys,\n listSecretManagementRows,\n listVariableManagementRows,\n lockWorkspaceEntries,\n type SecretManagementRow,\n type StoreScope,\n secretsOutbox,\n type Tx,\n upsertSecretValueRows,\n upsertSecretVariableRows,\n} from '#db/index.js';\nimport type {SecretVariable} from '#db/schema/variables.js';\nimport {normalizedProjectId} from '#db/scope.js';\nimport {\n classifySecretsOperationError,\n operationScope,\n recordSecretsEntriesMutated,\n recordSecretsOperation,\n type SecretsMetricResource,\n} from '#metrics/instance.js';\nimport type {DekManager} from './dek-manager.js';\nimport {\n SecretBatchDuplicateKeyError,\n SecretNotFoundError,\n VariableNotFoundError,\n} from './errors.js';\nimport {fingerprintSecretValue} from './fingerprint.js';\nimport {assertWorkspaceCap, validateSecretKeys, validateValueBytes} from './store-validation.js';\nimport {encryptSecretValue} from './value-cipher.js';\n\nexport interface ManagementEntry {\n key: string;\n value: string;\n}\n\nexport interface ManagementWriteParams extends StoreScope {\n workspaceId: string;\n entries: ManagementEntry[];\n actorId: string;\n}\n\nexport interface ManagementKeyParams extends StoreScope {\n workspaceId: string;\n key: string;\n actorId: string;\n}\n\nexport interface ManagementListParams extends StoreScope {\n workspaceId: string;\n limit: number;\n cursor?: string | undefined;\n}\n\nexport function createSecretsManagementApi(params: {dekManager: DekManager}) {\n return {\n async listSecrets(input: ManagementListParams) {\n const startedAt = Date.now();\n const scope = operationScope(input);\n try {\n const result = await listSecretManagementRows(input);\n recordSecretsOperation({\n resource: 'secret',\n operation: 'list',\n surface: 'management',\n scope,\n outcome: 'success',\n durationMs: Date.now() - startedAt,\n });\n return result;\n } catch (error) {\n recordSecretsOperation({\n resource: 'secret',\n operation: 'list',\n surface: 'management',\n scope,\n outcome: classifySecretsOperationError(error),\n durationMs: Date.now() - startedAt,\n });\n throw error;\n }\n },\n async listVariables(input: ManagementListParams) {\n const startedAt = Date.now();\n const scope = operationScope(input);\n try {\n const result = await listVariableManagementRows(input);\n recordSecretsOperation({\n resource: 'variable',\n operation: 'list',\n surface: 'management',\n scope,\n outcome: 'success',\n durationMs: Date.now() - startedAt,\n });\n return result;\n } catch (error) {\n recordSecretsOperation({\n resource: 'variable',\n operation: 'list',\n surface: 'management',\n scope,\n outcome: classifySecretsOperationError(error),\n durationMs: Date.now() - startedAt,\n });\n throw error;\n }\n },\n async getVariable(\n input: StoreScope & {workspaceId: string; key: string},\n ): Promise<SecretVariable> {\n const startedAt = Date.now();\n const scope = operationScope(input);\n try {\n validateSecretKeys([input.key]);\n\n const variable = await getVariableManagementRow(input);\n if (!variable) throw new VariableNotFoundError(input.key);\n recordSecretsOperation({\n resource: 'variable',\n operation: 'get',\n surface: 'management',\n scope,\n outcome: 'success',\n durationMs: Date.now() - startedAt,\n });\n return variable;\n } catch (error) {\n recordSecretsOperation({\n resource: 'variable',\n operation: 'get',\n surface: 'management',\n scope,\n outcome: classifySecretsOperationError(error),\n durationMs: Date.now() - startedAt,\n });\n throw error;\n }\n },\n async setSecrets(input: ManagementWriteParams): Promise<SecretManagementRow[]> {\n const startedAt = Date.now();\n const scope = operationScope(input);\n try {\n const entries = normalizeEntries(input.entries);\n validateSecretKeys(entries.map((entry) => entry.key));\n validateValueBytes(entries.map((entry) => entry.value));\n if (entries.length === 0) {\n recordSecretsOperation({\n resource: 'secret',\n operation: 'set',\n surface: 'management',\n scope,\n outcome: 'success',\n durationMs: Date.now() - startedAt,\n });\n return [];\n }\n\n const projectId = normalizedProjectId(input);\n const dek = await params.dekManager.getPlaintextDek(input.workspaceId);\n const rows = await writeManagementEntries({\n resource: 'secret',\n input,\n keys: entries.map((entry) => entry.key),\n listExistingKeys: listExistingSecretManagementKeys,\n writeRows: async (tx) => {\n await upsertSecretValueRows(\n entries.map((entry) => ({\n workspaceId: input.workspaceId,\n projectId,\n namespace: '',\n key: entry.key,\n ciphertext: encryptSecretValue({\n dek,\n workspaceId: input.workspaceId,\n scope: {projectId},\n namespace: '',\n key: entry.key,\n value: entry.value,\n }),\n fingerprint: fingerprintSecretValue(entry.value, dek),\n lastEditedBy: input.actorId,\n })),\n tx,\n );\n const rows = await Promise.all(\n entries.map((entry) =>\n getSecretManagementRow(\n {workspaceId: input.workspaceId, projectId, key: entry.key},\n tx,\n ),\n ),\n );\n return rows.map(assertFound);\n },\n eventTypes: {created: SECRET_CREATED, updated: SECRET_UPDATED},\n });\n recordSecretsOperation({\n resource: 'secret',\n operation: 'set',\n surface: 'management',\n scope,\n outcome: 'success',\n durationMs: Date.now() - startedAt,\n });\n return rows;\n } catch (error) {\n recordSecretsOperation({\n resource: 'secret',\n operation: 'set',\n surface: 'management',\n scope,\n outcome: classifySecretsOperationError(error),\n durationMs: Date.now() - startedAt,\n });\n throw error;\n }\n },\n async setVariables(input: ManagementWriteParams): Promise<SecretVariable[]> {\n const startedAt = Date.now();\n const scope = operationScope(input);\n try {\n const entries = normalizeEntries(input.entries);\n validateSecretKeys(entries.map((entry) => entry.key));\n validateValueBytes(entries.map((entry) => entry.value));\n if (entries.length === 0) {\n recordSecretsOperation({\n resource: 'variable',\n operation: 'set',\n surface: 'management',\n scope,\n outcome: 'success',\n durationMs: Date.now() - startedAt,\n });\n return [];\n }\n\n const projectId = normalizedProjectId(input);\n const rows = await writeManagementEntries({\n resource: 'variable',\n input,\n keys: entries.map((entry) => entry.key),\n listExistingKeys: listExistingVariableManagementKeys,\n writeRows: async (tx) => {\n await upsertSecretVariableRows(\n entries.map((entry) => ({\n workspaceId: input.workspaceId,\n projectId,\n namespace: '',\n key: entry.key,\n value: entry.value,\n lastEditedBy: input.actorId,\n })),\n tx,\n );\n const rows = await Promise.all(\n entries.map((entry) =>\n getVariableManagementRow(\n {workspaceId: input.workspaceId, projectId, key: entry.key},\n tx,\n ),\n ),\n );\n return rows.map(assertFound);\n },\n eventTypes: {created: VARIABLE_CREATED, updated: VARIABLE_UPDATED},\n });\n recordSecretsOperation({\n resource: 'variable',\n operation: 'set',\n surface: 'management',\n scope,\n outcome: 'success',\n durationMs: Date.now() - startedAt,\n });\n return rows;\n } catch (error) {\n recordSecretsOperation({\n resource: 'variable',\n operation: 'set',\n surface: 'management',\n scope,\n outcome: classifySecretsOperationError(error),\n durationMs: Date.now() - startedAt,\n });\n throw error;\n }\n },\n async deleteSecret(input: ManagementKeyParams): Promise<void> {\n const startedAt = Date.now();\n const scope = operationScope(input);\n try {\n validateSecretKeys([input.key]);\n\n const deleted = await deleteManagementEntries({\n resource: 'secret',\n input,\n keys: [input.key],\n deleteRows: deleteSecretManagementRows,\n eventType: SECRET_DELETED,\n });\n if (deleted === 0) throw new SecretNotFoundError(input.key);\n recordSecretsOperation({\n resource: 'secret',\n operation: 'delete',\n surface: 'management',\n scope,\n outcome: 'success',\n durationMs: Date.now() - startedAt,\n });\n } catch (error) {\n recordSecretsOperation({\n resource: 'secret',\n operation: 'delete',\n surface: 'management',\n scope,\n outcome: classifySecretsOperationError(error),\n durationMs: Date.now() - startedAt,\n });\n throw error;\n }\n },\n async deleteVariable(input: ManagementKeyParams): Promise<void> {\n const startedAt = Date.now();\n const scope = operationScope(input);\n try {\n validateSecretKeys([input.key]);\n\n const deleted = await deleteManagementEntries({\n resource: 'variable',\n input,\n keys: [input.key],\n deleteRows: deleteVariableManagementRows,\n eventType: VARIABLE_DELETED,\n });\n if (deleted === 0) throw new VariableNotFoundError(input.key);\n recordSecretsOperation({\n resource: 'variable',\n operation: 'delete',\n surface: 'management',\n scope,\n outcome: 'success',\n durationMs: Date.now() - startedAt,\n });\n } catch (error) {\n recordSecretsOperation({\n resource: 'variable',\n operation: 'delete',\n surface: 'management',\n scope,\n outcome: classifySecretsOperationError(error),\n durationMs: Date.now() - startedAt,\n });\n throw error;\n }\n },\n };\n}\n\nasync function writeManagementEntries<Row extends {key: string}>(params: {\n resource: SecretsMetricResource;\n input: ManagementWriteParams;\n keys: string[];\n listExistingKeys: (\n params: StoreScope & {workspaceId: string; keys: string[]},\n tx: Tx,\n ) => Promise<Set<string>>;\n writeRows: (tx: Tx) => Promise<Row[]>;\n eventTypes: {\n created: typeof SECRET_CREATED | typeof VARIABLE_CREATED;\n updated: typeof SECRET_UPDATED | typeof VARIABLE_UPDATED;\n };\n}): Promise<Row[]> {\n let createdCount = 0;\n let updatedCount = 0;\n const rows = await db().transaction(async (tx) => {\n await lockWorkspaceEntries(params.input.workspaceId, tx);\n const existingKeys = await params.listExistingKeys(\n {\n workspaceId: params.input.workspaceId,\n projectId: normalizedProjectId(params.input),\n keys: params.keys,\n },\n tx,\n );\n createdCount = params.keys.length - existingKeys.size;\n updatedCount = existingKeys.size;\n await assertWorkspaceCap({\n workspaceId: params.input.workspaceId,\n namespace: '',\n incomingEntries: createdCount,\n tx,\n });\n\n const rows = await params.writeRows(tx);\n await Promise.all(\n params.keys.map((key) =>\n writeOutboxEvent<SecretsEventMap>(tx, secretsOutbox, {\n type: existingKeys.has(key) ? params.eventTypes.updated : params.eventTypes.created,\n payload: managementEventPayload(params.input, key),\n }),\n ),\n );\n return rows;\n });\n recordSecretsEntriesMutated({\n resource: params.resource,\n operation: 'set',\n effect: 'created',\n surface: 'management',\n count: createdCount,\n });\n recordSecretsEntriesMutated({\n resource: params.resource,\n operation: 'set',\n effect: 'updated',\n surface: 'management',\n count: updatedCount,\n });\n return rows;\n}\n\nasync function deleteManagementEntries<Row extends {key: string}>(params: {\n resource: SecretsMetricResource;\n input: ManagementKeyParams;\n keys: string[];\n deleteRows: (\n params: StoreScope & {workspaceId: string; keys: string[]},\n tx: Tx,\n ) => Promise<Row[]>;\n eventType: typeof SECRET_DELETED | typeof VARIABLE_DELETED;\n}): Promise<number> {\n const deleted = await db().transaction(async (tx) => {\n await lockWorkspaceEntries(params.input.workspaceId, tx);\n const rows = await params.deleteRows(\n {\n workspaceId: params.input.workspaceId,\n projectId: normalizedProjectId(params.input),\n keys: params.keys,\n },\n tx,\n );\n await Promise.all(\n rows.map((row) =>\n writeOutboxEvent<SecretsEventMap>(tx, secretsOutbox, {\n type: params.eventType,\n payload: managementEventPayload(params.input, row.key),\n }),\n ),\n );\n return rows.length;\n });\n recordSecretsEntriesMutated({\n resource: params.resource,\n operation: 'delete',\n effect: 'deleted',\n surface: 'management',\n count: deleted,\n });\n return deleted;\n}\n\nfunction normalizeEntries(entries: ManagementEntry[]): ManagementEntry[] {\n const valuesByKey = new Map<string, string>();\n for (const entry of entries) {\n if (valuesByKey.has(entry.key)) throw new SecretBatchDuplicateKeyError(entry.key);\n valuesByKey.set(entry.key, entry.value);\n }\n return [...valuesByKey].map(([key, value]) => ({key, value}));\n}\n\nfunction managementEventPayload(\n input: Pick<ManagementWriteParams, 'workspaceId' | 'projectId' | 'actorId'>,\n key: string,\n) {\n return {\n actorId: input.actorId,\n workspaceId: input.workspaceId,\n projectId: normalizedProjectId(input),\n key,\n };\n}\n\nfunction assertFound<Row>(row: Row | undefined): Row {\n if (!row) throw new Error('Management write returned no row');\n return row;\n}\n"],"names":["SECRET_CREATED","SECRET_DELETED","SECRET_UPDATED","VARIABLE_CREATED","VARIABLE_DELETED","VARIABLE_UPDATED","writeOutboxEvent","db","deleteSecretManagementRows","deleteVariableManagementRows","getSecretManagementRow","getVariableManagementRow","listExistingSecretManagementKeys","listExistingVariableManagementKeys","listSecretManagementRows","listVariableManagementRows","lockWorkspaceEntries","secretsOutbox","upsertSecretValueRows","upsertSecretVariableRows","normalizedProjectId","classifySecretsOperationError","operationScope","recordSecretsEntriesMutated","recordSecretsOperation","SecretBatchDuplicateKeyError","SecretNotFoundError","VariableNotFoundError","fingerprintSecretValue","assertWorkspaceCap","validateSecretKeys","validateValueBytes","encryptSecretValue","createSecretsManagementApi","params","listSecrets","input","startedAt","Date","now","scope","result","resource","operation","surface","outcome","durationMs","error","listVariables","getVariable","key","variable","setSecrets","entries","normalizeEntries","map","entry","value","length","projectId","dek","dekManager","getPlaintextDek","workspaceId","rows","writeManagementEntries","keys","listExistingKeys","writeRows","tx","namespace","ciphertext","fingerprint","lastEditedBy","actorId","Promise","all","assertFound","eventTypes","created","updated","setVariables","deleteSecret","deleted","deleteManagementEntries","deleteRows","eventType","deleteVariable","createdCount","updatedCount","transaction","existingKeys","size","incomingEntries","type","has","payload","managementEventPayload","effect","count","row","valuesByKey","Map","set","Error"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { KeyProvider } from './key-provider.js';
2
+ export interface RotateWorkspaceDataKeysResult {
3
+ rotated: number;
4
+ skipped: number;
5
+ }
6
+ export interface RotateWorkspaceDataKeysOptions {
7
+ workspaceIds?: string[] | undefined;
8
+ }
9
+ export declare function rotateWorkspaceDataKeysWithProvider(keyProvider: KeyProvider, options?: RotateWorkspaceDataKeysOptions): Promise<RotateWorkspaceDataKeysResult>;
10
+ //# sourceMappingURL=rotate-kek.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rotate-kek.d.ts","sourceRoot":"","sources":["../../src/core/rotate-kek.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,mBAAmB,CAAC;AAInD,MAAM,WAAW,6BAA6B;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,8BAA8B;IAC7C,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CACrC;AAED,wBAAsB,mCAAmC,CACvD,WAAW,EAAE,WAAW,EACxB,OAAO,GAAE,8BAAmC,GAC3C,OAAO,CAAC,6BAA6B,CAAC,CAqExC"}
@@ -0,0 +1,94 @@
1
+ import { listDataKeysPage, listDataKeyVersions, updateDataKeyWrapCas } from '#db/index.js';
2
+ import { classifyKekRotationError, recordSecretsKekRotation } from '#metrics/instance.js';
3
+ import { KekVersionStrandedError } from './errors.js';
4
+ const PAGE_SIZE = 100;
5
+ export async function rotateWorkspaceDataKeysWithProvider(keyProvider, options = {}) {
6
+ const startedAt = Date.now();
7
+ try {
8
+ const knownVersions = [
9
+ keyProvider.currentKeyVersion,
10
+ keyProvider.previousKeyVersion
11
+ ].filter((version)=>Boolean(version));
12
+ const unknownVersions = await listDataKeyVersions(knownVersions, {
13
+ workspaceIds: options.workspaceIds
14
+ });
15
+ if (unknownVersions.length > 0) throw new KekVersionStrandedError(unknownVersions[0]);
16
+ let rotated = 0;
17
+ let skipped = 0;
18
+ let skippedCurrent = 0;
19
+ let skippedRace = 0;
20
+ let afterWorkspaceId;
21
+ while(true){
22
+ const page = await listDataKeysPage({
23
+ afterWorkspaceId,
24
+ limit: PAGE_SIZE,
25
+ workspaceIds: options.workspaceIds
26
+ });
27
+ if (page.length === 0) break;
28
+ for (const row of page){
29
+ afterWorkspaceId = row.workspaceId;
30
+ if (row.kekVersion === keyProvider.currentKeyVersion) {
31
+ skipped += 1;
32
+ skippedCurrent += 1;
33
+ continue;
34
+ }
35
+ const plaintextDek = keyProvider.unwrapDek(row.workspaceId, row.wrappedDek, row.kekVersion);
36
+ try {
37
+ const wrapped = keyProvider.wrapDek(row.workspaceId, plaintextDek);
38
+ const updated = await updateDataKeyWrapCas({
39
+ workspaceId: row.workspaceId,
40
+ oldKekVersion: row.kekVersion,
41
+ wrappedDek: wrapped.wrappedDek,
42
+ kekVersion: wrapped.kekVersion
43
+ });
44
+ if (updated) rotated += 1;
45
+ else {
46
+ skipped += 1;
47
+ skippedRace += 1;
48
+ }
49
+ } finally{
50
+ plaintextDek.fill(0);
51
+ }
52
+ }
53
+ }
54
+ recordSecretsKekRotation({
55
+ outcome: 'rotated',
56
+ count: rotated
57
+ });
58
+ recordSecretsKekRotation({
59
+ outcome: 'skipped_current',
60
+ count: skippedCurrent
61
+ });
62
+ recordSecretsKekRotation({
63
+ outcome: 'skipped_race',
64
+ count: skippedRace
65
+ });
66
+ recordSecretsKekRotation({
67
+ outcome: rotationDurationOutcome({
68
+ rotated,
69
+ skippedCurrent,
70
+ skippedRace
71
+ }),
72
+ count: 0,
73
+ durationMs: Date.now() - startedAt
74
+ });
75
+ return {
76
+ rotated,
77
+ skipped
78
+ };
79
+ } catch (error) {
80
+ recordSecretsKekRotation({
81
+ outcome: classifyKekRotationError(error),
82
+ durationMs: Date.now() - startedAt
83
+ });
84
+ throw error;
85
+ }
86
+ }
87
+ function rotationDurationOutcome(params) {
88
+ if (params.rotated > 0) return 'rotated';
89
+ if (params.skippedRace > 0) return 'skipped_race';
90
+ if (params.skippedCurrent === 0) return 'none';
91
+ return 'skipped_current';
92
+ }
93
+
94
+ //# sourceMappingURL=rotate-kek.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/rotate-kek.ts"],"sourcesContent":["import {listDataKeysPage, listDataKeyVersions, updateDataKeyWrapCas} from '#db/index.js';\nimport {classifyKekRotationError, recordSecretsKekRotation} from '#metrics/instance.js';\nimport {KekVersionStrandedError} from './errors.js';\nimport type {KeyProvider} from './key-provider.js';\n\nconst PAGE_SIZE = 100;\n\nexport interface RotateWorkspaceDataKeysResult {\n rotated: number;\n skipped: number;\n}\n\nexport interface RotateWorkspaceDataKeysOptions {\n workspaceIds?: string[] | undefined;\n}\n\nexport async function rotateWorkspaceDataKeysWithProvider(\n keyProvider: KeyProvider,\n options: RotateWorkspaceDataKeysOptions = {},\n): Promise<RotateWorkspaceDataKeysResult> {\n const startedAt = Date.now();\n try {\n const knownVersions = [keyProvider.currentKeyVersion, keyProvider.previousKeyVersion].filter(\n (version): version is string => Boolean(version),\n );\n const unknownVersions = await listDataKeyVersions(knownVersions, {\n workspaceIds: options.workspaceIds,\n });\n if (unknownVersions.length > 0) throw new KekVersionStrandedError(unknownVersions[0] as string);\n\n let rotated = 0;\n let skipped = 0;\n let skippedCurrent = 0;\n let skippedRace = 0;\n let afterWorkspaceId: string | undefined;\n\n while (true) {\n const page = await listDataKeysPage({\n afterWorkspaceId,\n limit: PAGE_SIZE,\n workspaceIds: options.workspaceIds,\n });\n if (page.length === 0) break;\n\n for (const row of page) {\n afterWorkspaceId = row.workspaceId;\n if (row.kekVersion === keyProvider.currentKeyVersion) {\n skipped += 1;\n skippedCurrent += 1;\n continue;\n }\n\n const plaintextDek = keyProvider.unwrapDek(row.workspaceId, row.wrappedDek, row.kekVersion);\n try {\n const wrapped = keyProvider.wrapDek(row.workspaceId, plaintextDek);\n const updated = await updateDataKeyWrapCas({\n workspaceId: row.workspaceId,\n oldKekVersion: row.kekVersion,\n wrappedDek: wrapped.wrappedDek,\n kekVersion: wrapped.kekVersion,\n });\n if (updated) rotated += 1;\n else {\n skipped += 1;\n skippedRace += 1;\n }\n } finally {\n plaintextDek.fill(0);\n }\n }\n }\n\n recordSecretsKekRotation({outcome: 'rotated', count: rotated});\n recordSecretsKekRotation({outcome: 'skipped_current', count: skippedCurrent});\n recordSecretsKekRotation({outcome: 'skipped_race', count: skippedRace});\n recordSecretsKekRotation({\n outcome: rotationDurationOutcome({rotated, skippedCurrent, skippedRace}),\n count: 0,\n durationMs: Date.now() - startedAt,\n });\n return {rotated, skipped};\n } catch (error) {\n recordSecretsKekRotation({\n outcome: classifyKekRotationError(error),\n durationMs: Date.now() - startedAt,\n });\n throw error;\n }\n}\n\nfunction rotationDurationOutcome(params: {\n rotated: number;\n skippedCurrent: number;\n skippedRace: number;\n}) {\n if (params.rotated > 0) return 'rotated';\n if (params.skippedRace > 0) return 'skipped_race';\n if (params.skippedCurrent === 0) return 'none';\n return 'skipped_current';\n}\n"],"names":["listDataKeysPage","listDataKeyVersions","updateDataKeyWrapCas","classifyKekRotationError","recordSecretsKekRotation","KekVersionStrandedError","PAGE_SIZE","rotateWorkspaceDataKeysWithProvider","keyProvider","options","startedAt","Date","now","knownVersions","currentKeyVersion","previousKeyVersion","filter","version","Boolean","unknownVersions","workspaceIds","length","rotated","skipped","skippedCurrent","skippedRace","afterWorkspaceId","page","limit","row","workspaceId","kekVersion","plaintextDek","unwrapDek","wrappedDek","wrapped","wrapDek","updated","oldKekVersion","fill","outcome","count","rotationDurationOutcome","durationMs","error","params"],"mappings":"AAAA,SAAQA,gBAAgB,EAAEC,mBAAmB,EAAEC,oBAAoB,QAAO,eAAe;AACzF,SAAQC,wBAAwB,EAAEC,wBAAwB,QAAO,uBAAuB;AACxF,SAAQC,uBAAuB,QAAO,cAAc;AAGpD,MAAMC,YAAY;AAWlB,OAAO,eAAeC,oCACpBC,WAAwB,EACxBC,UAA0C,CAAC,CAAC;IAE5C,MAAMC,YAAYC,KAAKC,GAAG;IAC1B,IAAI;QACF,MAAMC,gBAAgB;YAACL,YAAYM,iBAAiB;YAAEN,YAAYO,kBAAkB;SAAC,CAACC,MAAM,CAC1F,CAACC,UAA+BC,QAAQD;QAE1C,MAAME,kBAAkB,MAAMlB,oBAAoBY,eAAe;YAC/DO,cAAcX,QAAQW,YAAY;QACpC;QACA,IAAID,gBAAgBE,MAAM,GAAG,GAAG,MAAM,IAAIhB,wBAAwBc,eAAe,CAAC,EAAE;QAEpF,IAAIG,UAAU;QACd,IAAIC,UAAU;QACd,IAAIC,iBAAiB;QACrB,IAAIC,cAAc;QAClB,IAAIC;QAEJ,MAAO,KAAM;YACX,MAAMC,OAAO,MAAM3B,iBAAiB;gBAClC0B;gBACAE,OAAOtB;gBACPc,cAAcX,QAAQW,YAAY;YACpC;YACA,IAAIO,KAAKN,MAAM,KAAK,GAAG;YAEvB,KAAK,MAAMQ,OAAOF,KAAM;gBACtBD,mBAAmBG,IAAIC,WAAW;gBAClC,IAAID,IAAIE,UAAU,KAAKvB,YAAYM,iBAAiB,EAAE;oBACpDS,WAAW;oBACXC,kBAAkB;oBAClB;gBACF;gBAEA,MAAMQ,eAAexB,YAAYyB,SAAS,CAACJ,IAAIC,WAAW,EAAED,IAAIK,UAAU,EAAEL,IAAIE,UAAU;gBAC1F,IAAI;oBACF,MAAMI,UAAU3B,YAAY4B,OAAO,CAACP,IAAIC,WAAW,EAAEE;oBACrD,MAAMK,UAAU,MAAMnC,qBAAqB;wBACzC4B,aAAaD,IAAIC,WAAW;wBAC5BQ,eAAeT,IAAIE,UAAU;wBAC7BG,YAAYC,QAAQD,UAAU;wBAC9BH,YAAYI,QAAQJ,UAAU;oBAChC;oBACA,IAAIM,SAASf,WAAW;yBACnB;wBACHC,WAAW;wBACXE,eAAe;oBACjB;gBACF,SAAU;oBACRO,aAAaO,IAAI,CAAC;gBACpB;YACF;QACF;QAEAnC,yBAAyB;YAACoC,SAAS;YAAWC,OAAOnB;QAAO;QAC5DlB,yBAAyB;YAACoC,SAAS;YAAmBC,OAAOjB;QAAc;QAC3EpB,yBAAyB;YAACoC,SAAS;YAAgBC,OAAOhB;QAAW;QACrErB,yBAAyB;YACvBoC,SAASE,wBAAwB;gBAACpB;gBAASE;gBAAgBC;YAAW;YACtEgB,OAAO;YACPE,YAAYhC,KAAKC,GAAG,KAAKF;QAC3B;QACA,OAAO;YAACY;YAASC;QAAO;IAC1B,EAAE,OAAOqB,OAAO;QACdxC,yBAAyB;YACvBoC,SAASrC,yBAAyByC;YAClCD,YAAYhC,KAAKC,GAAG,KAAKF;QAC3B;QACA,MAAMkC;IACR;AACF;AAEA,SAASF,wBAAwBG,MAIhC;IACC,IAAIA,OAAOvB,OAAO,GAAG,GAAG,OAAO;IAC/B,IAAIuB,OAAOpB,WAAW,GAAG,GAAG,OAAO;IACnC,IAAIoB,OAAOrB,cAAc,KAAK,GAAG,OAAO;IACxC,OAAO;AACT"}
@@ -0,0 +1,33 @@
1
+ import { type StoreScope } from '#db/index.js';
2
+ import type { DekManager } from './dek-manager.js';
3
+ import type { SecretStoreProvider } from './store-resolver.js';
4
+ export interface SetSecretsParams extends StoreScope {
5
+ workspaceId: string;
6
+ namespace?: string | undefined;
7
+ values: Record<string, string>;
8
+ editedBy?: string | null | undefined;
9
+ }
10
+ export interface DeleteSecretsParams extends StoreScope {
11
+ workspaceId: string;
12
+ namespace?: string | undefined;
13
+ keys?: string[] | undefined;
14
+ }
15
+ export declare function createSecretStoreApi(params: {
16
+ dekManager: DekManager;
17
+ resolveSecretStore: (name?: string | undefined) => SecretStoreProvider;
18
+ }): {
19
+ getSecret(input: StoreScope & {
20
+ workspaceId: string;
21
+ namespace?: string | undefined;
22
+ key: string;
23
+ store?: string;
24
+ }): Promise<string | null>;
25
+ getSecretsByNamespace(input: StoreScope & {
26
+ workspaceId: string;
27
+ namespace?: string | undefined;
28
+ store?: string;
29
+ }): Promise<Record<string, string>>;
30
+ setSecrets(input: SetSecretsParams): Promise<void>;
31
+ deleteSecrets(input: DeleteSecretsParams): Promise<number>;
32
+ };
33
+ //# sourceMappingURL=secret-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-store.d.ts","sourceRoot":"","sources":["../../src/core/secret-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,UAAU,EAEhB,MAAM,cAAc,CAAC;AAQtB,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,qBAAqB,CAAC;AAS7D,MAAM,WAAW,gBAAiB,SAAQ,UAAU;IAClD,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CACtC;AAED,MAAM,WAAW,mBAAoB,SAAQ,UAAU;IACrD,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CAC7B;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE;IAC3C,UAAU,EAAE,UAAU,CAAC;IACvB,kBAAkB,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,KAAK,mBAAmB,CAAC;CACxE;qBAGY,UAAU,GAAG;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC/B,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB;iCA+BM,UAAU,GAAG;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAC;sBA+BnE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;yBA+F7B,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC;EA2CnE"}