flecto 2.0.0 → 3.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.
- package/CHANGELOG.md +533 -0
- package/README.md +345 -211
- package/index.js +826 -77
- package/package.json +9 -7
- package/schemas/flecto-policy-pack-2.0.json +129 -0
- package/src/alerter.js +24 -6
- package/src/config.js +135 -9
- package/src/differ.js +154 -47
- package/src/documents.js +106 -0
- package/src/encrypted.js +573 -0
- package/src/notifiers.js +430 -0
- package/src/packs/compose.json +45 -0
- package/src/packs/default.json +23 -1
- package/src/packs/kubernetes.json +112 -0
- package/src/packs/node-runtime.json +44 -0
- package/src/packs/sops.json +61 -0
- package/src/packs/strict-prod.json +11 -1
- package/src/packs/terraform.json +120 -0
- package/src/parser.js +189 -20
- package/src/policy-test.js +124 -0
- package/src/policy.js +815 -30
- package/src/pr-comment.js +480 -0
- package/src/renderer.js +75 -18
- package/src/report.js +653 -0
- package/src/secrets.js +316 -0
- package/src/terraform.js +500 -0
- package/src/watcher.js +27 -15
package/README.md
CHANGED
|
@@ -1,21 +1,39 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="docs/assets/flecto-hero.png" alt="Flecto — semantic config watcher" width="920"/>
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
<h1 align="center">Flecto</h1>
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
<p align="center">
|
|
8
|
+
<strong>Know what your config actually changed — and whether it's risky.</strong>
|
|
9
|
+
</p>
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/flecto"><img alt="npm" src="https://img.shields.io/npm/v/flecto?style=flat-square&color=34d399&labelColor=0b1220"/></a>
|
|
13
|
+
<a href="https://github.com/myselfsiddharth/Flecto/actions/workflows/ci.yml"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/myselfsiddharth/Flecto/ci.yml?branch=main&style=flat-square&label=CI&labelColor=0b1220"/></a>
|
|
14
|
+
<a href="LICENSE"><img alt="MIT" src="https://img.shields.io/badge/license-MIT-8fa3bf?style=flat-square&labelColor=0b1220"/></a>
|
|
15
|
+
<a href="#documentation"><img alt="Docs" src="https://img.shields.io/badge/docs-read-34d399?style=flat-square&labelColor=0b1220"/></a>
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
<p align="center">
|
|
19
|
+
<img src="docs/assets/demo-watch.svg" alt="Flecto reporting semantic config changes in the terminal" width="920"/>
|
|
20
|
+
</p>
|
|
13
21
|
|
|
14
22
|
---
|
|
15
23
|
|
|
16
|
-
|
|
24
|
+
Config drives the parts of a system that break loudest: connection pools, feature
|
|
25
|
+
flags, TLS, retries, secrets. But we still review it as text — so a reordered key
|
|
26
|
+
looks identical to a doubled pool size, and `debug: true` slips through in a
|
|
27
|
+
40-line formatting diff.
|
|
17
28
|
|
|
18
|
-
|
|
29
|
+
Flecto reads config as structure, not lines. It tells you what changed in plain
|
|
30
|
+
English, flags what looks risky, and gives you an exit code to gate on.
|
|
31
|
+
|
|
32
|
+
| Reviewing config without Flecto | With Flecto |
|
|
33
|
+
|---|---|
|
|
34
|
+
| `+ 40 lines of YAML noise` | `~ database.pool_size: 5 → 20` |
|
|
35
|
+
| Hope someone notices `debug: true` | Policy finding → build fails |
|
|
36
|
+
| "Something in `.env` changed" | The exact keys, with secrets masked |
|
|
19
37
|
|
|
20
38
|
---
|
|
21
39
|
|
|
@@ -25,340 +43,456 @@ Standard file watchers tell you *a file changed*. Flecto tells you *what* change
|
|
|
25
43
|
npm install -g flecto
|
|
26
44
|
```
|
|
27
45
|
|
|
28
|
-
|
|
46
|
+
Requires **Node.js 20.19.0+**. Verify:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
flecto --version
|
|
50
|
+
flecto doctor
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Prefer not to install globally? Every example below works with
|
|
54
|
+
`npx --yes flecto@3` instead of `flecto`.
|
|
29
55
|
|
|
30
56
|
---
|
|
31
57
|
|
|
32
|
-
## Quick
|
|
58
|
+
## Quick start
|
|
59
|
+
|
|
60
|
+
A complete walkthrough, start to finish. Copy-paste it anywhere.
|
|
33
61
|
|
|
34
|
-
|
|
62
|
+
**1. Create a config file to track.**
|
|
35
63
|
|
|
36
64
|
```bash
|
|
37
|
-
flecto
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
65
|
+
mkdir flecto-demo && cd flecto-demo && mkdir config
|
|
66
|
+
cat > config/prod.yaml <<'EOF'
|
|
67
|
+
database:
|
|
68
|
+
host: db.internal
|
|
69
|
+
pool_size: 5
|
|
70
|
+
ssl: true
|
|
71
|
+
logging:
|
|
72
|
+
level: info
|
|
73
|
+
debug: false
|
|
74
|
+
EOF
|
|
41
75
|
```
|
|
42
76
|
|
|
43
|
-
|
|
77
|
+
**2. Save it as your baseline.**
|
|
44
78
|
|
|
45
|
-
|
|
79
|
+
```bash
|
|
80
|
+
flecto watch config/prod.yaml --snapshot
|
|
81
|
+
```
|
|
46
82
|
|
|
47
|
-
|
|
83
|
+
```
|
|
84
|
+
✓ Snapshot saved: /path/to/flecto-demo/.flecto-snapshots/4b8cbbd70d1832a2.json
|
|
85
|
+
```
|
|
48
86
|
|
|
49
|
-
|
|
87
|
+
**3. Make the kind of edit that causes incidents.**
|
|
50
88
|
|
|
51
89
|
```bash
|
|
52
|
-
|
|
90
|
+
cat > config/prod.yaml <<'EOF'
|
|
91
|
+
database:
|
|
92
|
+
host: db.internal
|
|
93
|
+
pool_size: 20
|
|
94
|
+
ssl: true
|
|
95
|
+
logging:
|
|
96
|
+
level: info
|
|
97
|
+
debug: true
|
|
98
|
+
EOF
|
|
53
99
|
```
|
|
54
100
|
|
|
55
|
-
|
|
101
|
+
**4. Ask what changed.**
|
|
56
102
|
|
|
57
103
|
```bash
|
|
58
|
-
flecto watch config/prod.yaml --
|
|
104
|
+
flecto watch config/prod.yaml --diff
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
/path/to/flecto-demo/config/prod.yaml — 2 changes from snapshot:
|
|
109
|
+
~ database.pool_size: 5 → 20
|
|
110
|
+
~ logging.debug: false → true
|
|
59
111
|
```
|
|
60
112
|
|
|
61
|
-
|
|
113
|
+
Two sentences instead of a diff you have to interpret. Now let Flecto judge it:
|
|
62
114
|
|
|
63
115
|
```bash
|
|
64
|
-
flecto
|
|
116
|
+
flecto ci config/prod.yaml --format github-annotations
|
|
65
117
|
```
|
|
66
118
|
|
|
67
|
-
|
|
119
|
+
```
|
|
120
|
+
::warning title=flecto changed::database.pool_size
|
|
121
|
+
::warning title=flecto changed::logging.debug
|
|
122
|
+
::warning title=flecto policy pool-size-jump [default]::database.pool_size: Pool size increased from 5 to 20 (>=2x).
|
|
123
|
+
::error title=flecto policy dangerous-toggle-enabled [default]::logging.debug: Potentially dangerous toggle enabled.
|
|
124
|
+
```
|
|
68
125
|
|
|
69
|
-
|
|
70
|
-
|---|---|
|
|
71
|
-
| `meta.timestamp` | That exact key |
|
|
72
|
-
| `meta` | Everything under `meta.*` |
|
|
73
|
-
| `servers[*].meta.timestamp` | That key inside any array item |
|
|
74
|
-
| `**.updated_at` | Any key named `updated_at`, anywhere |
|
|
126
|
+
Exit code `1`. In CI, that's a failed build — before the change ships.
|
|
75
127
|
|
|
76
|
-
|
|
128
|
+
**5. Watch it live.** Leave this running and edit the file in another window:
|
|
77
129
|
|
|
78
130
|
```bash
|
|
79
|
-
flecto watch .
|
|
131
|
+
flecto watch config/prod.yaml
|
|
80
132
|
```
|
|
81
133
|
|
|
82
|
-
|
|
134
|
+
```
|
|
135
|
+
flecto watching /path/to/flecto-demo/config/prod.yaml
|
|
136
|
+
Press Ctrl+C to stop.
|
|
83
137
|
|
|
84
|
-
|
|
138
|
+
[18:24:48] /path/to/flecto-demo/config/prod.yaml — 2 changes
|
|
139
|
+
~ database.pool_size: 5 → 20
|
|
140
|
+
~ logging.debug: false → true
|
|
141
|
+
! policy(warn) [default] database.pool_size: Pool size increased from 5 to 20 (>=2x).
|
|
142
|
+
! policy(error) [default] logging.debug: Potentially dangerous toggle enabled.
|
|
143
|
+
```
|
|
85
144
|
|
|
86
|
-
|
|
87
|
-
|
|
145
|
+
That's the whole product. Everything below is depth.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## What you can do with it
|
|
150
|
+
|
|
151
|
+
### Catch risky changes before they merge
|
|
152
|
+
|
|
153
|
+
Add one step to your workflow and risky config edits show up as annotations on
|
|
154
|
+
the pull request:
|
|
155
|
+
|
|
156
|
+
```yaml
|
|
157
|
+
permissions:
|
|
158
|
+
contents: read
|
|
159
|
+
|
|
160
|
+
steps:
|
|
161
|
+
- uses: actions/checkout@v7
|
|
162
|
+
with:
|
|
163
|
+
fetch-depth: 2
|
|
164
|
+
- uses: myselfsiddharth/Flecto/.github/actions/flecto-ci@main
|
|
165
|
+
with:
|
|
166
|
+
targets: config/**/*.{yaml,yml,json,toml,ini}
|
|
167
|
+
snapshot-ref: HEAD~1
|
|
88
168
|
```
|
|
89
169
|
|
|
90
|
-
|
|
170
|
+
Prefer a summary nobody can miss? `--format pr-comment` renders the changes and
|
|
171
|
+
policy findings as markdown and, when you opt in with `--pr-comment-post` inside
|
|
172
|
+
a GitHub PR run, keeps **one** sticky comment up to date instead of adding a new
|
|
173
|
+
one per push. Without that flag it just prints the markdown, so it can't post
|
|
174
|
+
from your laptop.
|
|
175
|
+
|
|
176
|
+
The `flecto-pr-risk` Action is that, packaged — the whole adoption is one
|
|
177
|
+
`uses:`, with the baseline resolved from the pull request rather than `HEAD~1`:
|
|
178
|
+
|
|
179
|
+
```yaml
|
|
180
|
+
permissions:
|
|
181
|
+
contents: read
|
|
182
|
+
pull-requests: write
|
|
183
|
+
|
|
184
|
+
steps:
|
|
185
|
+
- uses: actions/checkout@v7
|
|
186
|
+
with:
|
|
187
|
+
fetch-depth: 0
|
|
188
|
+
- uses: myselfsiddharth/Flecto/.github/actions/flecto-pr-risk@main
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
A fork's pull request gets a read-only token, so the comment is skipped with a
|
|
192
|
+
warning there — the check itself still runs and still fails on risky changes.
|
|
193
|
+
|
|
194
|
+
Works on any CI runner — it's a plain CLI with meaningful exit codes.
|
|
195
|
+
→ **[CI guide](docs/ci.md)**
|
|
196
|
+
|
|
197
|
+
### Trigger automation on change
|
|
198
|
+
|
|
199
|
+
Restart a service, reload a process, or notify an endpoint whenever config moves:
|
|
91
200
|
|
|
92
201
|
```bash
|
|
202
|
+
flecto watch .env --command "docker-compose restart app"
|
|
203
|
+
|
|
93
204
|
flecto watch config/prod.yaml \
|
|
94
205
|
--webhook https://hooks.example.com/notify \
|
|
95
|
-
--
|
|
206
|
+
--delivery-mode at-least-once
|
|
96
207
|
```
|
|
97
208
|
|
|
98
|
-
|
|
209
|
+
Changes arrive as a versioned JSON envelope, and `at-least-once` persists and
|
|
210
|
+
retries failed deliveries.
|
|
99
211
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
{ "type": "changed", "path": "database.pool_size", "before": 5, "after": 20 }
|
|
109
|
-
],
|
|
110
|
-
"policies": [
|
|
111
|
-
{
|
|
112
|
-
"id": "pool-size-jump",
|
|
113
|
-
"severity": "warn",
|
|
114
|
-
"path": "database.pool_size",
|
|
115
|
-
"message": "Pool size increased from 5 to 20 (>=2x).",
|
|
116
|
-
"pack": "default"
|
|
117
|
-
}
|
|
118
|
-
]
|
|
119
|
-
}
|
|
212
|
+
Posting straight to chat needs no receiver of your own — `--webhook-format`
|
|
213
|
+
shapes the body for Slack, Discord, or Teams, colored by the highest policy
|
|
214
|
+
severity:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
flecto watch config/prod.yaml \
|
|
218
|
+
--webhook "https://hooks.slack.com/services/T000/B000/XXXX" \
|
|
219
|
+
--webhook-format slack
|
|
120
220
|
```
|
|
121
221
|
|
|
122
|
-
|
|
222
|
+
→ **[Webhooks and commands](docs/webhooks.md)**
|
|
223
|
+
|
|
224
|
+
### Compare two environments
|
|
123
225
|
|
|
124
|
-
|
|
226
|
+
"Works in staging, fails in prod" is usually one key apart:
|
|
125
227
|
|
|
126
228
|
```bash
|
|
127
|
-
flecto
|
|
229
|
+
flecto compare config/prod.yaml config/staging.yaml
|
|
128
230
|
```
|
|
129
231
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
"maskSecrets": false
|
|
137
|
-
},
|
|
138
|
-
"profiles": {
|
|
139
|
-
"prod": {
|
|
140
|
-
"policies": ["default", "strict-prod"],
|
|
141
|
-
"maskSecrets": true
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
232
|
+
```
|
|
233
|
+
"+" exists only in the compared file, "-" only in the baseline, "~" differs
|
|
234
|
+
/path/to/config/staging.yaml — 2 changes from /path/to/config/prod.yaml:
|
|
235
|
+
- only_in_prod: true
|
|
236
|
+
~ database.pool_size: 5 → 20
|
|
237
|
+
! policy(warn) [default] database.pool_size: Pool size increased from 5 to 20 (>=2x).
|
|
145
238
|
```
|
|
146
239
|
|
|
147
|
-
|
|
240
|
+
The first file is the baseline, the files don't have to share a format, and
|
|
241
|
+
`--format json` gives you the same output `flecto ci` produces.
|
|
242
|
+
→ **[CLI reference](docs/cli-reference.md#flecto-compare-filea-fileb)**
|
|
148
243
|
|
|
149
|
-
###
|
|
244
|
+
### Track drift over time
|
|
150
245
|
|
|
151
246
|
```bash
|
|
152
|
-
flecto
|
|
247
|
+
flecto history config/prod.yaml --limit 10
|
|
153
248
|
```
|
|
154
249
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
### Migrating from envelope 1.1
|
|
250
|
+
Snapshots stay on your machine in `.flecto-snapshots/`. Nothing is uploaded and
|
|
251
|
+
no account is required. → **[CLI reference](docs/cli-reference.md#flecto-history-files)**
|
|
158
252
|
|
|
159
|
-
|
|
160
|
-
- Envelope type name is `FlectoEnvelope` (was `SentinelEnvelope` in docs/types only)
|
|
161
|
-
- New `policies` array on change envelopes
|
|
162
|
-
- Webhook headers are unchanged (`X-Flecto-*`)
|
|
163
|
-
### Use both command and webhook together
|
|
253
|
+
### Share what changed before the incident
|
|
164
254
|
|
|
165
255
|
```bash
|
|
166
|
-
flecto
|
|
167
|
-
--command "make reload" \
|
|
168
|
-
--webhook https://hooks.example.com/notify
|
|
256
|
+
flecto report --limit 20 --mask-secrets --output drift.html
|
|
169
257
|
```
|
|
170
258
|
|
|
171
|
-
|
|
259
|
+
One HTML file from that same local history: a timeline per file, every change
|
|
260
|
+
with its UTC timestamp, and policy findings grouped by severity. Fully
|
|
261
|
+
self-contained — inline styles, no fonts, no CDN scripts, no analytics — so you
|
|
262
|
+
can attach it to an incident thread and it renders offline. No server and no
|
|
263
|
+
account, same as everything else here.
|
|
264
|
+
→ **[CLI reference](docs/cli-reference.md#flecto-report-files)**
|
|
265
|
+
|
|
266
|
+
### Review a Kubernetes change before it reaches a cluster
|
|
267
|
+
|
|
268
|
+
ArgoCD, Flux, and `helm diff` compare the cluster to the repo — which needs a
|
|
269
|
+
cluster, and an apply that already happened. Flecto compares the manifests *this
|
|
270
|
+
pull request would produce* against the ones `main` produces:
|
|
172
271
|
|
|
173
272
|
```bash
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
--delivery-mode at-least-once \
|
|
177
|
-
--on-alert-failure retry
|
|
273
|
+
helm template api ./charts/api -f values/prod.yaml > /tmp/head.yaml
|
|
274
|
+
flecto compare /tmp/base.yaml /tmp/head.yaml --policies kubernetes --fail-on error
|
|
178
275
|
```
|
|
179
276
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
277
|
+
```
|
|
278
|
+
~ Service/prod/api.spec.type: "ClusterIP" → "LoadBalancer"
|
|
279
|
+
! policy(error) [kubernetes] Service type is LoadBalancer, which exposes the
|
|
280
|
+
workload outside the cluster. Confirm the exposure is intended.
|
|
281
|
+
```
|
|
184
282
|
|
|
185
|
-
|
|
283
|
+
Multi-document YAML is keyed by `kind/namespace/name`, so findings name the
|
|
284
|
+
resource. Flecto never runs `helm` or `kustomize` — you render, it diffs, so any
|
|
285
|
+
renderer works and no binary is needed in CI.
|
|
286
|
+
→ **[Kubernetes](docs/kubernetes.md)**
|
|
186
287
|
|
|
187
|
-
|
|
288
|
+
### Encode your own rules
|
|
188
289
|
|
|
189
|
-
|
|
290
|
+
Beyond the built-in packs, write rules as declarative JSON or YAML — no code:
|
|
190
291
|
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
|
|
292
|
+
```json
|
|
293
|
+
{
|
|
294
|
+
"id": "risky-feature-enable",
|
|
295
|
+
"severity": "error",
|
|
296
|
+
"allOf": [
|
|
297
|
+
{ "match": { "pathPrefix": "features." } },
|
|
298
|
+
{ "afterTruthy": true }
|
|
299
|
+
]
|
|
300
|
+
}
|
|
194
301
|
```
|
|
195
302
|
|
|
196
|
-
|
|
303
|
+
For anything a predicate can't express, a local ESM plugin exporting
|
|
304
|
+
`evaluate(changes, ctx)` gets the full change set.
|
|
305
|
+
→ **[Writing policy packs](docs/policy-packs.md)** · **[Plugins](docs/plugins.md)**
|
|
306
|
+
|
|
307
|
+
### Cut the noise
|
|
197
308
|
|
|
198
309
|
```bash
|
|
199
|
-
flecto watch config/prod.yaml --
|
|
310
|
+
flecto watch config/prod.yaml --ignore "updated_at,**.meta.timestamp"
|
|
200
311
|
```
|
|
201
312
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
This is useful in deployment scripts and pre-commit hooks.
|
|
313
|
+
Arrays of objects are matched by `id` or `name`, so reordering a list of named
|
|
314
|
+
services doesn't read as a wall of changes.
|
|
315
|
+
→ **[Configuration](docs/configuration.md)**
|
|
207
316
|
|
|
208
317
|
---
|
|
209
318
|
|
|
210
|
-
##
|
|
319
|
+
## Built-in policy packs
|
|
211
320
|
|
|
212
|
-
|
|
321
|
+
| Pack | Catches |
|
|
322
|
+
|---|---|
|
|
323
|
+
| `default` | Secret-like keys added or changed, secret-shaped *values* under any key, dangerous toggles, pool-size jumps |
|
|
324
|
+
| `strict-prod` | The same ground, with production-grade severities and matching |
|
|
325
|
+
| `compose` | Privileged services, host networking, Docker socket mounts, sensitive bind mounts |
|
|
326
|
+
| `kubernetes` | Privileged containers, host namespaces, weakened `runAsNonRoot`, added `SYS_ADMIN`, unpinned images, replica jumps, dropped limits, `LoadBalancer`/`NodePort` exposure |
|
|
327
|
+
| `node-runtime` | Dropped engine requirements, TLS verification bypasses, debug/inspector flags |
|
|
328
|
+
| `terraform` | Replaced and destroyed stateful resources, ingress opened to `0.0.0.0/0`, IAM wildcards, public S3, capacity jumps |
|
|
329
|
+
| `sops` | Decryption recipients added or removed, a MAC that moved on its own, a file that stopped being encrypted |
|
|
213
330
|
|
|
214
331
|
```bash
|
|
215
|
-
flecto
|
|
216
|
-
|
|
217
|
-
--format github-annotations \
|
|
218
|
-
--fail-on "changed,policy,error"
|
|
332
|
+
flecto policies list # see what resolves here, built-in and local
|
|
333
|
+
flecto ci "config/**/*.yaml" --policies "default,strict-prod" --fail-on policy
|
|
219
334
|
```
|
|
220
335
|
|
|
221
|
-
|
|
336
|
+
Pass files or glob patterns, quoted so your shell doesn't expand them first — a
|
|
337
|
+
bare directory is not a valid target.
|
|
222
338
|
|
|
223
|
-
|
|
339
|
+
A local `policies/<id>.json` overrides the built-in pack of the same id, and
|
|
340
|
+
`severityRemap` raises or silences individual rules per profile without forking
|
|
341
|
+
anything. → **[Policy packs](docs/policy-packs.md)**
|
|
224
342
|
|
|
225
|
-
|
|
343
|
+
Community packs ship on npm as `flecto-pack-<id>` packages — a package name and
|
|
344
|
+
one declarative JSON file, nothing else:
|
|
226
345
|
|
|
227
|
-
|
|
346
|
+
```bash
|
|
347
|
+
npm install --save-dev flecto-pack-deployment-safety
|
|
348
|
+
flecto policies add deployment-safety
|
|
349
|
+
```
|
|
228
350
|
|
|
229
|
-
|
|
351
|
+
`policies add` validates the pack, writes it to `policies/deployment-safety.json`,
|
|
352
|
+
and runs no code from the package. →
|
|
353
|
+
**[Installing community packs](docs/policy-packs.md#installing-a-community-pack)**
|
|
230
354
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## Supported formats
|
|
234
358
|
|
|
235
|
-
|
|
359
|
+
| Format | Extensions |
|
|
360
|
+
|---|---|
|
|
361
|
+
| JSON | `.json` |
|
|
362
|
+
| YAML | `.yaml`, `.yml` |
|
|
363
|
+
| TOML | `.toml` |
|
|
364
|
+
| INI | `.ini` |
|
|
365
|
+
| dotenv | `.env`, `.env.*`, `*.env` |
|
|
366
|
+
| age (armored) | `.age`, or any file whose contents are one armored blob |
|
|
367
|
+
|
|
368
|
+
Multi-document YAML (`---`-separated, the usual shape of a Kubernetes manifest)
|
|
369
|
+
is supported. Each document is diffed under its own key — `kind/name` for
|
|
370
|
+
Kubernetes-shaped documents, so a document inserted at the top of the file does
|
|
371
|
+
not renumber every other path. →
|
|
372
|
+
**[Multi-document YAML](docs/configuration.md#multi-document-yaml)**
|
|
236
373
|
|
|
237
374
|
---
|
|
238
375
|
|
|
239
|
-
##
|
|
376
|
+
## Encrypted files
|
|
240
377
|
|
|
241
|
-
|
|
378
|
+
A `sops`- or age-encrypted file is detected from its **contents**, and diffed
|
|
379
|
+
structurally:
|
|
242
380
|
|
|
243
|
-
```
|
|
244
|
-
|
|
381
|
+
```
|
|
382
|
+
+ cache: {"ttl_seconds":300}
|
|
383
|
+
~ database.password: <encrypted value changed>
|
|
384
|
+
+ sops.age.age1exampleexample…: {"recipient":"age1exampleexample…","enc":"<encrypted value>"}
|
|
245
385
|
```
|
|
246
386
|
|
|
247
|
-
|
|
387
|
+
You get keys added and removed, which encrypted values moved, and — the useful
|
|
388
|
+
part — who can decrypt the file. A recipient added is a genuine security event
|
|
389
|
+
and the `sops` pack raises it as one.
|
|
248
390
|
|
|
249
|
-
|
|
391
|
+
**Flecto never decrypts.** It never shells out to `sops` or `age`, never reads a
|
|
392
|
+
key file or agent socket, and never prints ciphertext — not even without
|
|
393
|
+
`--mask-secrets`. Ciphertext is replaced with an opaque sentinel in the parser,
|
|
394
|
+
so no diff, snapshot, webhook, or report can carry it. →
|
|
395
|
+
**[Encrypted files](docs/encrypted-files.md)**
|
|
250
396
|
|
|
251
|
-
|
|
397
|
+
---
|
|
252
398
|
|
|
253
|
-
|
|
399
|
+
## Configuration
|
|
254
400
|
|
|
255
|
-
|
|
401
|
+
Most teams commit a `.flectorc` so local runs and CI agree:
|
|
256
402
|
|
|
257
403
|
```bash
|
|
258
404
|
flecto init
|
|
259
405
|
```
|
|
260
406
|
|
|
261
|
-
Flecto looks for `.flectorc`, `.flectorc.json`, `.flectorc.yaml`, or `.flectorc.yml`.
|
|
262
|
-
|
|
263
|
-
Example:
|
|
264
|
-
|
|
265
407
|
```json
|
|
266
408
|
{
|
|
267
409
|
"defaults": {
|
|
268
|
-
"
|
|
269
|
-
"
|
|
270
|
-
"ignore": ["**.updated_at"],
|
|
271
|
-
"deliveryMode": "best-effort",
|
|
272
|
-
"onAlertFailure": "warn"
|
|
410
|
+
"policies": ["default"],
|
|
411
|
+
"ignore": ["**.updated_at"]
|
|
273
412
|
},
|
|
274
413
|
"profiles": {
|
|
275
414
|
"dev": { "mode": "verbose" },
|
|
276
|
-
"ci": { "failOn": "policy,error" }
|
|
415
|
+
"ci": { "failOn": "policy,error" },
|
|
416
|
+
"prod": {
|
|
417
|
+
"policies": ["default", "strict-prod"],
|
|
418
|
+
"severityRemap": { "pool-size-jump": "error" },
|
|
419
|
+
"maskSecrets": true
|
|
420
|
+
}
|
|
277
421
|
},
|
|
278
|
-
"files": ["config/**/*.yaml", ".env"]
|
|
279
|
-
"exclude": ["**/node_modules/**"]
|
|
422
|
+
"files": ["config/**/*.{yaml,yml,json,toml,ini}", ".env"]
|
|
280
423
|
}
|
|
281
424
|
```
|
|
282
425
|
|
|
283
|
-
Use a named profile:
|
|
284
|
-
|
|
285
426
|
```bash
|
|
286
427
|
flecto watch --profile dev
|
|
287
428
|
flecto ci --profile ci
|
|
288
429
|
```
|
|
289
430
|
|
|
290
|
-
CLI flags
|
|
291
|
-
|
|
292
|
-
Verify your setup:
|
|
293
|
-
|
|
294
|
-
```bash
|
|
295
|
-
flecto doctor
|
|
296
|
-
```
|
|
431
|
+
Explicit CLI flags win over profiles, which win over `defaults`.
|
|
432
|
+
→ **[Full configuration reference](docs/configuration.md)**
|
|
297
433
|
|
|
298
434
|
---
|
|
299
435
|
|
|
300
|
-
##
|
|
301
|
-
|
|
302
|
-
### Compact (default)
|
|
303
|
-
|
|
304
|
-
```
|
|
305
|
-
[HH:MM:SS] <filepath> — N changes
|
|
306
|
-
~ path: before → after (yellow — value changed)
|
|
307
|
-
+ path: value (green — key added)
|
|
308
|
-
- path: value (red — key removed)
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
### Verbose (`--mode verbose`)
|
|
436
|
+
## Commands
|
|
312
437
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
438
|
+
| Command | What it does |
|
|
439
|
+
|---|---|
|
|
440
|
+
| `flecto watch [files...]` | Watch for changes and print them as they happen |
|
|
441
|
+
| `flecto watch --snapshot` | Save the current state as a baseline |
|
|
442
|
+
| `flecto watch --diff` | Compare against the baseline and exit |
|
|
443
|
+
| `flecto ci [files...]` | One-shot check with a gate-able exit code |
|
|
444
|
+
| `flecto compare <fileA> <fileB>` | Diff two files against each other (`fileA` is the baseline) |
|
|
445
|
+
| `flecto plan <planFiles...>` | Review `terraform show -json` output and gate on it |
|
|
446
|
+
| `flecto history [files...]` | Summarize drift across local snapshots |
|
|
447
|
+
| `flecto report [files...]` | Render that history as a self-contained HTML file |
|
|
448
|
+
| `flecto policies add <name>` | Install a pack from an `flecto-pack-*` npm package |
|
|
449
|
+
| `flecto policies list` | List available policy packs |
|
|
450
|
+
| `flecto policies test <dir>` | Assert pack and plugin findings from fixtures |
|
|
451
|
+
| `flecto init` | Create a `.flectorc` from detected stack signals |
|
|
452
|
+
| `flecto doctor` | Check setup, config, and environment |
|
|
453
|
+
|
|
454
|
+
→ **[Every flag, every command](docs/cli-reference.md)**
|
|
321
455
|
|
|
322
456
|
---
|
|
323
457
|
|
|
324
|
-
##
|
|
325
|
-
|
|
326
|
-
Flecto is designed to keep running even when things go wrong:
|
|
458
|
+
## Documentation
|
|
327
459
|
|
|
328
|
-
|
|
|
460
|
+
| Guide | Covers |
|
|
329
461
|
|---|---|
|
|
330
|
-
|
|
|
331
|
-
|
|
|
332
|
-
|
|
|
333
|
-
|
|
|
334
|
-
|
|
|
335
|
-
|
|
|
462
|
+
| **[CLI reference](docs/cli-reference.md)** | Every command, flag, and exit code |
|
|
463
|
+
| **[Configuration](docs/configuration.md)** | `.flectorc`, profiles, ignore patterns, array identity, masking |
|
|
464
|
+
| **[Encrypted files](docs/encrypted-files.md)** | SOPS and age: what is detected, what is reported, why nothing is decrypted |
|
|
465
|
+
| **[CI](docs/ci.md)** | Baselines, fail triggers, output formats, the bundled GitHub Actions |
|
|
466
|
+
| **[Kubernetes](docs/kubernetes.md)** | Diffing rendered Helm/Kustomize manifests before they reach a cluster |
|
|
467
|
+
| **[Terraform plans](docs/terraform.md)** | Reviewing `terraform show -json` output and the `terraform` pack |
|
|
468
|
+
| **[Webhooks and commands](docs/webhooks.md)** | Envelope shape, delivery modes, command environment |
|
|
469
|
+
| **[Policy packs](docs/policy-packs.md)** | Writing declarative rules |
|
|
470
|
+
| **[Plugins](docs/plugins.md)** · **[Cookbook](docs/plugin-cookbook.md)** | Rules that need real code |
|
|
471
|
+
| **[Troubleshooting](docs/troubleshooting.md)** | When something doesn't behave |
|
|
472
|
+
| **[Changelog](CHANGELOG.md)** | Release history and migration notes |
|
|
336
473
|
|
|
337
474
|
---
|
|
338
475
|
|
|
339
|
-
##
|
|
476
|
+
## How it works
|
|
340
477
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
478
|
+
1. **Parse** — format detected by extension or dotenv naming → structured values
|
|
479
|
+
2. **Watch** — [chokidar](https://github.com/paulmillr/chokidar) with debounce
|
|
480
|
+
3. **Diff** — semantic tree comparison with ignore rules and array identity
|
|
481
|
+
4. **Evaluate** — policy packs and plugins → severity-tagged findings
|
|
482
|
+
5. **Emit** — a versioned envelope (`schema_version: "2.0"`)
|
|
483
|
+
6. **Deliver** — terminal output, shell command, webhook, or CI annotations
|
|
346
484
|
|
|
347
|
-
|
|
485
|
+
Flecto runs entirely on your machine. Snapshots are local files, and nothing
|
|
486
|
+
leaves the process unless you configure a webhook or command.
|
|
348
487
|
|
|
349
488
|
---
|
|
350
489
|
|
|
351
|
-
##
|
|
352
|
-
|
|
353
|
-
1. **Parser** — detects the file format by extension and parses it into structured JS values.
|
|
354
|
-
2. **Watcher** — uses [chokidar](https://github.com/paulmillr/chokidar) with debouncing so rapid saves don't flood you with events.
|
|
355
|
-
3. **Differ** — computes a semantic diff (not a line diff), supporting objects, arrays, scalars, and ignore rules.
|
|
356
|
-
4. **Policy engine** — inspects the changes for patterns that look risky and adds severity findings.
|
|
357
|
-
5. **Envelope** — wraps each batch of changes in a versioned event schema ready for automation.
|
|
358
|
-
6. **Alerter** — delivers events via command execution and/or webhook, with configurable retry logic.
|
|
359
|
-
|
|
360
|
-
---
|
|
490
|
+
## Project
|
|
361
491
|
|
|
362
|
-
|
|
492
|
+
- **Questions and ideas** — [Discussions](https://github.com/myselfsiddharth/Flecto/discussions)
|
|
493
|
+
- **Bugs and requests** — [Issues](https://github.com/myselfsiddharth/Flecto/issues)
|
|
494
|
+
- **Contributing** — [CONTRIBUTING.md](CONTRIBUTING.md) · [Code of Conduct](CODE_OF_CONDUCT.md)
|
|
495
|
+
- **Security** — [SECURITY.md](SECURITY.md), private disclosure only
|
|
496
|
+
- **Roadmap** — [Milestones](https://github.com/myselfsiddharth/Flecto/milestones)
|
|
363
497
|
|
|
364
|
-
|
|
498
|
+
Released under the [MIT License](LICENSE).
|