Roadmap¶
compose-lint today ships 27 security rules, PyPI distribution, a published GitHub Action and Docker image, SARIF/JSON/text output, pre-commit support, per-service rule overrides, and --explain. The foundation is solid; the next milestone is the 1.0 stability commitment. Remaining investments make the tool more useful to the users already running it, not chase speculative distribution channels.
Strategic framing¶
compose-lint's differentiation is depth in Compose-specific security, not distribution breadth. Competitors (KICS, Checkov, Trivy) cover Compose as one format among many; they are wide and shallow per-format. Compose-lint wins by being the one tool that tells you exactly what's wrong with a Compose file and exactly how to fix it. Roadmap priorities are ordered around that thesis.
Open issues #4 (CL-0006 capability guidance) and #111 (real-world examples library) are the live signal from real-world usage now that #5 is closed. Distribution items beyond the already-shipped Docker image have no demand signal and are deprioritized accordingly.
Milestone 1 — Rule Coverage (v0.3) [complete]¶
Shipped in v0.3.0. Added 9 rules (CL-0011 – CL-0019) plus CL-0010 uts: host enhancement, bringing the total to 19 rules. See CHANGELOG.md.
Milestone 2 — Configuration Depth (v0.4) [shipped]¶
Per-service rule overrides shipped in v0.4.0 (issue #5, ADR-010). .compose-lint.yml now supports exclude_services per rule, with mapping (service → reason) and list forms. Excluded services still produce suppressed findings carrying the per-service reason — same suppression plumbing as global disables.
Milestone 2.5 — Trust Surface + Install Polish (v0.4.x)¶
The leftover Milestone 2 items, plus the new real-world examples ask. All additive over 0.4.0; no breaking changes.
CL-0006 capability guidance (issue #4)
- Make the generic <SPECIFIC_CAP> placeholder actionable by teaching users how to determine the capabilities an image needs, rather than shipping a per-image answer.
- Fix guidance gains: how to read an Operation not permitted failure, docker diff and the capable BPF tool for auditing, and the "if the entrypoint switches users, you likely need SETUID + SETGID" heuristic.
- Docs only, no engine changes. Superseding history: shipping derived per-image profiles was attempted as the profile-enrichment preview and withdrawn in 0.15.0 — see ADR-019. Do not reintroduce a bundled capability table without revisiting that decision.
Real-world examples library (issue #111)
- docs/examples/<name>/, one directory per example: index.md for the narrative, the Compose files alongside it. mkdocs copies the non-Markdown files verbatim, so each example publishes to the docs site at /examples/<name>/ with the exact files the narrative lints downloadable next to it. A .compose-lint.yml still ships for runnability, but mkdocs skips dotfiles — show the suppression config inline in the page too.
- Each example shows upstream Compose file → raw compose-lint output → hardened version → suppression config for unfixable findings, with per-finding narrative.
- Examples come from continuously deployed stacks rather than well-known upstream files, so every hardened value has an empirical basis (required post-ADR-019, which withdrew the derived profile catalog). Tier 1 walks CL-0001 up four escalating remediations — remove the service, re-architect so the socket is not needed, constrain it behind a GET-only socket proxy, suppress with a dated migration plan. Tier 2 adds a multi-service cap_drop: ALL case, a deliberately clean single-suppression edge proxy, and a CL-0007-versus-CL-0022 tension case.
- Teaching surface for ADR-010 suppression semantics, and for which remediation a given finding actually allows — both only get interesting against real files.
- Point-in-time by design: deployed files are sanitized before landing, re-linted locally after sanitizing (scrubbing paths and addresses can move CL-0005/CL-0013 findings), and stamped "last verified against <version>". Refreshes are manual; no drift-check automation.
Homebrew tap
- brew install tmatens/tap/compose-lint — works on macOS (Intel + Apple Silicon) and Linux via Homebrew-on-Linux.
- Closes the "not everyone has pip" gap with working brew upgrade UX (which GitHub-Releases-hosted .deb/.rpm could not match).
- Formula lives in a separate homebrew-tap repo; release workflow uses brew bump-formula-pr to keep versions in sync with low manual overhead.
Deferred: .deb/.rpm Linux packages (see ADR-008 — no user demand, no upgrade path without hosted repo infrastructure).
Milestone 3 — Remediation (v0.5)¶
Turn findings into fixes. This is where the product's differentiation grows the most against KICS/Checkov.
--explain CL-XXXX (shipped in v0.4.x) — prints the full prose from docs/rules/CL-XXXX.md in the terminal, reducing context-switching to the browser during triage. Rule-doc markdown is force-included into the wheel at build time. No new deps; pulled forward out of Milestone 3 because it's strictly additive and unblocks the --fix UX work.
fix subcommand (shipped; promoted to the documented, SemVer-covered surface in 0.11.0 — ADR-014) — auto-fix for safe, unambiguous rules:
- Five fixers: CL-0003 (no-new-privileges:true), CL-0005 (bind published ports to 127.0.0.1), CL-0007 (read_only: true), CL-0009, CL-0014.
- Dry run by default; fix --apply writes in-place via an atomic swap; --only CL-XXXX scopes to named rules.
- Refuses anchors/merge keys/${VAR} regions and guards every apply with a re-parse + verify-apply pass.
- Out of scope for auto-fix: CL-0001 (socket proxy replacement is non-trivial), CL-0006 (capability lists are image-specific), CL-0016 (correct secret management is context-dependent).
Remediation snippets in SARIF (shipped in 0.11.0) — check --format sarif populates fixes[].artifactChanges so GitHub Code Scanning displays a suggested-change diff inline on pull requests.
Shellcheck integration (pending decision — ADR-007)
- Lint shell commands inside command and entrypoint (string form) and healthcheck.test with CMD-SHELL.
- Unique coverage vs. KICS/Checkov — reinforces the "depth" thesis.
- Optional dependency; rule skips silently if shellcheck is not in PATH.
Milestone 3.5 — Severity Grounding (v0.16) [shipped]¶
A prerequisite for the 1.0 freeze rather than a feature: after 1.0 a severity change is a breaking change, so the numbers have to be defensible before the contract closes over them.
Severities are derived, not chosen (shipped in 0.16.0) — a two-axis matrix under a stated attacker baseline and a stated Docker posture produces each rule's value; shipping a different one requires a declared override from a closed reason list, with a link. Every rule page carries its derivation block, and a test fails if the page and the severity table disagree. See ADR-020, ADR-021, ADR-022.
Grounded against a live daemon, not against reasoning (shipped in 0.16.0) — scripts/validate_rule_premises.py asserts the daemon under test is at Docker's defaults before measuring anything, and aborts if it is not. A premise measured on a hardened or loosened daemon returns a confidently wrong answer.
Branching rules split by what they grant — cap_add became CL-0011, CL-0024, CL-0027 and CL-0028 in 0.16.0; CL-0029 (host availability) and CL-0030 (host disclosure) followed, and every remaining capability now has a recorded disposition, so the ungraded set is closed. Host paths became CL-0013 and CL-0025. A rule that branched its severity could not be represented honestly in SARIF, where security-severity sits on the rule descriptor (issue #503) — the split fixes that as a side effect.
Not covered by this milestone: the fixers, the parser, line-number accuracy and the config layer have not had an equivalent audit. None of them is a severity question, and none blocks the freeze.
Milestone 4 — GA / 1.0¶
v1.0 is the stability commitment: the CLI surface, exit codes, configuration schema, and the JSON/SARIF output shapes come under SemVer. Breaking any of them after 1.0 requires a major version bump. The VS Code extension is explicitly not a 1.0 blocker — it's a reach multiplier that doesn't gate stability, and moves to Milestone 5.
GA criteria:
- Stable, documented contract — CLI flags, exit codes (ADR-006), the .compose-lint.yml schema, and the JSON + SARIF output shapes are frozen and documented as the 1.0 surface. The JSON output gains a versioned envelope before the freeze, so run-level metadata (tool version, parse errors) can be added later without breaking consumers.
- fix resolved — done. Shipped as GA and brought under the SemVer contract in 0.11.0 (ADR-014), independently of the 1.0 cut.
- Grounding + severity audit complete — done for severity. Every rule cites OWASP/CIS/Docker and derives its severity from the documented model (Milestone 3.5). What remains before the freeze is confirming no further severity change is pending that would alter a CI gate.
- Documented upgrade/deprecation policy — the SemVer stability promise (rule additions, severity changes, config and output-shape changes) and the deprecation lifecycle, in compatibility.md.
At GA: bump the PyPI classifier from 4 - Beta to 5 - Production/Stable in the version→1.0.0 commit, and publish a moving v1 Action tag so users can pin uses: tmatens/compose-lint@v1.
Milestone 5 — Ecosystem Integrations (v1.x)¶
Pursue based on user demand after v1.0.
| Integration | Notes |
|---|---|
| VS Code extension | Shells out to compose-lint --format json on save (no embedded Python). Inline diagnostics, hover shows fix:/ref:, Fix All Auto-fixable command. The biggest editor reach multiplier once fix lands. |
| GitLab SAST template | SARIF upload to GitLab Security Dashboard |
| Azure DevOps task | Published to VS Marketplace |
| JetBrains plugin | Same shell-out pattern as VS Code |
| Custom rule plugins | entry_points hook (compose_lint.rules group) for third-party rules |
| LSP server | Language Server Protocol support — follows VS Code extension post-v1.0 |
Linux packages (.deb/.rpm) |
Revisit ADR-008 on first concrete user request |
Out of Scope¶
- Full Compose schema validation — intentionally excluded; the niche is security, not correctness
- Kubernetes/Helm support — would dilute the zero-config, Compose-specific positioning
- SaaS offering — adds infrastructure, compliance, and billing complexity with no clear moat over the CLI
Python version support¶
Track current CPython: add new minor versions to the CI matrix within ~3 months of each October release, drop versions at upstream end-of-life. Adding a version is additive (PATCH per docs/RELEASING.md); dropping a version is a MINOR pre-1.0 and a MAJOR post-1.0.
| Version | Released | EOL | In matrix as of |
|---|---|---|---|
| 3.10 | 2021-10 | 2026-10 | 0.2.0 |
| 3.11 | 2022-10 | 2027-10 | 0.2.0 |
| 3.12 | 2023-10 | 2028-10 | 0.2.0 |
| 3.13 | 2024-10 | 2029-10 | 0.2.0 |
| 3.14 | 2025-10 | 2030-10 | 0.3.8 |
Python 3.10 is scheduled to age out of the matrix when it reaches upstream EOL in October 2026 (release bump to 0.4.x or later).
Summary¶
| Milestone | Version | Status |
|---|---|---|
| Rule Coverage (19 rules) | v0.3 | complete |
| Per-service rule overrides | v0.4 | complete |
| CL-0006 capability guidance + real-world examples + Homebrew tap | v0.4.x | in progress |
Remediation (--explain, fix, SARIF fixes, shellcheck) |
v0.5–0.11 | fix GA in 0.11.0; shellcheck pending |
| Severity grounding — derived severities, capability + host-path splits | v0.16 | complete |
GA / 1.0 — stable contract + fix + upgrade policy |
v1.0 | next |
| Ecosystem integrations (VS Code, custom rules) | v1.x |