Skip to content

Roadmap

Where Punix is going, and roughly in what order. For what already works, see what works; for what doesn't, limitations.

One rule governs everything on this page: nothing gets built without a real driver: a host in the corpus that needs it, or an explicit product decision. Several designs on this list are finished on paper and deliberately not coded, because code without a consumer is maintenance debt.

Stage 7: Corpus scale βœ…

Done. The brew→PCL translator handles every dominant Homebrew install shape (cargo, go, npm, autotools, cmake, meson, swift, pip, git-tag). 385 recipes build green from source; 67% of Homebrew's 500 most-installed formulae resolve through the corpus.

What's left here is a long tail rather than a milestone: translator extensions (multi-source Perl-CPAN bundles, git-only head do sources, vendored submodules) and per-recipe build fixes. Blockers names each one and the shape of its fix, so a contribution lands in the right layer.

Stage 8: Lifecycle and a second backend 🚧

The current arc. It closes most of limitations.

Already landed:

  • Services actually start. --activate runs daemon-reload + restart over the transport after the atomic flip; --enable makes it survive a reboot. Proven by examples/tangled-deploy, which deploys once and then restarts the container to show the stack coming back from the persisted generation alone.
  • scope = "user": no root anywhere, units in ~/.config/systemd/user/. This is what the planned --unit-dir flag turned into: the unit directory travels with the allowed config roots and the activator verb, so all three move together as one scope rather than as three knobs. Still local-only.
  • punix verify: read-only drift detection against the current generation. Details.
  • Secrets to a 0600 sidecar: automatic for any service whose environment holds a resolved secret.

Still open:

  • A second service backend. launchd (macOS), supervisord, docker-compose. The transport seam already takes them; the interesting part is that the fields you write today are systemd-shaped, so a portable stack needs a neutral service vocabulary underneath, rather than a second renderer. That's a design slice, not a rename.
  • Rollback should check the target's store rather than the deploy host's. punix verify does this correctly and settles the shape; service rollback hasn't adopted it.
  • punix service set-current STACK N: jump to any preserved generation, forwards or backwards, instead of only stepping back one.
  • Cross-arch deploys should build on the target through the transport rather than pushing binaries that can't run there.
  • from_file secrets need their own conformance test: they work; the property isn't pinned.

Stage 9: Release evidence

The proof v2.0 ships with:

  1. A ~3-minute screencast deploying a real monitoring stack to a cloud VM from a Mac.
  2. A blog post whose commands you can paste verbatim and end up with a working Grafana.
  3. Public CI badges, green on x86_64 and aarch64.
  4. This limitations page, complete.

v2.0 means "it works, look at this."

The remaining work is running it: examples/smallorg (four hosts, a derived WireGuard mesh, TLS, Postgres, a backup timer, and a monitoring stack whose scrape list is generated from the fleet model) is green in containers. Pointing it at rented hardware is the next step.

Stage 10: After 2.0

  • Binary caches. The seam is already cut: the build loop asks a substituter for a path before building it, and today that's an empty list. Filling it in with an S3-style or peer-to-peer cache means popular packages stop being rebuilt on every machine.
  • Reproducibility CI. Build the corpus on one architecture, snapshot it, rebuild on another, diff. Any drift turns the build red.
  • punix doctor: one command that runs every consistency check there is.

What is never coming

Three things are ruled out on purpose, and the design depends on their absence:

  • Subtyping in the language. The type checker is provably decidable because there's no subtyping. Adding it would trade a guarantee for convenience.
  • First-class type members. Same proof, same answer.
  • Import-from-derivation: running a build in order to find out what the next build needs. That's an effect, and effects live below the eval/realise line. The equivalent capability exists as a deploy-time fetch instead.

β†’ Decisions for the frozen constitutional layer.