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.
Files changed (3) hide show
  1. package/CHANGELOG.md +32 -1
  2. package/README.md +107 -506
  3. 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.0...HEAD
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>Know what your config actually changed — and whether it's risky.</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
- ## Install
53
-
54
- ```bash
55
- npm install -g flecto
56
- ```
17
+ ## What lands on the pull request
57
18
 
58
- Requires **Node.js 20.19.0+**. Verify:
59
-
60
- ```bash
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
- Prefer not to install globally? Every example below works with
66
- `npx --yes flecto@4` instead of `flecto`.
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
- ## Quick start
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
- A complete walkthrough, start to finish. Copy-paste it anywhere.
36
+ <details>
37
+ <summary>The same report as text, and what a plan with existing state adds</summary>
73
38
 
74
- **1. Create a config file to track.**
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
- **2. Save it as your baseline.**
90
-
91
- ```bash
92
- flecto watch config/prod.yaml --snapshot
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
- ✓ Snapshot saved: /path/to/flecto-demo/.flecto-snapshots/4b8cbbd70d1832a2.json
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
- **3. Make the kind of edit that causes incidents.**
100
-
101
- ```bash
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
- **4. Ask what changed.**
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
- /path/to/flecto-demo/config/prod.yaml — 2 changes from snapshot:
121
- ~ database.pool_size: 5 → 20
122
- ~ logging.debug: false → true
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
- Two sentences instead of a diff you have to interpret. Now let Flecto judge it:
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
- ## What you can do with it
75
+ ## Add it in 60 seconds
162
76
 
163
- ### Catch risky changes before they merge
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
- fetch-depth: 2
176
- - uses: myselfsiddharth/Flecto/.github/actions/flecto-ci@v4.0.0
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
- Prefer a summary nobody can miss? `--format pr-comment` renders the changes and
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
- - uses: myselfsiddharth/Flecto/.github/actions/flecto-pr-risk@v4.0.0
201
- ```
202
-
203
- GitLab and Bitbucket work the same way — Flecto detects the host from CI
204
- variables, or `--pr-provider` forces one. See [CI](docs/ci.md#providers).
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
- Arrays of objects are matched by `id` or `name`, so reordering a list of named
363
- services doesn't read as a wall of changes.
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
- ## Built-in policy packs
369
-
370
- | Pack | Catches |
371
- |---|---|
372
- | `default` | Secret-like keys added or changed, secret-shaped *values* under any key, dangerous toggles, pool-size jumps |
373
- | `strict-prod` | The same ground, with production-grade severities and matching |
374
- | `compose` | Privileged services, host networking, Docker socket mounts, sensitive bind mounts |
375
- | `kubernetes` | Privileged containers, host namespaces, weakened `runAsNonRoot`, added `SYS_ADMIN`, unpinned images, replica jumps, dropped limits, `LoadBalancer`/`NodePort` exposure |
376
- | `node-runtime` | Dropped engine requirements, TLS verification bypasses, debug/inspector flags |
377
- | `terraform` | Replaced and destroyed stateful resources, ingress opened to `0.0.0.0/0`, IAM wildcards, public S3, capacity jumps |
378
- | `sops` | Decryption recipients added or removed, a MAC that moved on its own, a file that stopped being encrypted |
379
- | `github-actions` | Changed workflow triggers, widened permissions, self-hosted runners, unpinned actions, pull-request head checkout, and secrets interpolated into `run` |
380
-
381
- ```bash
382
- flecto policies list # see what resolves here, built-in and local
383
- flecto ci "config/**/*.yaml" --policies "default,strict-prod" --fail-on policy
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
- ## Supported formats
139
+ ## Also works as
408
140
 
409
- | Format | Extensions |
141
+ | | |
410
142
  |---|---|
411
- | JSON / JSONC | `.json`, `.jsonc` |
412
- | YAML | `.yaml`, `.yml` |
413
- | TOML | `.toml` |
414
- | INI | `.ini` |
415
- | dotenv | `.env`, `.env.*`, `*.env` |
416
- | age (armored) | `.age`, or any file whose contents are one armored blob |
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
- ## Configuration
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
- Explicit CLI flags win over profiles, which win over `defaults`.
492
- → **[Full configuration reference](docs/configuration.md)**
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
- ## Commands
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
- | **[CLI reference](docs/cli-reference.md)** | Every command, flag, and exit code |
526
- | **[Configuration](docs/configuration.md)** | `.flectorc`, profiles, ignore patterns, array identity, masking |
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
- | **[Policy packs](docs/policy-packs.md)** | Writing declarative rules |
537
- | **[Plugins](docs/plugins.md)** · **[Cookbook](docs/plugin-cookbook.md)** | Rules that need real code |
538
- | **[Live drift](docs/drift.md)** | `flecto-drift`: comparing a declared config against what is actually running |
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
- | **[Stability](docs/stability.md)** | What you can build against, what you cannot, and the deprecation sequence |
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.0",
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": [