@checkstack/gitops-backend 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,54 @@
1
1
  # @checkstack/gitops-backend
2
2
 
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 8ef367a: Added `registerSpecSchemaDocumentation` to EntityKindRegistry to allow plugins to provide detailed JSON Schemas for specific configurations. The frontend now displays these registered schemas as dropdown alternatives, improving the developer experience when authoring GitOps configurations.
8
+ - cb65e9d: ### Schema-driven secret resolution, rotation invalidation, and security hardening
9
+
10
+ **Breaking**: Replaced `{ secretRef: "..." }` object syntax with `${{ secrets.NAME }}` template interpolation. The `secretField()`, `secretRefSchema`, `isSecretRef`, `SecretRef`, and `ResolvedSecretField` exports have been removed from `@checkstack/gitops-common`.
11
+
12
+ **Breaking**: `ReconcileContext.resolveSecretsBySchema()` now returns `{ resolved: T; warnings: string[] }` instead of `T` directly. Plugins must destructure the result. Warnings contain messages for `${{ secrets.NAME }}` templates found in non-secret fields (fields without `x-secret` annotation).
13
+
14
+ **New features**:
15
+
16
+ - Secrets can be referenced in **any string field** using `${{ secrets.NAME }}` syntax
17
+ - Inline interpolation is supported: `"postgres://user:${{ secrets.DB_PASS }}@host/db"`
18
+ - Resolution is **schema-driven** — reuses the existing `configString({ "x-secret": true })` pattern from DynamicForm
19
+ - Secret rotation now automatically invalidates affected entities, triggering re-reconciliation on the next sync cycle
20
+ - New `getSecretUsage` RPC endpoint to look up which entities reference a given secret
21
+ - Secrets UI now shows an expandable usage panel per secret showing referencing entities
22
+ - Reconciliation warnings: templates in non-secret fields are detected and surfaced in the provenance UI
23
+ - New `secretNameSchema` and `SECRET_NAME_REGEX` exports for validating secret names
24
+
25
+ **Security**:
26
+
27
+ - Secret names are validated at creation: must start with a letter, contain only `[a-zA-Z0-9_-]`, max 63 chars
28
+ - Secrets are validated to exist at sync time but **not pre-resolved** into the spec
29
+ - Templates in `metadata` fields are **rejected** to prevent secret leaks via display fields
30
+ - Only fields with `x-secret` schema annotations get resolved — no escape hatch
31
+ - Templates in non-secret fields emit warnings (stored in provenance, visible in UI) instead of silently passing
32
+
33
+ **Migration**: Update YAML descriptors to use `${{ secrets.NAME }}` instead of `secretRef: name`. Remove `secretField()` imports from plugin schemas — use `configString({ "x-secret": true })` to annotate secret fields. Destructure `const { resolved } = await context.resolveSecretsBySchema({ value, schema })` (return type changed from `T` to `{ resolved: T; warnings: string[] }`).
34
+
35
+ ### Patch Changes
36
+
37
+ - Updated dependencies [8ef367a]
38
+ - Updated dependencies [cb65e9d]
39
+ - @checkstack/gitops-common@0.2.0
40
+
41
+ ## 0.1.2
42
+
43
+ ### Patch Changes
44
+
45
+ - 79cf5f8: ### GitOps: Fix sync lifecycle management
46
+
47
+ - Schedule recurring sync job immediately when creating a provider (previously required server restart)
48
+ - Reschedule recurring job when provider's sync interval is updated
49
+ - Cancel recurring job when provider is deleted
50
+ - Fix manual sync trigger being silently dropped due to job ID deduplication
51
+
3
52
  ## 0.1.1
4
53
 
5
54
  ### Patch Changes
@@ -0,0 +1 @@
1
+ ALTER TABLE "provenance" ADD COLUMN "secret_refs" text[] DEFAULT '{}';
@@ -0,0 +1 @@
1
+ ALTER TABLE "provenance" ADD COLUMN "warnings" text[] DEFAULT '{}';
@@ -0,0 +1,317 @@
1
+ {
2
+ "id": "c1ccd846-04bb-4799-b93b-26ae781e01d3",
3
+ "prevId": "01bbd45b-d962-4e98-b25d-53310f487822",
4
+ "version": "7",
5
+ "dialect": "postgresql",
6
+ "tables": {
7
+ "public.provenance": {
8
+ "name": "provenance",
9
+ "schema": "",
10
+ "columns": {
11
+ "id": {
12
+ "name": "id",
13
+ "type": "text",
14
+ "primaryKey": true,
15
+ "notNull": true
16
+ },
17
+ "api_version": {
18
+ "name": "api_version",
19
+ "type": "text",
20
+ "primaryKey": false,
21
+ "notNull": true
22
+ },
23
+ "kind": {
24
+ "name": "kind",
25
+ "type": "text",
26
+ "primaryKey": false,
27
+ "notNull": true
28
+ },
29
+ "entity_name": {
30
+ "name": "entity_name",
31
+ "type": "text",
32
+ "primaryKey": false,
33
+ "notNull": true
34
+ },
35
+ "entity_id": {
36
+ "name": "entity_id",
37
+ "type": "text",
38
+ "primaryKey": false,
39
+ "notNull": true
40
+ },
41
+ "provider_id": {
42
+ "name": "provider_id",
43
+ "type": "text",
44
+ "primaryKey": false,
45
+ "notNull": true
46
+ },
47
+ "repository": {
48
+ "name": "repository",
49
+ "type": "text",
50
+ "primaryKey": false,
51
+ "notNull": true
52
+ },
53
+ "file_path": {
54
+ "name": "file_path",
55
+ "type": "text",
56
+ "primaryKey": false,
57
+ "notNull": true
58
+ },
59
+ "last_sync_hash": {
60
+ "name": "last_sync_hash",
61
+ "type": "text",
62
+ "primaryKey": false,
63
+ "notNull": true
64
+ },
65
+ "secret_refs": {
66
+ "name": "secret_refs",
67
+ "type": "text[]",
68
+ "primaryKey": false,
69
+ "notNull": false,
70
+ "default": "'{}'"
71
+ },
72
+ "status": {
73
+ "name": "status",
74
+ "type": "provenance_status",
75
+ "typeSchema": "public",
76
+ "primaryKey": false,
77
+ "notNull": true,
78
+ "default": "'synced'"
79
+ },
80
+ "error_message": {
81
+ "name": "error_message",
82
+ "type": "text",
83
+ "primaryKey": false,
84
+ "notNull": false
85
+ },
86
+ "last_synced_at": {
87
+ "name": "last_synced_at",
88
+ "type": "timestamp",
89
+ "primaryKey": false,
90
+ "notNull": true,
91
+ "default": "now()"
92
+ },
93
+ "created_at": {
94
+ "name": "created_at",
95
+ "type": "timestamp",
96
+ "primaryKey": false,
97
+ "notNull": true,
98
+ "default": "now()"
99
+ }
100
+ },
101
+ "indexes": {},
102
+ "foreignKeys": {
103
+ "provenance_provider_id_providers_id_fk": {
104
+ "name": "provenance_provider_id_providers_id_fk",
105
+ "tableFrom": "provenance",
106
+ "tableTo": "providers",
107
+ "columnsFrom": [
108
+ "provider_id"
109
+ ],
110
+ "columnsTo": [
111
+ "id"
112
+ ],
113
+ "onDelete": "cascade",
114
+ "onUpdate": "no action"
115
+ }
116
+ },
117
+ "compositePrimaryKeys": {},
118
+ "uniqueConstraints": {},
119
+ "policies": {},
120
+ "checkConstraints": {},
121
+ "isRLSEnabled": false
122
+ },
123
+ "public.providers": {
124
+ "name": "providers",
125
+ "schema": "",
126
+ "columns": {
127
+ "id": {
128
+ "name": "id",
129
+ "type": "text",
130
+ "primaryKey": true,
131
+ "notNull": true
132
+ },
133
+ "type": {
134
+ "name": "type",
135
+ "type": "provider_type",
136
+ "typeSchema": "public",
137
+ "primaryKey": false,
138
+ "notNull": true
139
+ },
140
+ "target": {
141
+ "name": "target",
142
+ "type": "text",
143
+ "primaryKey": false,
144
+ "notNull": true
145
+ },
146
+ "path_pattern": {
147
+ "name": "path_pattern",
148
+ "type": "text",
149
+ "primaryKey": false,
150
+ "notNull": true
151
+ },
152
+ "auth_token": {
153
+ "name": "auth_token",
154
+ "type": "text",
155
+ "primaryKey": false,
156
+ "notNull": false
157
+ },
158
+ "base_url": {
159
+ "name": "base_url",
160
+ "type": "text",
161
+ "primaryKey": false,
162
+ "notNull": false
163
+ },
164
+ "sync_interval": {
165
+ "name": "sync_interval",
166
+ "type": "integer",
167
+ "primaryKey": false,
168
+ "notNull": true,
169
+ "default": 300
170
+ },
171
+ "deletion_policy": {
172
+ "name": "deletion_policy",
173
+ "type": "deletion_policy",
174
+ "typeSchema": "public",
175
+ "primaryKey": false,
176
+ "notNull": true,
177
+ "default": "'orphan'"
178
+ },
179
+ "last_sync_at": {
180
+ "name": "last_sync_at",
181
+ "type": "timestamp",
182
+ "primaryKey": false,
183
+ "notNull": false
184
+ },
185
+ "last_sync_error": {
186
+ "name": "last_sync_error",
187
+ "type": "text",
188
+ "primaryKey": false,
189
+ "notNull": false
190
+ },
191
+ "created_at": {
192
+ "name": "created_at",
193
+ "type": "timestamp",
194
+ "primaryKey": false,
195
+ "notNull": true,
196
+ "default": "now()"
197
+ },
198
+ "updated_at": {
199
+ "name": "updated_at",
200
+ "type": "timestamp",
201
+ "primaryKey": false,
202
+ "notNull": true,
203
+ "default": "now()"
204
+ }
205
+ },
206
+ "indexes": {},
207
+ "foreignKeys": {},
208
+ "compositePrimaryKeys": {},
209
+ "uniqueConstraints": {},
210
+ "policies": {},
211
+ "checkConstraints": {},
212
+ "isRLSEnabled": false
213
+ },
214
+ "public.secrets": {
215
+ "name": "secrets",
216
+ "schema": "",
217
+ "columns": {
218
+ "id": {
219
+ "name": "id",
220
+ "type": "text",
221
+ "primaryKey": true,
222
+ "notNull": true
223
+ },
224
+ "name": {
225
+ "name": "name",
226
+ "type": "text",
227
+ "primaryKey": false,
228
+ "notNull": true
229
+ },
230
+ "encrypted_value": {
231
+ "name": "encrypted_value",
232
+ "type": "text",
233
+ "primaryKey": false,
234
+ "notNull": true
235
+ },
236
+ "description": {
237
+ "name": "description",
238
+ "type": "text",
239
+ "primaryKey": false,
240
+ "notNull": false
241
+ },
242
+ "created_by": {
243
+ "name": "created_by",
244
+ "type": "text",
245
+ "primaryKey": false,
246
+ "notNull": false
247
+ },
248
+ "created_at": {
249
+ "name": "created_at",
250
+ "type": "timestamp",
251
+ "primaryKey": false,
252
+ "notNull": true,
253
+ "default": "now()"
254
+ },
255
+ "updated_at": {
256
+ "name": "updated_at",
257
+ "type": "timestamp",
258
+ "primaryKey": false,
259
+ "notNull": true,
260
+ "default": "now()"
261
+ }
262
+ },
263
+ "indexes": {},
264
+ "foreignKeys": {},
265
+ "compositePrimaryKeys": {},
266
+ "uniqueConstraints": {
267
+ "secrets_name_unique": {
268
+ "name": "secrets_name_unique",
269
+ "nullsNotDistinct": false,
270
+ "columns": [
271
+ "name"
272
+ ]
273
+ }
274
+ },
275
+ "policies": {},
276
+ "checkConstraints": {},
277
+ "isRLSEnabled": false
278
+ }
279
+ },
280
+ "enums": {
281
+ "public.deletion_policy": {
282
+ "name": "deletion_policy",
283
+ "schema": "public",
284
+ "values": [
285
+ "orphan",
286
+ "auto"
287
+ ]
288
+ },
289
+ "public.provenance_status": {
290
+ "name": "provenance_status",
291
+ "schema": "public",
292
+ "values": [
293
+ "synced",
294
+ "error",
295
+ "orphaned"
296
+ ]
297
+ },
298
+ "public.provider_type": {
299
+ "name": "provider_type",
300
+ "schema": "public",
301
+ "values": [
302
+ "github",
303
+ "gitlab"
304
+ ]
305
+ }
306
+ },
307
+ "schemas": {},
308
+ "sequences": {},
309
+ "roles": {},
310
+ "policies": {},
311
+ "views": {},
312
+ "_meta": {
313
+ "columns": {},
314
+ "schemas": {},
315
+ "tables": {}
316
+ }
317
+ }
@@ -0,0 +1,324 @@
1
+ {
2
+ "id": "7b64a31e-1aec-46e5-bf15-013bef1a5e9c",
3
+ "prevId": "c1ccd846-04bb-4799-b93b-26ae781e01d3",
4
+ "version": "7",
5
+ "dialect": "postgresql",
6
+ "tables": {
7
+ "public.provenance": {
8
+ "name": "provenance",
9
+ "schema": "",
10
+ "columns": {
11
+ "id": {
12
+ "name": "id",
13
+ "type": "text",
14
+ "primaryKey": true,
15
+ "notNull": true
16
+ },
17
+ "api_version": {
18
+ "name": "api_version",
19
+ "type": "text",
20
+ "primaryKey": false,
21
+ "notNull": true
22
+ },
23
+ "kind": {
24
+ "name": "kind",
25
+ "type": "text",
26
+ "primaryKey": false,
27
+ "notNull": true
28
+ },
29
+ "entity_name": {
30
+ "name": "entity_name",
31
+ "type": "text",
32
+ "primaryKey": false,
33
+ "notNull": true
34
+ },
35
+ "entity_id": {
36
+ "name": "entity_id",
37
+ "type": "text",
38
+ "primaryKey": false,
39
+ "notNull": true
40
+ },
41
+ "provider_id": {
42
+ "name": "provider_id",
43
+ "type": "text",
44
+ "primaryKey": false,
45
+ "notNull": true
46
+ },
47
+ "repository": {
48
+ "name": "repository",
49
+ "type": "text",
50
+ "primaryKey": false,
51
+ "notNull": true
52
+ },
53
+ "file_path": {
54
+ "name": "file_path",
55
+ "type": "text",
56
+ "primaryKey": false,
57
+ "notNull": true
58
+ },
59
+ "last_sync_hash": {
60
+ "name": "last_sync_hash",
61
+ "type": "text",
62
+ "primaryKey": false,
63
+ "notNull": true
64
+ },
65
+ "secret_refs": {
66
+ "name": "secret_refs",
67
+ "type": "text[]",
68
+ "primaryKey": false,
69
+ "notNull": false,
70
+ "default": "'{}'"
71
+ },
72
+ "status": {
73
+ "name": "status",
74
+ "type": "provenance_status",
75
+ "typeSchema": "public",
76
+ "primaryKey": false,
77
+ "notNull": true,
78
+ "default": "'synced'"
79
+ },
80
+ "error_message": {
81
+ "name": "error_message",
82
+ "type": "text",
83
+ "primaryKey": false,
84
+ "notNull": false
85
+ },
86
+ "warnings": {
87
+ "name": "warnings",
88
+ "type": "text[]",
89
+ "primaryKey": false,
90
+ "notNull": false,
91
+ "default": "'{}'"
92
+ },
93
+ "last_synced_at": {
94
+ "name": "last_synced_at",
95
+ "type": "timestamp",
96
+ "primaryKey": false,
97
+ "notNull": true,
98
+ "default": "now()"
99
+ },
100
+ "created_at": {
101
+ "name": "created_at",
102
+ "type": "timestamp",
103
+ "primaryKey": false,
104
+ "notNull": true,
105
+ "default": "now()"
106
+ }
107
+ },
108
+ "indexes": {},
109
+ "foreignKeys": {
110
+ "provenance_provider_id_providers_id_fk": {
111
+ "name": "provenance_provider_id_providers_id_fk",
112
+ "tableFrom": "provenance",
113
+ "tableTo": "providers",
114
+ "columnsFrom": [
115
+ "provider_id"
116
+ ],
117
+ "columnsTo": [
118
+ "id"
119
+ ],
120
+ "onDelete": "cascade",
121
+ "onUpdate": "no action"
122
+ }
123
+ },
124
+ "compositePrimaryKeys": {},
125
+ "uniqueConstraints": {},
126
+ "policies": {},
127
+ "checkConstraints": {},
128
+ "isRLSEnabled": false
129
+ },
130
+ "public.providers": {
131
+ "name": "providers",
132
+ "schema": "",
133
+ "columns": {
134
+ "id": {
135
+ "name": "id",
136
+ "type": "text",
137
+ "primaryKey": true,
138
+ "notNull": true
139
+ },
140
+ "type": {
141
+ "name": "type",
142
+ "type": "provider_type",
143
+ "typeSchema": "public",
144
+ "primaryKey": false,
145
+ "notNull": true
146
+ },
147
+ "target": {
148
+ "name": "target",
149
+ "type": "text",
150
+ "primaryKey": false,
151
+ "notNull": true
152
+ },
153
+ "path_pattern": {
154
+ "name": "path_pattern",
155
+ "type": "text",
156
+ "primaryKey": false,
157
+ "notNull": true
158
+ },
159
+ "auth_token": {
160
+ "name": "auth_token",
161
+ "type": "text",
162
+ "primaryKey": false,
163
+ "notNull": false
164
+ },
165
+ "base_url": {
166
+ "name": "base_url",
167
+ "type": "text",
168
+ "primaryKey": false,
169
+ "notNull": false
170
+ },
171
+ "sync_interval": {
172
+ "name": "sync_interval",
173
+ "type": "integer",
174
+ "primaryKey": false,
175
+ "notNull": true,
176
+ "default": 300
177
+ },
178
+ "deletion_policy": {
179
+ "name": "deletion_policy",
180
+ "type": "deletion_policy",
181
+ "typeSchema": "public",
182
+ "primaryKey": false,
183
+ "notNull": true,
184
+ "default": "'orphan'"
185
+ },
186
+ "last_sync_at": {
187
+ "name": "last_sync_at",
188
+ "type": "timestamp",
189
+ "primaryKey": false,
190
+ "notNull": false
191
+ },
192
+ "last_sync_error": {
193
+ "name": "last_sync_error",
194
+ "type": "text",
195
+ "primaryKey": false,
196
+ "notNull": false
197
+ },
198
+ "created_at": {
199
+ "name": "created_at",
200
+ "type": "timestamp",
201
+ "primaryKey": false,
202
+ "notNull": true,
203
+ "default": "now()"
204
+ },
205
+ "updated_at": {
206
+ "name": "updated_at",
207
+ "type": "timestamp",
208
+ "primaryKey": false,
209
+ "notNull": true,
210
+ "default": "now()"
211
+ }
212
+ },
213
+ "indexes": {},
214
+ "foreignKeys": {},
215
+ "compositePrimaryKeys": {},
216
+ "uniqueConstraints": {},
217
+ "policies": {},
218
+ "checkConstraints": {},
219
+ "isRLSEnabled": false
220
+ },
221
+ "public.secrets": {
222
+ "name": "secrets",
223
+ "schema": "",
224
+ "columns": {
225
+ "id": {
226
+ "name": "id",
227
+ "type": "text",
228
+ "primaryKey": true,
229
+ "notNull": true
230
+ },
231
+ "name": {
232
+ "name": "name",
233
+ "type": "text",
234
+ "primaryKey": false,
235
+ "notNull": true
236
+ },
237
+ "encrypted_value": {
238
+ "name": "encrypted_value",
239
+ "type": "text",
240
+ "primaryKey": false,
241
+ "notNull": true
242
+ },
243
+ "description": {
244
+ "name": "description",
245
+ "type": "text",
246
+ "primaryKey": false,
247
+ "notNull": false
248
+ },
249
+ "created_by": {
250
+ "name": "created_by",
251
+ "type": "text",
252
+ "primaryKey": false,
253
+ "notNull": false
254
+ },
255
+ "created_at": {
256
+ "name": "created_at",
257
+ "type": "timestamp",
258
+ "primaryKey": false,
259
+ "notNull": true,
260
+ "default": "now()"
261
+ },
262
+ "updated_at": {
263
+ "name": "updated_at",
264
+ "type": "timestamp",
265
+ "primaryKey": false,
266
+ "notNull": true,
267
+ "default": "now()"
268
+ }
269
+ },
270
+ "indexes": {},
271
+ "foreignKeys": {},
272
+ "compositePrimaryKeys": {},
273
+ "uniqueConstraints": {
274
+ "secrets_name_unique": {
275
+ "name": "secrets_name_unique",
276
+ "nullsNotDistinct": false,
277
+ "columns": [
278
+ "name"
279
+ ]
280
+ }
281
+ },
282
+ "policies": {},
283
+ "checkConstraints": {},
284
+ "isRLSEnabled": false
285
+ }
286
+ },
287
+ "enums": {
288
+ "public.deletion_policy": {
289
+ "name": "deletion_policy",
290
+ "schema": "public",
291
+ "values": [
292
+ "orphan",
293
+ "auto"
294
+ ]
295
+ },
296
+ "public.provenance_status": {
297
+ "name": "provenance_status",
298
+ "schema": "public",
299
+ "values": [
300
+ "synced",
301
+ "error",
302
+ "orphaned"
303
+ ]
304
+ },
305
+ "public.provider_type": {
306
+ "name": "provider_type",
307
+ "schema": "public",
308
+ "values": [
309
+ "github",
310
+ "gitlab"
311
+ ]
312
+ }
313
+ },
314
+ "schemas": {},
315
+ "sequences": {},
316
+ "roles": {},
317
+ "policies": {},
318
+ "views": {},
319
+ "_meta": {
320
+ "columns": {},
321
+ "schemas": {},
322
+ "tables": {}
323
+ }
324
+ }