@checkstack/gitops-backend 0.1.2 → 0.2.1

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,55 @@
1
1
  # @checkstack/gitops-backend
2
2
 
3
+ ## 0.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 57d54de: Fix GitOps Healthcheck reconciliation engine and Kind Registry UI
8
+
9
+ - Mandated fully qualified IDs for all healthcheck strategies and collector definitions.
10
+ - Refactored the Kind Registry UI to display schema documentation in beautifully formatted, interactive YAML examples.
11
+ - Entity Envelope Fields and Base Spec Schema are now displayed in collapsed accordions.
12
+ - Fixed condition logic that broke the collector documentation display.
13
+ - Enhanced UX by dynamically injecting fully-qualified strategy variants directly into the YAML examples.
14
+
15
+ ## 0.2.0
16
+
17
+ ### Minor Changes
18
+
19
+ - 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.
20
+ - cb65e9d: ### Schema-driven secret resolution, rotation invalidation, and security hardening
21
+
22
+ **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`.
23
+
24
+ **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).
25
+
26
+ **New features**:
27
+
28
+ - Secrets can be referenced in **any string field** using `${{ secrets.NAME }}` syntax
29
+ - Inline interpolation is supported: `"postgres://user:${{ secrets.DB_PASS }}@host/db"`
30
+ - Resolution is **schema-driven** — reuses the existing `configString({ "x-secret": true })` pattern from DynamicForm
31
+ - Secret rotation now automatically invalidates affected entities, triggering re-reconciliation on the next sync cycle
32
+ - New `getSecretUsage` RPC endpoint to look up which entities reference a given secret
33
+ - Secrets UI now shows an expandable usage panel per secret showing referencing entities
34
+ - Reconciliation warnings: templates in non-secret fields are detected and surfaced in the provenance UI
35
+ - New `secretNameSchema` and `SECRET_NAME_REGEX` exports for validating secret names
36
+
37
+ **Security**:
38
+
39
+ - Secret names are validated at creation: must start with a letter, contain only `[a-zA-Z0-9_-]`, max 63 chars
40
+ - Secrets are validated to exist at sync time but **not pre-resolved** into the spec
41
+ - Templates in `metadata` fields are **rejected** to prevent secret leaks via display fields
42
+ - Only fields with `x-secret` schema annotations get resolved — no escape hatch
43
+ - Templates in non-secret fields emit warnings (stored in provenance, visible in UI) instead of silently passing
44
+
45
+ **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[] }`).
46
+
47
+ ### Patch Changes
48
+
49
+ - Updated dependencies [8ef367a]
50
+ - Updated dependencies [cb65e9d]
51
+ - @checkstack/gitops-common@0.2.0
52
+
3
53
  ## 0.1.2
4
54
 
5
55
  ### 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
+ }