flecto 2.1.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/README.md CHANGED
@@ -5,52 +5,35 @@
5
5
  <h1 align="center">Flecto</h1>
6
6
 
7
7
  <p align="center">
8
- <strong>Config changes, in plain Englishwith risk flags built in.</strong><br/>
9
- Watch · Diff · Policy · CI · Webhooks
8
+ <strong>Know what your config actually changed and whether it's risky.</strong>
10
9
  </p>
11
10
 
12
11
  <p align="center">
13
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>
14
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>
15
14
  <a href="LICENSE"><img alt="MIT" src="https://img.shields.io/badge/license-MIT-8fa3bf?style=flat-square&labelColor=0b1220"/></a>
16
- <a href="https://github.com/myselfsiddharth/Flecto/stargazers"><img alt="Stars" src="https://img.shields.io/github/stars/myselfsiddharth/Flecto?style=flat-square&color=fbbf24&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>
17
16
  </p>
18
17
 
19
18
  <p align="center">
20
- <a href="https://github.com/myselfsiddharth/Flecto/stargazers">⭐ Star this repo</a> if Flecto saves you from a noisy config diff it helps others find the project.
21
- </p>
22
-
23
- <p align="center">
24
- <img src="docs/assets/flecto-demo.png" alt="Flecto watch demo in the terminal" width="920"/>
25
- </p>
26
-
27
- <p align="center">
28
- <img src="docs/assets/demo-watch.svg" alt="Animated Flecto watch output" width="920"/>
19
+ <img src="docs/assets/demo-watch.svg" alt="Flecto reporting semantic config changes in the terminal" width="920"/>
29
20
  </p>
30
21
 
31
22
  ---
32
23
 
33
- ## Why teams use Flecto
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.
34
28
 
35
- Line diffs lie about config. Formatting churn, key reorders, and “small” YAML edits hide the changes that actually matter in production.
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.
36
31
 
37
- **Flecto** turns structured config into semantic events:
38
-
39
- - What changed (`pool_size: 5 → 20`)
40
- - What was added or removed
41
- - What looks risky (secrets, dangerous toggles, pool jumps)
42
- - What to do next (CI gate, webhook, shell command)
43
-
44
- See the [changelog and migration notes](CHANGELOG.md) for release history and
45
- upcoming v2.1 behavior changes.
46
-
47
- > Diff tools compare trees. **Flecto watches, scores risk, and alerts.**
48
-
49
- | Without Flecto | With Flecto |
32
+ | Reviewing config without Flecto | With Flecto |
50
33
  |---|---|
51
- | `+ 40 lines of YAML noise` | `~ database.pool_size: 5 → 20` |
52
- | Hope someone notices `debug: true` | Policy finding → CI fails |
53
- | Something in `.env` changed | Exact keys + optional secret masking |
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 |
54
37
 
55
38
  ---
56
39
 
@@ -60,358 +43,372 @@ upcoming v2.1 behavior changes.
60
43
  npm install -g flecto
61
44
  ```
62
45
 
63
- Requires Node.js 20.19.0 or later.
46
+ Requires **Node.js 20.19.0+**. Verify:
64
47
 
65
48
  ```bash
66
49
  flecto --version
67
50
  flecto doctor
68
51
  ```
69
52
 
70
- ---
71
-
72
- ## Quick start
73
-
74
- ```bash
75
- flecto watch config/prod.yaml
76
- flecto watch .env
77
- flecto watch settings.json
78
- flecto watch pyproject.toml
79
- flecto watch app.ini
80
- ```
81
-
82
- That’s it — Flecto prints a clear summary on every meaningful change.
53
+ Prefer not to install globally? Every example below works with
54
+ `npx --yes flecto@3` instead of `flecto`.
83
55
 
84
56
  ---
85
57
 
86
- ## Features at a glance
87
-
88
- - **Semantic diffs** for JSON, YAML, TOML, INI, and dotenv (`.env`, `.env.*`, `*.env`)
89
- - **Live watch** with optional command + webhook delivery
90
- - **Policy packs** (`default`, `strict-prod`, `compose`, `node-runtime`) + custom `policies/*.json` + local ESM plugins
91
- - **CI mode** with JSON / NDJSON / GitHub annotations and fail rules
92
- - **Snapshots & diffs** for deploy scripts and pre-commit hooks
93
- - **Profiles** via `--profile` or `FLECTO_PROFILE`
94
- - **Default array identity** (`id` / `name`, or `--array-id-key`) and secret masking
95
-
96
- ---
58
+ ## Quick start
97
59
 
98
- ## Common use cases
60
+ A complete walkthrough, start to finish. Copy-paste it anywhere.
99
61
 
100
- ### Watch multiple files
62
+ **1. Create a config file to track.**
101
63
 
102
64
  ```bash
103
- flecto watch "config/**/*.yaml" ".env"
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
104
75
  ```
105
76
 
106
- ### Verbose before/after
77
+ **2. Save it as your baseline.**
107
78
 
108
79
  ```bash
109
- flecto watch config/prod.yaml --mode verbose
80
+ flecto watch config/prod.yaml --snapshot
81
+ ```
82
+
83
+ ```
84
+ ✓ Snapshot saved: /path/to/flecto-demo/.flecto-snapshots/4b8cbbd70d1832a2.json
110
85
  ```
111
86
 
112
- ### Ignore noisy keys
87
+ **3. Make the kind of edit that causes incidents.**
113
88
 
114
89
  ```bash
115
- flecto watch config/prod.yaml --ignore "updated_at,meta.timestamp"
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
116
99
  ```
117
100
 
118
- | Pattern | What it ignores |
119
- |---|---|
120
- | `meta.timestamp` | That exact key |
121
- | `meta` | Everything under `meta.*` |
122
- | `servers[*].meta.timestamp` | That key inside any array item |
123
- | `**.updated_at` | Any key named `updated_at`, anywhere |
124
-
125
- ### Run a command on change
101
+ **4. Ask what changed.**
126
102
 
127
103
  ```bash
128
- flecto watch .env --command "docker-compose restart app"
104
+ flecto watch config/prod.yaml --diff
129
105
  ```
130
106
 
131
- Changes are passed as JSON via `FLECTO_CHANGES` (large payloads may use `FLECTO_CHANGES_FILE`).
107
+ ```
108
+ /path/to/flecto-demo/config/prod.yaml — 2 changes from snapshot:
109
+ ~ database.pool_size: 5 → 20
110
+ ~ logging.debug: false → true
111
+ ```
132
112
 
133
- ### Webhooks
113
+ Two sentences instead of a diff you have to interpret. Now let Flecto judge it:
134
114
 
135
115
  ```bash
136
- flecto watch config/prod.yaml \
137
- --webhook https://hooks.example.com/notify \
138
- --webhook-header "Authorization: Bearer TOKEN"
116
+ flecto ci config/prod.yaml --format github-annotations
139
117
  ```
140
118
 
141
- Envelope shape (`schema_version: "2.0"`):
142
-
143
- ```json
144
- {
145
- "schema_version": "2.0",
146
- "event_id": "uuid",
147
- "event_type": "changes",
148
- "emitted_at": "2026-04-14T10:42:31.000Z",
149
- "file": "/absolute/path/to/config/prod.yaml",
150
- "changes": [
151
- { "type": "changed", "path": "database.pool_size", "before": 5, "after": 20 }
152
- ],
153
- "policies": [
154
- {
155
- "id": "pool-size-jump",
156
- "severity": "warn",
157
- "path": "database.pool_size",
158
- "message": "Pool size increased from 5 to 20 (>=2x).",
159
- "pack": "default"
160
- }
161
- ]
162
- }
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.
163
124
  ```
164
125
 
165
- JSON Schema: [`schemas/flecto-envelope-2.0.json`](schemas/flecto-envelope-2.0.json).
126
+ Exit code `1`. In CI, that's a failed build — before the change ships.
166
127
 
167
- ### Policy packs and profiles
128
+ **5. Watch it live.** Leave this running and edit the file in another window:
168
129
 
169
130
  ```bash
170
- flecto ci config/prod.yaml --profile prod --snapshot-ref HEAD~1
131
+ flecto watch config/prod.yaml
171
132
  ```
172
133
 
173
- ```json
174
- {
175
- "defaults": {
176
- "policies": ["default"],
177
- "maskSecrets": false
178
- },
179
- "profiles": {
180
- "prod": {
181
- "policies": ["default", "strict-prod"],
182
- "severityRemap": { "pool-size-jump": "error" },
183
- "maskSecrets": true
184
- }
185
- }
186
- }
134
+ ```
135
+ flecto watching /path/to/flecto-demo/config/prod.yaml
136
+ Press Ctrl+C to stop.
137
+
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.
187
143
  ```
188
144
 
189
- Profile selection: `--profile` > `FLECTO_PROFILE` > defaults.
190
- Custom packs: `policies/<id>.json`. Plugins: local ESM exporting `evaluate(changes, ctx)`.
145
+ That's the whole product. Everything below is depth.
191
146
 
192
- #### Declarative rule predicates
147
+ ---
193
148
 
194
- Rules combine their top-level predicates with AND. In addition to `when`, regex
195
- `match.path`, `afterEquals`, and `numericJump`, packs can use:
149
+ ## What you can do with it
196
150
 
197
- - `beforeEquals`, `afterIn`, and `beforeIn` for exact values or allowed value lists.
198
- - `beforeTruthy: true` and `afterTruthy: true` to require a truthy before/after value.
199
- - `afterMatches` to require a string after value that matches a regular expression.
200
- - `numericDelta: { "min": 10 }` to match an absolute numeric change of at least 10.
201
- - `match.pathEquals` and `match.pathPrefix` for exact or prefix path matching without regex.
202
- - `allOf` and `anyOf` arrays of simple match clauses. Every `allOf` clause and at least
203
- one `anyOf` clause must match. Clauses support the same value, truthiness, numeric, and
204
- `match` predicates, but cannot nest composition.
151
+ ### Catch risky changes before they merge
205
152
 
206
- ```json
207
- {
208
- "id": "risky-feature-enable",
209
- "severity": "error",
210
- "allOf": [
211
- { "match": { "pathPrefix": "features." } },
212
- { "afterTruthy": true }
213
- ],
214
- "anyOf": [
215
- { "afterEquals": true },
216
- { "afterIn": ["unsafe", "disabled"] }
217
- ]
218
- }
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
219
168
  ```
220
169
 
221
- Pack loading fails closed for unknown rule or `match` fields, invalid regexes, and invalid
222
- predicate shapes, so misspelled predicates cannot silently disable a rule.
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.
223
175
 
224
- Use `severityRemap` in defaults or a profile to change pack rule severities without forking a pack:
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`:
225
178
 
226
- ```json
227
- {
228
- "profiles": {
229
- "dev": {
230
- "severityRemap": { "pool-size-jump": "off" }
231
- },
232
- "prod": {
233
- "severityRemap": { "pool-size-jump": "error" }
234
- }
235
- }
236
- }
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
237
189
  ```
238
190
 
239
- Each key is a rule id and each value must be `info`, `warn`, `error`, or `off`. The remap applies after all configured built-in and local packs load, before findings and CI `--fail-on` checks. When multiple packs provide the same rule id, the remap applies to every matching pack rule. Plugin findings are unchanged. Unknown rule ids print a warning instead of being ignored silently.
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)**
240
196
 
241
- Authoring guides: [policy packs](docs/policy-packs.md) · [plugins](docs/plugins.md) · [plugin cookbook](docs/plugin-cookbook.md).
197
+ ### Trigger automation on change
242
198
 
243
- ### Discover policy packs
199
+ Restart a service, reload a process, or notify an endpoint whenever config moves:
244
200
 
245
201
  ```bash
246
- flecto policies list
247
- flecto policies list --json
202
+ flecto watch .env --command "docker-compose restart app"
203
+
204
+ flecto watch config/prod.yaml \
205
+ --webhook https://hooks.example.com/notify \
206
+ --delivery-mode at-least-once
248
207
  ```
249
208
 
250
- The command lists every bundled and local pack that resolves from the current
251
- working directory, including its source path and rule count. For a given pack
252
- id, Flecto resolves local files before bundled packs in this order:
253
- `policies/<id>.json`, `policies/<id>.yaml`, `policies/<id>.yml`, then the
254
- built-in pack. A local pack with the same id overrides its built-in counterpart.
209
+ Changes arrive as a versioned JSON envelope, and `at-least-once` persists and
210
+ retries failed deliveries.
255
211
 
256
- ### Array identity matching
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:
257
215
 
258
216
  ```bash
259
- flecto watch config/services.yaml
217
+ flecto watch config/prod.yaml \
218
+ --webhook "https://hooks.slack.com/services/T000/B000/XXXX" \
219
+ --webhook-format slack
260
220
  ```
261
221
 
262
- Arrays of objects automatically match by a shared, unique `id` key, falling back
263
- to `name` when `id` is unavailable. This avoids false changes when named items
264
- are reordered. Use a custom identity field when needed:
222
+ **[Webhooks and commands](docs/webhooks.md)**
265
223
 
266
- ```bash
267
- flecto watch config/services.yaml --array-id-key serviceKey
268
- ```
224
+ ### Compare two environments
269
225
 
270
- To restore index-based diffs for every array, pass `--no-array-id`:
226
+ "Works in staging, fails in prod" is usually one key apart:
271
227
 
272
228
  ```bash
273
- flecto watch config/services.yaml --no-array-id
229
+ flecto compare config/prod.yaml config/staging.yaml
230
+ ```
231
+
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).
274
238
  ```
275
239
 
276
- In `.flectorc`, set `"arrayId": false` in `defaults` or a profile for the same
277
- escape hatch.
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)**
278
243
 
279
- ### Command + webhook together
244
+ ### Track drift over time
280
245
 
281
246
  ```bash
282
- flecto watch .env \
283
- --command "make reload" \
284
- --webhook https://hooks.example.com/notify
247
+ flecto history config/prod.yaml --limit 10
285
248
  ```
286
249
 
287
- ### Retry on failure
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)**
252
+
253
+ ### Share what changed before the incident
288
254
 
289
255
  ```bash
290
- flecto watch config/prod.yaml \
291
- --webhook https://hooks.example.com/notify \
292
- --delivery-mode at-least-once \
293
- --on-alert-failure retry
256
+ flecto report --limit 20 --mask-secrets --output drift.html
294
257
  ```
295
258
 
296
- | Flag | Options | What it does |
297
- |---|---|---|
298
- | `--delivery-mode` | `best-effort` (default), `at-least-once` | Persist and retry failed webhook events |
299
- | `--on-alert-failure` | `warn`, `exit`, `retry` | Behavior when command/webhook fails |
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)**
300
265
 
301
- ---
266
+ ### Review a Kubernetes change before it reaches a cluster
302
267
 
303
- ## Snapshots & diffs
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:
304
271
 
305
272
  ```bash
306
- flecto watch config/prod.yaml --snapshot
307
- flecto watch config/prod.yaml --diff
308
- flecto history config/prod.yaml --limit 10
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
309
275
  ```
310
276
 
311
- Exit codes: `0` clean · `1` changes detected.
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
+ ```
312
282
 
313
- `flecto history` stays local: it lists recent snapshots from `.flecto-snapshots/` with their timestamps and semantic change counts from the previous snapshot. Counts use the same ignore paths, array identity, and order settings as `flecto watch --diff` (CLI flags or `.flectorc`). Omit files to view all saved snapshot history.
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)**
314
287
 
315
- ---
288
+ ### Encode your own rules
316
289
 
317
- ## CI mode
290
+ Beyond the built-in packs, write rules as declarative JSON or YAML — no code:
318
291
 
319
- ```bash
320
- flecto ci "config/**/*.yaml" \
321
- --snapshot-ref HEAD~1 \
322
- --format github-annotations \
323
- --fail-on "changed,policy,error"
292
+ ```json
293
+ {
294
+ "id": "risky-feature-enable",
295
+ "severity": "error",
296
+ "allOf": [
297
+ { "match": { "pathPrefix": "features." } },
298
+ { "afterTruthy": true }
299
+ ]
300
+ }
324
301
  ```
325
302
 
326
- **Formats:** `json`, `ndjson`, `github-annotations`
327
- **Fail triggers:** `changed`, `added`, `removed`, `policy`, `error`, `warn`
328
- Unresolved `--snapshot-ref` fails closed (no silent empty baseline).
329
- If every target is missing or unsupported, `flecto ci` and `flecto watch --snapshot` exit non-zero (pass `--allow-empty` to opt out).
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)**
330
306
 
331
- ### GitHub Action
307
+ ### Cut the noise
332
308
 
333
- Use the [Flecto CI Action](.github/actions/flecto-ci/action.yml) to run `flecto ci` in a workflow with GitHub annotations enabled by default. A complete local-action workflow is available at [`examples/github-action/flecto-ci.yml`](examples/github-action/flecto-ci.yml).
309
+ ```bash
310
+ flecto watch config/prod.yaml --ignore "updated_at,**.meta.timestamp"
311
+ ```
334
312
 
335
- ```yaml
336
- permissions:
337
- contents: read
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)**
338
316
 
339
- steps:
340
- - uses: actions/checkout@v7
341
- with:
342
- fetch-depth: 2
343
- - uses: myselfsiddharth/Flecto/.github/actions/flecto-ci@main
344
- with:
345
- targets: config/**/*.{yaml,yml,json,toml,ini}
346
- snapshot-ref: HEAD~1
347
- ```
317
+ ---
348
318
 
349
- `contents: read` is required by `actions/checkout`. The Action emits workflow-command annotations and needs no write permissions. Keep `fetch-depth: 2` (or use `fetch-depth: 0`) when using the default `HEAD~1` baseline.
319
+ ## Built-in policy packs
350
320
 
351
- | Input | Default | Description |
352
- |---|---|---|
353
- | `targets` | `config/**/*.{yaml,yml,json,toml,ini}` | Whitespace-separated paths or glob patterns to check |
354
- | `fail-on` | `policy,error` | Comma-separated events that fail the job |
355
- | `policies` | _(empty)_ | Comma-separated policy pack IDs; omit to use `.flectorc` / Flecto defaults |
356
- | `profile` | _(empty)_ | Optional `.flectorc` profile |
357
- | `format` | `github-annotations` | Flecto output format |
358
- | `snapshot-ref` | `HEAD~1` | Git ref or snapshot file used as the baseline |
359
- | `node-version` | `20` | Node.js version used to run Flecto |
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 |
360
330
 
361
- The Action runs `npx --yes flecto@2 ci`: the major version is pinned so compatible Flecto updates are received. For fully reproducible builds, pin the Action reference to a commit SHA and replace `@2` in a forked Action with an exact published Flecto version.
331
+ ```bash
332
+ flecto policies list # see what resolves here, built-in and local
333
+ flecto ci "config/**/*.yaml" --policies "default,strict-prod" --fail-on policy
334
+ ```
362
335
 
363
- ---
336
+ Pass files or glob patterns, quoted so your shell doesn't expand them first — a
337
+ bare directory is not a valid target.
364
338
 
365
- ## Built-in policy checks
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)**
366
342
 
367
- Built-in pack ids:
343
+ Community packs ship on npm as `flecto-pack-<id>` packages — a package name and
344
+ one declarative JSON file, nothing else:
368
345
 
369
- - `default` — secrets, dangerous toggles, and pool-size jumps.
370
- - `strict-prod` — stricter severities and matching for production use.
371
- - `compose` privileged services, host networking, Docker socket mounts, and sensitive host-directory bind mounts.
372
- - `node-runtime` — removed Node.js engine requirements, TLS verification bypasses, and enabled Node debugging or inspector options.
346
+ ```bash
347
+ npm install --save-dev flecto-pack-deployment-safety
348
+ flecto policies add deployment-safety
349
+ ```
373
350
 
374
- Fail CI with `--fail-on policy`.
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)**
375
354
 
376
355
  ---
377
356
 
378
- ## Migrating from envelope 1.1
357
+ ## Supported formats
379
358
 
380
- - `schema_version` is now `"2.0"`
381
- - Type name is `FlectoEnvelope` (docs/types)
382
- - New `policies` array on change envelopes
383
- - Webhook headers unchanged (`X-Flecto-*`)
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)**
384
373
 
385
374
  ---
386
375
 
387
- ## Tuning for network drives / odd editors
376
+ ## Encrypted files
377
+
378
+ A `sops`- or age-encrypted file is detected from its **contents**, and diffed
379
+ structurally:
388
380
 
389
- ```bash
390
- flecto watch config/prod.yaml --polling --interval 500
391
381
  ```
382
+ + cache: {"ttl_seconds":300}
383
+ ~ database.password: <encrypted value changed>
384
+ + sops.age.age1exampleexample…: {"recipient":"age1exampleexample…","enc":"<encrypted value>"}
385
+ ```
386
+
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.
392
390
 
393
- Polling is off by default (interval default `100ms` when enabled).
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)**
394
396
 
395
397
  ---
396
398
 
397
- ## Config file (`.flectorc`)
399
+ ## Configuration
400
+
401
+ Most teams commit a `.flectorc` so local runs and CI agree:
398
402
 
399
403
  ```bash
400
404
  flecto init
401
405
  ```
402
406
 
403
- Looks for `.flectorc`, `.flectorc.json`, `.flectorc.yaml`, or `.flectorc.yml`.
404
-
405
407
  ```json
406
408
  {
407
409
  "defaults": {
408
- "mode": "compact",
409
- "interval": 100,
410
- "ignore": ["**.updated_at"],
411
- "deliveryMode": "best-effort",
412
- "onAlertFailure": "warn",
413
410
  "policies": ["default"],
414
- "arrayId": true
411
+ "ignore": ["**.updated_at"]
415
412
  },
416
413
  "profiles": {
417
414
  "dev": { "mode": "verbose" },
@@ -422,81 +419,80 @@ Looks for `.flectorc`, `.flectorc.json`, `.flectorc.yaml`, or `.flectorc.yml`.
422
419
  "maskSecrets": true
423
420
  }
424
421
  },
425
- "files": ["config/**/*.{yaml,yml,json,toml,ini}", ".env", ".env.*", "*.env"],
426
- "exclude": ["**/node_modules/**"]
422
+ "files": ["config/**/*.{yaml,yml,json,toml,ini}", ".env"]
427
423
  }
428
424
  ```
429
425
 
430
426
  ```bash
431
427
  flecto watch --profile dev
432
428
  flecto ci --profile ci
433
- flecto doctor
434
429
  ```
435
430
 
436
- CLI flags override profile/default values.
431
+ Explicit CLI flags win over profiles, which win over `defaults`.
432
+ → **[Full configuration reference](docs/configuration.md)**
437
433
 
438
434
  ---
439
435
 
440
- ## Output format reference
441
-
442
- ### Compact (default)
443
-
444
- ```
445
- [HH:MM:SS] <filepath> — N changes
446
- ~ path: before → after (yellow — value changed)
447
- + path: value (green — key added)
448
- - path: value (red — key removed)
449
- ```
450
-
451
- ### Verbose (`--mode verbose`)
436
+ ## Commands
452
437
 
453
- ```
454
- [HH:MM:SS] <filepath> — N changes
455
- ~ path
456
- before: old_value
457
- after: new_value
458
- ```
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)**
459
455
 
460
456
  ---
461
457
 
462
- ## Error handling
458
+ ## Documentation
463
459
 
464
- | Situation | Behavior |
460
+ | Guide | Covers |
465
461
  |---|---|
466
- | File not found | Error + exit 1 |
467
- | Unsupported format | Lists supported extensions + exit 1 |
468
- | Parse error while watching | Warning, last valid state kept |
469
- | Command / webhook fails | Warning (unless `--on-alert-failure exit`) |
470
- | Ctrl+C | Clean shutdown |
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 |
471
473
 
472
474
  ---
473
475
 
474
476
  ## How it works
475
477
 
476
- 1. **Parser** — format by extension / dotenv naming → structured values
477
- 2. **Watcher** — [chokidar](https://github.com/paulmillr/chokidar) + debounce
478
- 3. **Differ** — semantic tree diff (objects, arrays, ignore rules, automatic array ids)
479
- 4. **Policy engine** — packs + plugins → severity findings
480
- 5. **Envelope** — versioned automation payload (`2.0`)
481
- 6. **Alerter** — command and/or webhook with retry modes
482
-
483
- ---
484
-
485
- ## Contributing
486
-
487
- Flecto is open source. See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, PR rules, and review expectations.
488
- Please read the [Code of Conduct](CODE_OF_CONDUCT.md) and [Security policy](SECURITY.md).
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
489
484
 
490
- Roadmap lives in [GitHub milestones](https://github.com/myselfsiddharth/Flecto/milestones).
485
+ Flecto runs entirely on your machine. Snapshots are local files, and nothing
486
+ leaves the process unless you configure a webhook or command.
491
487
 
492
488
  ---
493
489
 
494
- ## Star the project
495
-
496
- If Flecto helps your team catch a risky config change — **[star the repo](https://github.com/myselfsiddharth/Flecto)** so more people can find it.
497
-
498
- ---
490
+ ## Project
499
491
 
500
- ## License
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)
501
497
 
502
- MIT see [LICENSE](./LICENSE).
498
+ Released under the [MIT License](LICENSE).