Skip to content

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 grantcap_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 resolveddone. Shipped as GA and brought under the SemVer contract in 0.11.0 (ADR-014), independently of the 1.0 cut. - Grounding + severity audit completedone 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