star × nix

planned — this whole page is design, tracked in FUTURE-NIX.md. Nothing here is implemented yet, and the decisions below are proposals to validate, not settled fact.

star’s star.toml deliberately overlaps in purpose with nix flake templates outputs. The plan is to meet in the middle, in three layers of increasing ambition. The invariant across all of them: ordinary star.toml templates and ordinary flake templates are always supported on their own. star.nix is additive; it never becomes required.

Layer 1 — discovering flake templates

A repository whose root exposes a flake templates output becomes a template source, no changes required on the repository side. For each templates.<name> attrset, star registers the name (qualified namespace#name, exactly like star.toml templates) and the description. Author and init-message have no flake-native equivalent, so defaults apply.

These flow into the same discovery registry star.toml produces, so resolution, disambiguation, and materialization are unchanged. A directory with both a star.toml and a flake templates output keeps one identity: the star.toml wins — it is strictly more expressive. templates.<name>.path is treated as advisory; content is the declaring directory, consistent with star.toml semantics.

Layer 2 — star.nix

Repositories that want full metadata declare templates in a star.nix at the repo root:

# star.nix — evaluated by star (and importable by the repo's flake)
{ lib, ... }:
{
  templates = {
    rust-cli = {
      # star.toml-equivalent metadata, fully typed
      directory = "templates/rust-cli";   # template root, relative to repo
      author = "jingus";
      description = "clap + color-eyre + tracing + tokio, built by crane";
      init-message = ''
        Hope you enjoy my template! Use `nix develop` to enter the dev shell!
        God bless!
      '';
      ignore = [ "target" "*.log" ];
    };
  };
}

Design decisions that matter:

  • star.nix is evaluated with a fixed, minimal eval context — nix eval --json on an expression star controls, never coupled to the repo’s flake inputs. The file must be evaluable with only lib in scope.

  • A flake input exposes the module system for declaring inside your flake instead:

    inputs.star.url = "git+https://git.jingus.dev/star/star";
    
    # in outputs:
    templates = inputs.star.lib.mkStarTemplates ./star.nix;

    mkStarTemplates converts the star declaration into a conventional flake templates output, so the same repository remains 100% usable by nix flake init -t. The round-trip is the core promise: star metadata is the source of truth, the flake output is derived, and plain nix users lose nothing.

  • Mixed repos are fine during migration: when both exist, star.nix wins for directories it claims; star.toml in unclaimed directories still works.

  • Schema versioning gates incompatible changes the same way [star] version does.

Layer 3 — what else the module can expose

  • checks — per-template validation as flake checks: manifest parses, directory exists, ignore globs compile, no star.toml inside a claimed directory.
  • devShells.template-author — a shell with star itself plus git fixtures for testing templates end-to-end.
  • Generated index/docs — render the repo’s template catalog into a README section, so the catalog is never stale.
  • Passthru metadata — future star features (post-init hooks, prompts, variable substitution) get a declaration point here first.

Open questions

  • Eval trust & cost — nix eval executes arbitrary nix from the cloned repo. Timeouts, --option eval-cache, and opt-in vs graceful-failure defaults need deciding.
  • Where flake templates live — root templates output only, or subdirectory flakes too? (Start root-only.)
  • URL-qualified flake refs — should star use github:owner/repo#t ever parse? The # grammar must not grow ambiguities.
  • Lockfiles — flake-sourced templates probably should materialize their flake.lock (it is part of the committed tree); the ignore pipeline can exclude it per-template if authors disagree.