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.

The usage-driven asks that shaped the last two milestones — #5 (per-service overrides), #4 (CL-0006 capability guidance) and #111 (real-world examples library) — are all closed and shipped. The live signal now runs through the 1.0 stability commitment (Milestone 4) and the one Milestone 3 item still open, shellcheck (ADR-007, pending decision). 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) [complete]

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) [complete]

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 shipped (issue #4 closed); the examples library shipped both tiers under docs/examples/ (issue #111 closed). The Homebrew tap moved to Milestone 5: like the .deb/.rpm packages deferred below, it has no demand signal yet, and the roadmap's own rule — distribution beyond the shipped channels waits for demand — applies to it too.

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.

Deferred: Homebrew tap — moved to Milestone 5 (design notes preserved in the table there). 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: - Six 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, CL-0022 (drop exec/suid from a tmpfs entry). - 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) [complete]

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. Versioned JSON envelope done. It shipped under ADR-015: check --format json emits a schema version alongside a tool block, so run-level metadata (parse errors, and more later) can be added without breaking consumers. What remains is the freeze itself — declaring the surface closed. - 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. The moving v1 Action tag needs no separate step: publish.yml's action-major-tag job force-moves the major tag on every non-v0 release (lightweight and unsigned by design — the signed claim is the release tag; README documents the trade).


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
Homebrew tap brew install tmatens/tap/compose-lint (macOS Intel/ARM + Homebrew-on-Linux). Closes the "not everyone has pip" gap with working brew upgrade UX. Formula in a separate homebrew-tap repo; release workflow syncs via brew bump-formula-pr. Pursue on demand signal, like the row above
Agent skill / plugin A packaged distillation of Automation and agent use, so the judgment layer reaches an agent working in a repo that has wired neither the pre-commit hook nor the Action. Deferred in ADR-035: no demand signal, and the channel has no staging or signing analog for DISTRIBUTION.md. The shipped docs section and the two gates cover everything else

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); a scheduled drop — announced ≥180 days ahead with the runtime warning, shipping no earlier than upstream EOL — is a MINOR at any point (ADR-029). The eol-watch workflow files the announcement issue when a floor enters its final 180 days.

Version Released EOL In matrix as of
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 was dropped ahead of its October 2026 upstream EOL (issue #643): because dropping a version is a MINOR pre-1.0 and a MAJOR after, landing the drop before 1.0 avoided a routine EOL forcing a 2.0. The deprecation was announced and warned on stderr from 0.22.0, per compatibility.md; 3.10 users stay on the last release that supported it (pip resolves requires-python to 0.23.0).


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 v0.4.x complete (Homebrew tap moved to v1.x)
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