- Rust 44.3%
- Nix 28.5%
- TypeScript 25.3%
- Python 1.4%
- HTML 0.5%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| .forgejo/workflows | ||
| nix-modules | ||
| templates | ||
| .envrc | ||
| .gitignore | ||
| AGENTS.md | ||
| flake.lock | ||
| flake.nix | ||
| README.md | ||
starters
Reusable project starters. Every starter is a self-contained nix flake
you can instantiate with nix flake init, built to the standards below.
Repos created from a starter get a hermetic build, a devshell, locked
inputs, static-analysis gates, and forgejo CI out of the box.
Using a template
nix flake show git+https://git.jingus.dev/jingus/starters # list templates
nix flake init -t git+https://git.jingus.dev/jingus/starters#<name>
# or from a local checkout:
nix flake init -t path:/path/to/starters#<name>
Starters
python-uv-cli
A type-safe Python 3 CLI starter: uv for dependencies and virtualenvs,
typer for the CLI, ruff for lint + format, strict mypy for types,
and a nix flake for the build system, devshell, and CI. Ships locked
inputs on both sides (flake.lock + uv.lock), ruff/mypy/smoke checks
as nix derivations, an attic-cached forgejo workflow, an optional direnv
.envrc, and a validate self-test app.
nix flake init -t git+https://git.jingus.dev/jingus/starters#python-uv-cli
See templates/python-uv-cli for the full picture.
expo-axum
A mobile app + API starter: an Expo (React Native) TypeScript app with a vite-powered web target (oxlint/oxfmt/vitest for tooling), a Rust Axum backend on Postgres via SeaORM (Argon2id + hashed-session bearer auth behind a custom extractor), crane nix builds with dependency caching, a deterministic docker image, deploy-rs stubs for NixOS, a one-command dev Postgres, and forgejo CI with the two canonical gates plus identity-tagged image publishing.
nix flake init -t git+https://git.jingus.dev/jingus/starters#expo-axum
See templates/expo-axum for the full picture.
rust-cli
A Rust CLI starter: clap (derive API) for argument parsing,
color-eyre for error reports, tracing for structured logs, and
tokio for async execution. Ships a crane nix build where
dependencies compile once and source changes never recompile them, an
optimized release profile (thin LTO, stripped), clippy/fmt/test/smoke
checks as cached derivations, locked inputs on both sides
(flake.lock + Cargo.lock), an attic-cached forgejo workflow, and a
validate self-test app.
nix flake init -t git+https://git.jingus.dev/jingus/starters#rust-cli
See templates/rust-cli for the full picture.
Standards
These are the rules a template must follow to live here. The root flake's checks and CI enforce most of them mechanically; reviewers (and agents) should enforce the rest.
Nix first, flakes always
- Every template is a nix flake, and the flake is the build system:
packages.<name>builds the project hermetically (no network, no host toolchains beyond nix itself). - Flake structure follows the dendritic pattern with flake-parts and
import-tree: a minimal
flake.nix(inputs only) whose outputs aremkFlake ... (import-tree ./nix-modules). nix-modules/parts.nixdeclares the supportedsystems.- One file per concern inside
nix-modules/:shell.nix(devshell +perSystem.formatter),checks.nix(validation derivations),apps.nix(thevalidateapp), andpackages/<name>.nixper package. - Locked inputs are committed:
flake.lockalways, plus the language lockfile (uv.lock,Cargo.lock, ...). Locks are updated in this repo by CI-red-drivennix flake update/ lock-refresh commits — never deleted. - Build inputs are narrowed with
lib.filesetso unrelated files (lockfiles, CI, nix code) cannot invalidate build caches.
Reproducibility
- A fresh clone must reach a working state with nothing but nix:
nix developfor hacking,nix build/nix runfor artifacts. - Nothing may depend on tools installed outside nix. For one-off tasks,
prefer ephemeral shells —
nix shell nixpkgs#<tool> -c <cmd>— over any installation. - Python templates pin the interpreter through the devshell (e.g.
UV_PYTHON) so uv's virtualenv and the nix build share one version.
Type-safety and static analysis
- Templates are fully annotated and check with the ecosystem's strictest
practical checker in
checks.*derivations (mypy--strictfor Python,clippy/denyfor Rust, ...). Typed libraries ship their marker (py.typedetc.). - Formatting and linting (nixfmt for nix, ruff for Python, ...) are
checks.*derivations too — they run innix flake checkand get cached like any other build.
Devshells
devShells.defaultprovides every tool needed to develop the project: language toolchain, formatters, linters, editor support (e.g.nil).- The devshell is the only supported way to get tooling. Never document (or perform) global installs.
- Templates ship an
.envrccontaininguse flakeso direnv users get the devshell automatically oncd. direnv is optional —nix developis the always-available path — and.direnv/stays gitignored.
Self-validation contract
- Every template exposes
apps.<system>.validate, runnable from the project root (nix run .#validate). It must verify, at minimum: lockfile freshness, a clean dependency sync from the lockfile, and a smoke test of the built entry point. nix flake checkmust pass on a freshly initialized copy.
CI
- Every template ships
.forgejo/workflows/ci.ymltargetingruns-on: nix, using jingus/actionsattic-cachefor binary caching and running the two canonical gates:nix flake checknix run .#validate
- The root workflow (this repo) additionally instantiates every
template the way a user would (
nix flake init -t path:...#<name>) and runs both gates on the initialized copy, so a template can only be merged if it actually works as a starting point.
Bookkeeping
- Register new templates in
nix-modules/templates.nixwith a description. Thetemplatescheck fails ifflake.templatesand thetemplates/directory contents ever drift apart. - Every template has a
README.mdexplaining instantiation, the dev loop, verification, and any dual-source trade-offs (e.g. uv.lock vs nixpkgs dependencies).
Adding a new template
mkdir templates/<name>and scaffold it to the standards above (copying an existing template is encouraged).- Register it in
nix-modules/templates.nix. - Generate and commit both lockfiles (
nix flake lock, language lock). - Verify locally:
(Runnix flake check # root cp -r templates/<name> /tmp/<name>-check cd /tmp/<name>-check && nix flake check && nix run .#validate nix flake init -t path:/path/to/starters#<name> # in an empty dirvalidateon the copy — it creates a.venvin its working directory, which must not pollute the template itself.) - Commit; root CI re-validates everything.
Repository layout
flake.nix # root flake: inputs only
nix-modules/
parts.nix # supported systems
templates.nix # flake.templates registry (checked)
checks.nix # wellformedness + nixfmt checks
shell.nix # devshell + formatter
templates/
python-uv-cli/ # the templates themselves (flakes)
expo-axum/
rust-cli/
.envrc # direnv → root devshell (optional)
.forgejo/workflows/ci.yml # root validation CI
AGENTS.md # rules for agents working in this repo
Agents (human or otherwise) working on this repository must read AGENTS.md first — the short version: never modify global state; use devshells and ephemeral nix shells for everything.