flecto 4.1.0 → 4.1.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 +32 -1
- package/README.md +107 -506
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,36 @@ The format is based on [Keep a Changelog], and this project adheres to
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [4.1.1] - 2026-09-29
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **The root `action.yml` description was too long to list on the Marketplace.**
|
|
15
|
+
GitHub rejects a listing whose description is 125 characters or more; 4.1.0
|
|
16
|
+
shipped 194. It is now 119, and a test pins the limit — the constraint is not
|
|
17
|
+
in GitHub's metadata-syntax documentation and the publish form only reports it
|
|
18
|
+
at publish time, by which point the release is already cut.
|
|
19
|
+
|
|
20
|
+
The rest of the pitch — that Flecto never runs `terraform`, `helm`, or `sops`,
|
|
21
|
+
and never decrypts — lives in the README, which has room for it.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- **Documented Action pins moved from `@v4.0.0` to `@v4.1.0`.** Tags are
|
|
26
|
+
immutable, and the fix that stopped the bundled Actions installing the pre-4.0
|
|
27
|
+
CLI shipped *in* 4.1.0 — so while the examples said `@v4.0.0` they pointed at a
|
|
28
|
+
tag whose `flecto-ci` and `flecto-pr-risk` still run `flecto@3`, the line
|
|
29
|
+
exposed to the baseline-shadowing bypass 4.0 closed.
|
|
30
|
+
|
|
31
|
+
**If you pinned `@v4.0.0` following an earlier example, repin to `@v4.1.0`.**
|
|
32
|
+
The CLI at `flecto@4.0.0` is fine; it is only the bundled Action metadata at
|
|
33
|
+
that tag that is not. [docs/ci.md](docs/ci.md#pinning) now warns against it.
|
|
34
|
+
|
|
35
|
+
[RELEASE.md](RELEASE.md) gained a step that repins the documented Actions as
|
|
36
|
+
part of every release. That step is the real fix — the bug was not the pin, it
|
|
37
|
+
was that nothing tied the documented pin to the release that fixed what it
|
|
38
|
+
pointed at.
|
|
39
|
+
|
|
10
40
|
## [4.1.0] - 2026-09-29
|
|
11
41
|
|
|
12
42
|
### Added
|
|
@@ -1292,7 +1322,8 @@ fixed — those runs were never actually gated — but the failure is new.
|
|
|
1292
1322
|
- Misconfigured policy packs/plugins cause `watch` to exit non-zero instead of
|
|
1293
1323
|
continuing with no policies.
|
|
1294
1324
|
|
|
1295
|
-
[Unreleased]: https://github.com/myselfsiddharth/Flecto/compare/v4.1.
|
|
1325
|
+
[Unreleased]: https://github.com/myselfsiddharth/Flecto/compare/v4.1.1...HEAD
|
|
1326
|
+
[4.1.1]: https://github.com/myselfsiddharth/Flecto/compare/v4.1.0...v4.1.1
|
|
1296
1327
|
[4.1.0]: https://github.com/myselfsiddharth/Flecto/compare/v4.0.0...v4.1.0
|
|
1297
1328
|
[4.0.0]: https://github.com/myselfsiddharth/Flecto/compare/v3.1.0...v4.0.0
|
|
1298
1329
|
[3.1.0]: https://github.com/myselfsiddharth/Flecto/compare/v3.0.2...v3.1.0
|
package/README.md
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
<p align="center">
|
|
2
|
-
<img src="docs/assets/flecto-hero.png" alt="Flecto — semantic config watcher" width="920"/>
|
|
3
|
-
</p>
|
|
4
|
-
|
|
5
1
|
<h1 align="center">Flecto</h1>
|
|
6
2
|
|
|
7
3
|
<p align="center">
|
|
8
|
-
<strong>
|
|
4
|
+
<strong>Flecto reads your Terraform plan and Kubernetes changes and posts a
|
|
5
|
+
plain-English risk summary on every pull request, blocking the dangerous ones.</strong>
|
|
9
6
|
</p>
|
|
10
7
|
|
|
11
8
|
<p align="center">
|
|
@@ -15,178 +12,87 @@
|
|
|
15
12
|
<a href="#documentation"><img alt="Docs" src="https://img.shields.io/badge/docs-read-34d399?style=flat-square&labelColor=0b1220"/></a>
|
|
16
13
|
</p>
|
|
17
14
|
|
|
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>
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
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.
|
|
28
|
-
|
|
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 |
|
|
37
|
-
|
|
38
|
-
The same engine reads whatever your change actually lives in:
|
|
39
|
-
|
|
40
|
-
| You are reviewing | Flecto reads |
|
|
41
|
-
|---|---|
|
|
42
|
-
| App config — YAML, JSON, TOML, INI, dotenv | the files directly |
|
|
43
|
-
| A Terraform change | `terraform show -json` output, via `flecto plan` |
|
|
44
|
-
| A Kubernetes change | rendered manifests from `helm`, `kustomize`, or anything else |
|
|
45
|
-
| A SOPS-encrypted file | its structure and recipients — **never its plaintext** |
|
|
46
|
-
|
|
47
|
-
It never invokes `terraform`, `helm`, `kustomize`, `sops`, or `age`, so nothing
|
|
48
|
-
extra has to exist on the CI runner.
|
|
49
|
-
|
|
50
15
|
---
|
|
51
16
|
|
|
52
|
-
##
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
npm install -g flecto
|
|
56
|
-
```
|
|
17
|
+
## What lands on the pull request
|
|
57
18
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
flecto --version
|
|
62
|
-
flecto doctor
|
|
63
|
-
```
|
|
19
|
+
<p align="center">
|
|
20
|
+
<img src="docs/assets/flecto-pr-comment.png" alt="Flecto's comment on a pull request: check failing, six policy errors naming an IAM wildcard, a disabled S3 public-access block, and security group ingress opened to 0.0.0.0/0" width="960"/>
|
|
21
|
+
</p>
|
|
64
22
|
|
|
65
|
-
|
|
66
|
-
|
|
23
|
+
That is a real comment on a
|
|
24
|
+
**[real pull request](https://github.com/myselfsiddharth/flecto-example-terraform/pull/1)**
|
|
25
|
+
you can open right now. The PR says it is about partner access and changes eight
|
|
26
|
+
lines; it opens the web tier to the internet, turns off the bucket's
|
|
27
|
+
public-access protection, and widens an IAM policy to `s3:*` on `*`.
|
|
67
28
|
|
|
68
|
-
|
|
29
|
+
One sticky comment, updated in place on every push. Exit code `1`, so the build
|
|
30
|
+
fails before the change ships.
|
|
69
31
|
|
|
70
|
-
|
|
32
|
+
**[The same gate on an ordinary change](https://github.com/myselfsiddharth/flecto-example-terraform/pull/2)**
|
|
33
|
+
reports *no findings* and passes. That matters as much: a check that fires on
|
|
34
|
+
everything gets uninstalled in a week.
|
|
71
35
|
|
|
72
|
-
|
|
36
|
+
<details>
|
|
37
|
+
<summary>The same report as text, and what a plan with existing state adds</summary>
|
|
73
38
|
|
|
74
|
-
|
|
39
|
+
The comment above, as `flecto plan` prints it:
|
|
75
40
|
|
|
76
|
-
```bash
|
|
77
|
-
mkdir flecto-demo && cd flecto-demo && mkdir config
|
|
78
|
-
cat > config/prod.yaml <<'EOF'
|
|
79
|
-
database:
|
|
80
|
-
host: db.internal
|
|
81
|
-
pool_size: 5
|
|
82
|
-
ssl: true
|
|
83
|
-
logging:
|
|
84
|
-
level: info
|
|
85
|
-
debug: false
|
|
86
|
-
EOF
|
|
87
41
|
```
|
|
42
|
+
❌ Check failing — 30 changes in 1 file — 0 changed, 30 added, 0 removed.
|
|
43
|
+
Policy: 6 errors.
|
|
88
44
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
45
|
+
aws_security_group.web.ingress[0].cidr_blocks[0]
|
|
46
|
+
terraform-security-group-open-ingress
|
|
47
|
+
Security group ingress will accept traffic from the whole internet
|
|
48
|
+
(0.0.0.0/0). Restrict the source to a known CIDR, a prefix list, or
|
|
49
|
+
another security group.
|
|
94
50
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
51
|
+
aws_s3_bucket_public_access_block.uploads.block_public_acls (+3 more)
|
|
52
|
+
terraform-s3-public-access-block-disabled
|
|
53
|
+
S3 public access block is being turned off or removed.
|
|
98
54
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
cat > config/prod.yaml <<'EOF'
|
|
103
|
-
database:
|
|
104
|
-
host: db.internal
|
|
105
|
-
pool_size: 20
|
|
106
|
-
ssl: true
|
|
107
|
-
logging:
|
|
108
|
-
level: info
|
|
109
|
-
debug: true
|
|
110
|
-
EOF
|
|
55
|
+
aws_iam_role_policy.app.policy
|
|
56
|
+
terraform-iam-wildcard
|
|
57
|
+
IAM policy grants a wildcard action or resource ("*").
|
|
111
58
|
```
|
|
112
59
|
|
|
113
|
-
|
|
60
|
+
The example repository has no Terraform state, so every resource shows as
|
|
61
|
+
`create`. With existing state, a plan that replaces a database also reports:
|
|
114
62
|
|
|
115
|
-
```bash
|
|
116
|
-
flecto watch config/prod.yaml --diff
|
|
117
63
|
```
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
64
|
+
aws_db_instance.main.#action
|
|
65
|
+
terraform-stateful-resource-destroyed
|
|
66
|
+
Terraform will destroy a stateful resource. Its data does not survive.
|
|
67
|
+
Take a final snapshot, or add a prevent_destroy lifecycle block, before
|
|
68
|
+
applying.
|
|
123
69
|
```
|
|
124
70
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
```bash
|
|
128
|
-
flecto ci config/prod.yaml --format github-annotations
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
```
|
|
132
|
-
::warning title=flecto changed::database.pool_size
|
|
133
|
-
::warning title=flecto changed::logging.debug
|
|
134
|
-
::warning title=flecto policy pool-size-jump [default]::database.pool_size: Pool size increased from 5 to 20 (>=2x).
|
|
135
|
-
::error title=flecto policy dangerous-toggle-enabled [default]::logging.debug: Potentially dangerous toggle enabled.
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
Exit code `1`. In CI, that's a failed build — before the change ships.
|
|
139
|
-
|
|
140
|
-
**5. Watch it live.** Leave this running and edit the file in another window:
|
|
141
|
-
|
|
142
|
-
```bash
|
|
143
|
-
flecto watch config/prod.yaml
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
```
|
|
147
|
-
flecto watching /path/to/flecto-demo/config/prod.yaml
|
|
148
|
-
Press Ctrl+C to stop.
|
|
149
|
-
|
|
150
|
-
[18:24:48] /path/to/flecto-demo/config/prod.yaml — 2 changes
|
|
151
|
-
~ database.pool_size: 5 → 20
|
|
152
|
-
~ logging.debug: false → true
|
|
153
|
-
! policy(warn) [default] database.pool_size: Pool size increased from 5 to 20 (>=2x).
|
|
154
|
-
! policy(error) [default] logging.debug: Potentially dangerous toggle enabled.
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
That's the whole product. Everything below is depth.
|
|
71
|
+
</details>
|
|
158
72
|
|
|
159
73
|
---
|
|
160
74
|
|
|
161
|
-
##
|
|
75
|
+
## Add it in 60 seconds
|
|
162
76
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
Add one step to your workflow and risky config edits show up as annotations on
|
|
166
|
-
the pull request:
|
|
77
|
+
**Terraform** — point it at the plan JSON:
|
|
167
78
|
|
|
168
79
|
```yaml
|
|
169
80
|
permissions:
|
|
170
81
|
contents: read
|
|
82
|
+
pull-requests: write
|
|
171
83
|
|
|
172
84
|
steps:
|
|
173
85
|
- uses: actions/checkout@v7
|
|
86
|
+
- run: |
|
|
87
|
+
terraform plan -out=tf.plan
|
|
88
|
+
terraform show -json tf.plan > plan.json
|
|
89
|
+
- uses: myselfsiddharth/Flecto/.github/actions/flecto-pr-risk@v4.1.0
|
|
174
90
|
with:
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
with:
|
|
178
|
-
targets: config/**/*.{yaml,yml,json,toml,ini}
|
|
179
|
-
snapshot-ref: HEAD~1
|
|
91
|
+
terraform-plan: plan.json
|
|
92
|
+
fail-on: error
|
|
180
93
|
```
|
|
181
94
|
|
|
182
|
-
|
|
183
|
-
policy findings as markdown and, when you opt in with `--pr-comment-post` inside
|
|
184
|
-
a GitHub PR run, keeps **one** sticky comment up to date instead of adding a new
|
|
185
|
-
one per push. Without that flag it just prints the markdown, so it can't post
|
|
186
|
-
from your laptop.
|
|
187
|
-
|
|
188
|
-
The `flecto-pr-risk` Action is that, packaged — the whole adoption is one
|
|
189
|
-
`uses:`, with the baseline resolved from the pull request rather than `HEAD~1`:
|
|
95
|
+
**Kubernetes** — render your manifests, then diff them against the PR's base:
|
|
190
96
|
|
|
191
97
|
```yaml
|
|
192
98
|
permissions:
|
|
@@ -197,324 +103,64 @@ steps:
|
|
|
197
103
|
- uses: actions/checkout@v7
|
|
198
104
|
with:
|
|
199
105
|
fetch-depth: 0
|
|
200
|
-
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
A fork's pull request gets a read-only token, so the comment is skipped with a
|
|
207
|
-
warning there — the check itself still runs and still fails on risky changes.
|
|
208
|
-
|
|
209
|
-
Works on any CI runner — it's a plain CLI with meaningful exit codes.
|
|
210
|
-
→ **[CI guide](docs/ci.md)**
|
|
211
|
-
|
|
212
|
-
### Trigger automation on change
|
|
213
|
-
|
|
214
|
-
Restart a service, reload a process, or notify an endpoint whenever config moves:
|
|
215
|
-
|
|
216
|
-
```bash
|
|
217
|
-
flecto watch .env --command "docker-compose restart app"
|
|
218
|
-
|
|
219
|
-
flecto watch config/prod.yaml \
|
|
220
|
-
--webhook https://hooks.example.com/notify \
|
|
221
|
-
--delivery-mode at-least-once
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
Changes arrive as a versioned JSON envelope, and `at-least-once` persists and
|
|
225
|
-
retries failed deliveries.
|
|
226
|
-
|
|
227
|
-
Posting straight to chat needs no receiver of your own — `--webhook-format`
|
|
228
|
-
shapes the body for Slack, Discord, or Teams, colored by the highest policy
|
|
229
|
-
severity:
|
|
230
|
-
|
|
231
|
-
```bash
|
|
232
|
-
flecto watch config/prod.yaml \
|
|
233
|
-
--webhook "https://hooks.slack.com/services/T000/B000/XXXX" \
|
|
234
|
-
--webhook-format slack
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
→ **[Webhooks and commands](docs/webhooks.md)**
|
|
238
|
-
|
|
239
|
-
### Compare two environments
|
|
240
|
-
|
|
241
|
-
"Works in staging, fails in prod" is usually one key apart:
|
|
242
|
-
|
|
243
|
-
```bash
|
|
244
|
-
flecto compare config/prod.yaml config/staging.yaml
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
```
|
|
248
|
-
"+" exists only in the compared file, "-" only in the baseline, "~" differs
|
|
249
|
-
/path/to/config/staging.yaml — 2 changes from /path/to/config/prod.yaml:
|
|
250
|
-
- only_in_prod: true
|
|
251
|
-
~ database.pool_size: 5 → 20
|
|
252
|
-
! policy(warn) [default] database.pool_size: Pool size increased from 5 to 20 (>=2x).
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
The first file is the baseline, the files don't have to share a format, and
|
|
256
|
-
`--format json` gives you the same output `flecto ci` produces.
|
|
257
|
-
→ **[CLI reference](docs/cli-reference.md#flecto-compare-filea-fileb)**
|
|
258
|
-
|
|
259
|
-
### Track drift over time
|
|
260
|
-
|
|
261
|
-
```bash
|
|
262
|
-
flecto history config/prod.yaml --limit 10
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
Snapshots stay on your machine in `.flecto-snapshots/`. Nothing is uploaded and
|
|
266
|
-
no account is required. To read the same history on a CI runner, save it to the
|
|
267
|
-
git-tracked store instead — `--snapshot-store shared` writes a committable
|
|
268
|
-
`.flecto/snapshots/`, masking secret-like values into digests as it goes.
|
|
269
|
-
→ **[CLI reference](docs/cli-reference.md#flecto-history-files)**
|
|
270
|
-
|
|
271
|
-
### Share what changed before the incident
|
|
272
|
-
|
|
273
|
-
```bash
|
|
274
|
-
flecto report --limit 20 --mask-secrets --output drift.html
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
One HTML file from that same local history: a timeline per file, every change
|
|
278
|
-
with its UTC timestamp, and policy findings grouped by severity. Fully
|
|
279
|
-
self-contained — inline styles, no fonts, no CDN scripts, no analytics — so you
|
|
280
|
-
can attach it to an incident thread and it renders offline. No server and no
|
|
281
|
-
account, same as everything else here.
|
|
282
|
-
→ **[CLI reference](docs/cli-reference.md#flecto-report-files)**
|
|
283
|
-
|
|
284
|
-
### Review a Kubernetes change before it reaches a cluster
|
|
285
|
-
|
|
286
|
-
ArgoCD, Flux, and `helm diff` compare the cluster to the repo — which needs a
|
|
287
|
-
cluster, and an apply that already happened. Flecto compares the manifests *this
|
|
288
|
-
pull request would produce* against the ones `main` produces:
|
|
289
|
-
|
|
290
|
-
```bash
|
|
291
|
-
helm template api ./charts/api -f values/prod.yaml > /tmp/head.yaml
|
|
292
|
-
flecto compare /tmp/base.yaml /tmp/head.yaml --policies kubernetes --fail-on error
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
```
|
|
296
|
-
~ Service/prod/api.spec.type: "ClusterIP" → "LoadBalancer"
|
|
297
|
-
! policy(error) [kubernetes] Service type is LoadBalancer, which exposes the
|
|
298
|
-
workload outside the cluster. Confirm the exposure is intended.
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
Multi-document YAML is keyed by `kind/namespace/name`, so findings name the
|
|
302
|
-
resource. Flecto never runs `helm` or `kustomize` — you render, it diffs, so any
|
|
303
|
-
renderer works and no binary is needed in CI.
|
|
304
|
-
→ **[Kubernetes](docs/kubernetes.md)**
|
|
305
|
-
|
|
306
|
-
### Read a Terraform plan in plain English
|
|
307
|
-
|
|
308
|
-
`terraform plan` output is precise and long. Flecto turns it into the handful of
|
|
309
|
-
lines a reviewer actually needs to argue about:
|
|
310
|
-
|
|
311
|
-
```bash
|
|
312
|
-
terraform show -json plan.tfplan > plan.json
|
|
313
|
-
flecto plan plan.json --fail-on error
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
```
|
|
317
|
-
plan.json — plan format 1.2
|
|
318
|
-
Plan: 0 to add, 1 to change, 0 to destroy, 1 to replace.
|
|
319
|
-
~ aws_security_group.web.ingress[0].cidr_blocks[0]: "10.0.0.0/8" → "0.0.0.0/0"
|
|
320
|
-
- aws_db_instance.main.#action: "replace" [terraform will destroy and recreate aws_db_instance.main]
|
|
321
|
-
~ aws_db_instance.main.password: "(sensitive value)" → "(sensitive value)" [sensitive]
|
|
322
|
-
! policy(error) [terraform] …cidr_blocks[0]: Security group ingress will accept
|
|
323
|
-
traffic from the whole internet (0.0.0.0/0). Restrict the source to a known CIDR…
|
|
324
|
-
! policy(error) [terraform] …#action: Terraform will destroy a stateful resource.
|
|
325
|
-
Its data does not survive. Take a final snapshot, or add a prevent_destroy…
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
A **replace reads as a removal**, not a benign update — a recreated database
|
|
329
|
-
should never look like a config tweak. Values Terraform marks sensitive are
|
|
330
|
-
redacted during parsing, before the policy engine or any formatter sees them, and
|
|
331
|
-
`after_unknown` renders as `(known after apply)` rather than `null`.
|
|
332
|
-
|
|
333
|
-
**Flecto never runs `terraform`** — you produce the JSON, it reads it, so nothing
|
|
334
|
-
extra has to exist on the CI runner.
|
|
335
|
-
→ **[Terraform plans](docs/terraform.md)**
|
|
336
|
-
|
|
337
|
-
### Encode your own rules
|
|
338
|
-
|
|
339
|
-
Beyond the built-in packs, write rules as declarative JSON or YAML — no code:
|
|
340
|
-
|
|
341
|
-
```json
|
|
342
|
-
{
|
|
343
|
-
"id": "risky-feature-enable",
|
|
344
|
-
"severity": "error",
|
|
345
|
-
"allOf": [
|
|
346
|
-
{ "match": { "pathPrefix": "features." } },
|
|
347
|
-
{ "afterTruthy": true }
|
|
348
|
-
]
|
|
349
|
-
}
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
For anything a predicate can't express, a local ESM plugin exporting
|
|
353
|
-
`evaluate(changes, ctx)` gets the full change set.
|
|
354
|
-
→ **[Writing policy packs](docs/policy-packs.md)** · **[Plugins](docs/plugins.md)**
|
|
355
|
-
|
|
356
|
-
### Cut the noise
|
|
357
|
-
|
|
358
|
-
```bash
|
|
359
|
-
flecto watch config/prod.yaml --ignore "updated_at,**.meta.timestamp"
|
|
106
|
+
- run: helm template ./chart > rendered.yaml
|
|
107
|
+
- uses: myselfsiddharth/Flecto/.github/actions/flecto-pr-risk@v4.1.0
|
|
108
|
+
with:
|
|
109
|
+
targets: rendered.yaml
|
|
110
|
+
policies: kubernetes
|
|
360
111
|
```
|
|
361
112
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
→ **[Configuration](docs/configuration.md)**
|
|
113
|
+
Flecto **never invokes** `terraform`, `helm`, `kustomize`, `sops`, or `age`, so
|
|
114
|
+
nothing extra has to exist on the runner. → **[CI guide](docs/ci.md)**
|
|
365
115
|
|
|
366
116
|
---
|
|
367
117
|
|
|
368
|
-
##
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
Pass files or glob patterns, quoted so your shell doesn't expand them first — a
|
|
387
|
-
bare directory is not a valid target.
|
|
388
|
-
|
|
389
|
-
A local `policies/<id>.json` overrides the built-in pack of the same id, and
|
|
390
|
-
`severityRemap` raises or silences individual rules per profile without forking
|
|
391
|
-
anything. → **[Policy packs](docs/policy-packs.md)**
|
|
392
|
-
|
|
393
|
-
Community packs ship on npm as `flecto-pack-<id>` packages — a package name and
|
|
394
|
-
one declarative JSON file, nothing else:
|
|
395
|
-
|
|
396
|
-
```bash
|
|
397
|
-
npm install --save-dev flecto-pack-deployment-safety
|
|
398
|
-
flecto policies add deployment-safety
|
|
399
|
-
```
|
|
400
|
-
|
|
401
|
-
`policies add` validates the pack, writes it to `policies/deployment-safety.json`,
|
|
402
|
-
and runs no code from the package. →
|
|
403
|
-
**[Installing community packs](docs/policy-packs.md#installing-a-community-pack)**
|
|
118
|
+
## What it catches
|
|
119
|
+
|
|
120
|
+
- **A database about to be destroyed** — `terraform plan` says `replace`, the
|
|
121
|
+
diff says the data does not survive
|
|
122
|
+
- **Ingress widened to the world** — `10.0.0.0/8` → `0.0.0.0/0` on a security
|
|
123
|
+
group
|
|
124
|
+
- **An IAM policy gone wildcard** — `Action: "*"` where it used to be scoped
|
|
125
|
+
- **S3 public-access protection switched off** — the block removed, not just
|
|
126
|
+
loosened
|
|
127
|
+
- **A container turned privileged**, or `runAsNonRoot` quietly weakened
|
|
128
|
+
- **Resource limits removed** from a Deployment, so one pod can starve a node
|
|
129
|
+
- **An image tag unpinned** — `:1.4.0` → `:latest`
|
|
130
|
+
- **A new recipient on a SOPS file** — someone who could not decrypt it now can
|
|
131
|
+
- **`debug: true` in a 40-line formatting diff**, or a connection pool
|
|
132
|
+
quadrupled
|
|
133
|
+
|
|
134
|
+
Rules live in [policy packs](docs/policy-packs.md) you can extend, remap, or
|
|
135
|
+
replace.
|
|
404
136
|
|
|
405
137
|
---
|
|
406
138
|
|
|
407
|
-
##
|
|
139
|
+
## Also works as
|
|
408
140
|
|
|
409
|
-
|
|
|
141
|
+
| | |
|
|
410
142
|
|---|---|
|
|
411
|
-
|
|
|
412
|
-
|
|
|
413
|
-
|
|
|
414
|
-
|
|
|
415
|
-
|
|
|
416
|
-
|
|
|
417
|
-
|
|
418
|
-
`.json` accepts comments and trailing commas, so `tsconfig.json`,
|
|
419
|
-
`.vscode/settings.json`, `jsconfig.json`, and `devcontainer.json` are read as
|
|
420
|
-
written. →
|
|
421
|
-
**[JSON with comments](docs/configuration.md#json-with-comments)**
|
|
422
|
-
|
|
423
|
-
Terraform plan JSON (`terraform show -json`) is read by **`flecto plan`**, which
|
|
424
|
-
applies Terraform's own sensitivity marking. Point `plan` at it rather than `ci`
|
|
425
|
-
or `watch` — those treat it as ordinary JSON and will print values Terraform
|
|
426
|
-
marks sensitive ([#113](https://github.com/myselfsiddharth/Flecto/issues/113)).
|
|
427
|
-
|
|
428
|
-
Multi-document YAML (`---`-separated, the usual shape of a Kubernetes manifest)
|
|
429
|
-
is supported. Each document is diffed under its own key — `kind/name` for
|
|
430
|
-
Kubernetes-shaped documents, so a document inserted at the top of the file does
|
|
431
|
-
not renumber every other path. →
|
|
432
|
-
**[Multi-document YAML](docs/configuration.md#multi-document-yaml)**
|
|
433
|
-
|
|
434
|
-
---
|
|
435
|
-
|
|
436
|
-
## Encrypted files
|
|
437
|
-
|
|
438
|
-
A `sops`- or age-encrypted file is detected from its **contents**, and diffed
|
|
439
|
-
structurally:
|
|
440
|
-
|
|
441
|
-
```
|
|
442
|
-
+ cache: {"ttl_seconds":300}
|
|
443
|
-
~ database.password: <encrypted value changed>
|
|
444
|
-
+ sops.age.age1exampleexample…: {"recipient":"age1exampleexample…","enc":"<encrypted value>"}
|
|
445
|
-
```
|
|
446
|
-
|
|
447
|
-
You get keys added and removed, which encrypted values moved, and — the useful
|
|
448
|
-
part — who can decrypt the file. A recipient added is a genuine security event
|
|
449
|
-
and the `sops` pack raises it as one.
|
|
450
|
-
|
|
451
|
-
**Flecto never decrypts.** It never shells out to `sops` or `age`, never reads a
|
|
452
|
-
key file or agent socket, and never prints ciphertext — not even without
|
|
453
|
-
`--mask-secrets`. Ciphertext is replaced with an opaque sentinel in the parser,
|
|
454
|
-
so no diff, snapshot, webhook, or report can carry it. →
|
|
455
|
-
**[Encrypted files](docs/encrypted-files.md)**
|
|
143
|
+
| **A CLI** | `flecto ci`, `flecto plan`, `flecto compare` — plain exit codes, any runner → **[CLI reference](docs/cli-reference.md)** |
|
|
144
|
+
| **A file watcher** | `flecto watch` reports changes as you edit → **[Getting started](docs/getting-started.md)** |
|
|
145
|
+
| **A webhook / command trigger** | Restart a service or notify an endpoint on change → **[Webhooks](docs/webhooks.md)** |
|
|
146
|
+
| **An MCP server** | Read-only `diff`, `check`, `explain` tools for agents → **[MCP](docs/mcp.md)** |
|
|
147
|
+
| **An editor language server** | `flecto lsp` — diagnostics while you type → **[Editor](docs/editor.md)** |
|
|
148
|
+
| **A drift detector** | `flecto-drift` compares declared config against what is running → **[Drift](docs/drift.md)** |
|
|
456
149
|
|
|
457
150
|
---
|
|
458
151
|
|
|
459
|
-
##
|
|
460
|
-
|
|
461
|
-
Most teams commit a `.flectorc` so local runs and CI agree:
|
|
462
|
-
|
|
463
|
-
```bash
|
|
464
|
-
flecto init
|
|
465
|
-
```
|
|
466
|
-
|
|
467
|
-
```json
|
|
468
|
-
{
|
|
469
|
-
"defaults": {
|
|
470
|
-
"policies": ["default"],
|
|
471
|
-
"ignore": ["**.updated_at"]
|
|
472
|
-
},
|
|
473
|
-
"profiles": {
|
|
474
|
-
"dev": { "mode": "verbose" },
|
|
475
|
-
"ci": { "failOn": "policy,error" },
|
|
476
|
-
"prod": {
|
|
477
|
-
"policies": ["default", "strict-prod"],
|
|
478
|
-
"severityRemap": { "pool-size-jump": "error" },
|
|
479
|
-
"maskSecrets": true
|
|
480
|
-
}
|
|
481
|
-
},
|
|
482
|
-
"files": ["config/**/*.{yaml,yml,json,toml,ini}", ".env"]
|
|
483
|
-
}
|
|
484
|
-
```
|
|
485
|
-
|
|
486
|
-
```bash
|
|
487
|
-
flecto watch --profile dev
|
|
488
|
-
flecto ci --profile ci
|
|
489
|
-
```
|
|
152
|
+
## Stability
|
|
490
153
|
|
|
491
|
-
|
|
492
|
-
|
|
154
|
+
Flecto runs inside your merge path. The **JSON envelope**
|
|
155
|
+
(`schema_version: "2.0"`), **exit codes** (`0` clean, `1` failed — it fails
|
|
156
|
+
closed), **`.flectorc`** keys, and **command and flag names** follow
|
|
157
|
+
[semver](https://semver.org/), and no breaking change to them ships without a
|
|
158
|
+
minor release that warns first. The one exception is a security fix.
|
|
493
159
|
|
|
494
|
-
|
|
160
|
+
Terminal output, message wording, and anything under `src/` are deliberately
|
|
161
|
+
**not** stable — parse `--format json`.
|
|
495
162
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
| Command | What it does |
|
|
499
|
-
|---|---|
|
|
500
|
-
| `flecto watch [files...]` | Watch for changes and print them as they happen |
|
|
501
|
-
| `flecto watch --snapshot` | Save the current state as a baseline |
|
|
502
|
-
| `flecto watch --diff` | Compare against the baseline and exit |
|
|
503
|
-
| `flecto ci [files...]` | One-shot check with a gate-able exit code |
|
|
504
|
-
| `flecto explain [files...]` | Advisory, model-generated narration of the masked diff (opt-in, your own key) |
|
|
505
|
-
| `flecto compare <fileA> <fileB>` | Diff two files against each other (`fileA` is the baseline) |
|
|
506
|
-
| `flecto plan <planFiles...>` | Review `terraform show -json` output and gate on it |
|
|
507
|
-
| `flecto history [files...]` | Summarize drift across local snapshots |
|
|
508
|
-
| `flecto report [files...]` | Render that history as a self-contained HTML file |
|
|
509
|
-
| `flecto policies add <name>` | Install a pack from an `flecto-pack-*` npm package |
|
|
510
|
-
| `flecto policies list` | List available policy packs |
|
|
511
|
-
| `flecto policies test <dir>` | Assert pack and plugin findings from fixtures |
|
|
512
|
-
| `flecto init` | Create a `.flectorc` from detected stack signals |
|
|
513
|
-
| `flecto mcp` | Serve read-only diff/check/explain tools to an agent over MCP |
|
|
514
|
-
| `flecto lsp` | Show findings and changes as diagnostics while a config file is edited |
|
|
515
|
-
| `flecto doctor` | Check setup, config, and environment |
|
|
516
|
-
|
|
517
|
-
→ **[Every flag, every command](docs/cli-reference.md)**
|
|
163
|
+
→ **[Full stability policy](docs/stability.md)**
|
|
518
164
|
|
|
519
165
|
---
|
|
520
166
|
|
|
@@ -522,75 +168,30 @@ Explicit CLI flags win over profiles, which win over `defaults`.
|
|
|
522
168
|
|
|
523
169
|
| Guide | Covers |
|
|
524
170
|
|---|---|
|
|
525
|
-
| **[
|
|
526
|
-
| **[
|
|
527
|
-
| **[Editor diagnostics](docs/editor.md)** | `flecto lsp` setup for Neovim, Helix, Emacs, and where diagnostics land |
|
|
528
|
-
| **[Encrypted files](docs/encrypted-files.md)** | SOPS and age: what is detected, what is reported, why nothing is decrypted |
|
|
529
|
-
| **[CI](docs/ci.md)** | Baselines, fail triggers, output formats, the bundled GitHub Actions |
|
|
530
|
-
| **[Performance](docs/performance.md)** | Where time goes at scale, and how much smaller a diff is than the config |
|
|
531
|
-
| **[Kubernetes](docs/kubernetes.md)** | Diffing rendered Helm/Kustomize manifests before they reach a cluster |
|
|
171
|
+
| **[Getting started](docs/getting-started.md)** | A full walkthrough, from install to a failing build |
|
|
172
|
+
| **[CI](docs/ci.md)** | Baselines, fail triggers, output formats, the bundled Actions, pinning |
|
|
532
173
|
| **[Terraform plans](docs/terraform.md)** | Reviewing `terraform show -json` output and the `terraform` pack |
|
|
174
|
+
| **[Kubernetes](docs/kubernetes.md)** | Diffing rendered Helm/Kustomize manifests before they reach a cluster |
|
|
175
|
+
| **[Comparison](docs/comparison.md)** | Honest comparison with Checkov, Trivy/tfsec, conftest/OPA, tf-summarize, dyff |
|
|
176
|
+
| **[Supported formats](docs/formats.md)** | YAML, JSON, TOML, INI, dotenv, age — and what each one accepts |
|
|
177
|
+
| **[Encrypted files](docs/encrypted-files.md)** | SOPS and age: what is detected, why nothing is decrypted |
|
|
178
|
+
| **[Configuration](docs/configuration.md)** | `.flectorc`, profiles, ignore patterns, array identity, masking |
|
|
179
|
+
| **[CLI reference](docs/cli-reference.md)** | Every command, flag, and exit code |
|
|
180
|
+
| **[Policy packs](docs/policy-packs.md)** | Writing declarative rules |
|
|
181
|
+
| **[Plugins](docs/plugins.md)** · **[Cookbook](docs/plugin-cookbook.md)** | Rules that need real code |
|
|
533
182
|
| **[Webhooks and commands](docs/webhooks.md)** | Envelope shape, delivery modes, command environment |
|
|
534
183
|
| **[MCP server](docs/mcp.md)** | Read-only diff/check/explain tools for agents, and the security posture |
|
|
535
184
|
| **[Explain](docs/explain.md)** | Opt-in model narration of a diff: what is sent, what it can never do, cost |
|
|
536
|
-
| **[
|
|
537
|
-
| **[
|
|
538
|
-
| **[
|
|
185
|
+
| **[Editor diagnostics](docs/editor.md)** | `flecto lsp` setup for Neovim, Helix, Emacs |
|
|
186
|
+
| **[Live drift](docs/drift.md)** | `flecto-drift`: declared config versus what is actually running |
|
|
187
|
+
| **[Performance](docs/performance.md)** | Where time goes at scale |
|
|
188
|
+
| **[Stability](docs/stability.md)** | What you can build against, and the deprecation sequence |
|
|
539
189
|
| **[Troubleshooting](docs/troubleshooting.md)** | When something doesn't behave |
|
|
540
|
-
| **[
|
|
541
|
-
| **[Migrating to 4.0](docs/migrating-to-4.md)** | The five breaking changes, and how to tell whether they affect you |
|
|
190
|
+
| **[Migrating to 4.0](docs/migrating-to-4.md)** | The five breaking changes, and whether they affect you |
|
|
542
191
|
| **[Changelog](CHANGELOG.md)** | Release history and migration notes |
|
|
543
192
|
|
|
544
193
|
---
|
|
545
194
|
|
|
546
|
-
## How it works
|
|
547
|
-
|
|
548
|
-
1. **Parse** — format detected by extension or dotenv naming → structured values
|
|
549
|
-
2. **Watch** — [chokidar](https://github.com/paulmillr/chokidar) with debounce
|
|
550
|
-
3. **Diff** — semantic tree comparison with ignore rules and array identity
|
|
551
|
-
4. **Evaluate** — policy packs and plugins → severity-tagged findings
|
|
552
|
-
5. **Emit** — a versioned envelope (`schema_version: "2.0"`)
|
|
553
|
-
6. **Deliver** — terminal output, shell command, webhook, or CI annotations
|
|
554
|
-
|
|
555
|
-
Flecto runs entirely on your machine. Snapshots are local files, and nothing
|
|
556
|
-
leaves the process unless you configure a webhook or command.
|
|
557
|
-
|
|
558
|
-
---
|
|
559
|
-
|
|
560
|
-
## Stability
|
|
561
|
-
|
|
562
|
-
Flecto runs inside your merge path, so here is what you can build against.
|
|
563
|
-
These follow [semver](https://semver.org/) and are covered by the deprecation
|
|
564
|
-
sequence below:
|
|
565
|
-
|
|
566
|
-
- **The JSON envelope** (`schema_version: "2.0"`) — existing fields keep their
|
|
567
|
-
name, type, and meaning; new fields are additive. Schemas in [`schemas/`](schemas).
|
|
568
|
-
- **Exit codes** — `0` clean, `1` a fail trigger matched or the run could not
|
|
569
|
-
complete. That is the whole set, and Flecto fails closed.
|
|
570
|
-
- **`.flectorc`** — documented keys keep their name, meaning, and default.
|
|
571
|
-
- **Command and flag names**, and what a flag accepts.
|
|
572
|
-
|
|
573
|
-
**No breaking change to those ships without a minor release that warns first**,
|
|
574
|
-
names the replacement, and says which version removes the old form. The one
|
|
575
|
-
exception is a security fix: if a surface can make `flecto ci` report a clean run
|
|
576
|
-
on a change that is not clean, it gets closed in the next release with an
|
|
577
|
-
advisory. 4.0 was exactly that — five breaking changes, every one a bypass.
|
|
578
|
-
|
|
579
|
-
Deliberately **not** stable: terminal and `pr-comment` output (presentation —
|
|
580
|
-
parse `--format json` instead), message wording, anything under `src/`, and
|
|
581
|
-
snapshot file internals. Built-in packs gain rules in minor releases; rule IDs
|
|
582
|
-
never change meaning.
|
|
583
|
-
|
|
584
|
-
Flecto reached 4.0 in four months, which is fast. That churn was front-loaded
|
|
585
|
-
into a period with no real users, and 4.0 was forced by a
|
|
586
|
-
[security review](docs/security-review.md) finding real bypasses. The intent now
|
|
587
|
-
is minor releases only — anything needing a 5.0 waits in
|
|
588
|
-
[`docs/v5-proposals.md`](docs/v5-proposals.md).
|
|
589
|
-
|
|
590
|
-
→ **[Full stability policy](docs/stability.md)**
|
|
591
|
-
|
|
592
|
-
---
|
|
593
|
-
|
|
594
195
|
## Project
|
|
595
196
|
|
|
596
197
|
- **Questions and ideas** — [Discussions](https://github.com/myselfsiddharth/Flecto/discussions)
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"access": "public",
|
|
5
5
|
"provenance": true
|
|
6
6
|
},
|
|
7
|
-
"version": "4.1.
|
|
7
|
+
"version": "4.1.1",
|
|
8
8
|
"description": "Reads your Terraform plan and Kubernetes changes and posts a plain-English risk summary on every pull request, blocking the dangerous ones",
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"keywords": [
|