since 2024 · formerly nix-config · the site · history · releases · cite · whitepaper
My Nix labs: self-contained flakes for the tools I actually work in, on an
Ubuntu workstation (i9 + RTX 4090) running Determinate Nix.
Each lab is one directory with its own flake.nix, flake.lock, justfile and README, references
nothing outside itself, and works unchanged as a git submodule of the repositories that consume it.
Built on Gabriel Volpe's nix-config, and
his repository is better anyway: read it first.
The workstation's NixOS system (nixosConfigurations.nixos, nixos-26.05 + Home Manager) in a QEMU
VM, from any Linux with Nix. Log in as h0ffmann / nixos. just vm first prints the pins from
flake.lock and the headline versions (linux, nix, claude-code, gh, graphify); the VM's login
message repeats them.
just vm # build and boot it; Claude Code and gh already logged in, ~/repos cloned
just vm-reset # delete the VM's disk (nixos.qcow2) and start fresh next time
just vm-uninstall # undo `just vm`: the disk, `result` and every store path only the VM uses
just freeze vm # every package the VM installs, name==version (pip freeze); `just freeze` for the host
just skills # the vendored agent skills, and which to ask for to improve this repo
just graphify # code graph of this repo (build | query "<q>" | path | explain), keylessWithout a checkout:
nix run nixpkgs#nixos-rebuild -- build-vm --flake github:h0ffmann/picasso#nixos
./result/bin/run-nixos-vmWhat just vm shares from the host:
| In the VM | From the host | |
|---|---|---|
~/nix-config |
this checkout | edit on either side |
~/.claude |
~/.claude |
Claude Code logged in |
GH_TOKEN |
gh auth token |
through qemu's fw_cfg, never a file |
~/repos |
~/repos (or $VM_REPOS) |
on boot, clones what is missing: marola and its submodules, marola-devkit, ww3-gpu and its submodules, the private h0ffmann/nagori, and Karpathy's main repos (nanochat, nanoGPT, llm.c, llama2.c, minbpe, micrograd); never re-clones, survives just vm-reset |
The VM gets modesetting instead of the NVIDIA driver, no DaVinci, 8 GB / 4 cores / 20 GB; change
that in virtualisation.vmVariant in configuration.nix. The first just vm downloads about
4.6 GiB and builds the unfree packages the binary cache does not carry (vault and pritunl-client
from Go source; steam, zoom, vscode and obsidian repackaged); later runs reuse the store. .mcp.json gives Claude Code the
mcp-nixos server for package and option search.
Ubuntu users
Everything here runs on Ubuntu with Nix installed; the VM is optional, and every lab works on the host directly. Written for Ubuntu 24.04 with Determinate Nix, the setup this repo is used on; the AppArmor profile in step 4 is not exercised by CI.
-
Nix, with flakes on. Determinate Nix turns flakes on and keeps its own settings out of
/etc/nix/nix.conf(custom ones go in/etc/nix/nix.custom.conf):curl -fsSL https://install.determinate.systems/nix | sh -s -- installIf any
nixcommand then warnsunable to download 'https://cuda-maintainers.cachix.org/...': HTTP error 401, an oldcachix use cuda-maintainersleft that dead cache in your config. This lists each file that names it and the command that fixes that file:nix run 'github:h0ffmann/picasso?dir=labs/cuda#setup-cuda-cache' -- --doctor -
justandgh.sudo apt install justis enough: every justfile here runs with the just 1.21 of Ubuntu 24.04, and CI checks it. Nix gives you newer versions:nix profile add nixpkgs#just nixpkgs#gh # `nix profile install` on an older Nix gh auth login # once; `just vm` hands this token to the VM
-
KVM for
just vm. Without write access to/dev/kvmQEMU falls back to software emulation and the VM is unusably slow.just vmstops and says so; the fix is:sudo usermod -aG kvm "$USER" # then log out and back in -
ai-jail and AppArmor. Ubuntu 23.10 and later block unprivileged user namespaces, so bubblewrap from the Nix store fails with
bwrap: setting up uid map: Permission denied(thelabs/agenticsandbox,just jcoinlabs/pratico, marola'sjust jail-claude). Ubuntu's ownbwrap-userns-restrictprofile covers only/usr/bin/bwrap; this one grantsusernsto the Nix store's bwrap and nothing else:sudo tee /etc/apparmor.d/nix-bwrap >/dev/null <<'EOF' abi <abi/4.0>, include <tunables/global> profile nix-bwrap /nix/store/*/bin/bwrap flags=(unconfined) { userns, } EOF sudo apparmor_parser -r /etc/apparmor.d/nix-bwrap
-
marola on the host.
git clone --recurse-submodules https://github.com/marola-dev/marola, thennix developin it; Docker comes from Ubuntu (sudo apt install docker.ioandsudo usermod -aG docker "$USER"), not from Nix. In the VM,~/repos/marolais already cloned and Docker is already on.
The WAVEWATCH III toolchain that ww-lab builds in, plus a local
zsh pilot and a sandbox for coding agents. See labs/pratico.
cd labs/pratico
just ww3 # toolchain-only shell — gfortran, OpenMPI, NetCDF, METIS, ecCodes, python
just toolchain # exact pinned versions, for a paper's methods section
just smoke # Fortran 2008 + MPI + NetCDF-4 compiled and run in the sandbox (CI)
just kokkos-smoke # Kokkos + GoogleTest compiled, tested and run in the sandbox (CI)
just cuda # toolchain shell with Kokkos built for CUDA — x86_64-linux, needs a GPU
just dev # interactive shell: same toolchain + zsh-ai (Ctrl+O) → llm → Ollama
just jco # Claude Code inside ai-jail (jcf / jcs for other models, jo for OpenCode)Environment details
| Type | Program |
|---|---|
| Fortran / C / C++ | GCC 15 (gfortran) |
| MPI | OpenMPI 5 |
| NetCDF | netcdf-c 4.10 + netcdf-fortran, HDF5 |
| Decomposition | METIS / ParMETIS, PT-Scotch (static, for WW3's PDLIB) |
| WW3 regtest data | NOAA's ww3_from_ftp tarball, SHA-256 pinned (ww3-regtest-data) |
| GRIB | ecCodes |
| C++ portability | Kokkos (Serial + OpenMP, CUDA in just cuda) + GoogleTest, gdb, valgrind |
| Build | CMake / Ninja |
| Analysis | nco, cdo, python (numpy, scipy, xarray, netCDF4, matplotlib) |
| Shell pilot | zsh-ai → llm → Ollama (qwen2.5-coder:7b) |
| Agent sandbox | ai-jail (bubblewrap / Landlock / seccomp) |
| Task runner | just |
Markdown → LaTeX → PDF, reproducibly, and mkPdf / mkDocx helpers so other repositories build
their documents in the Nix sandbox with this toolchain. docx is optional and takes the pandoc
writer, no TeX. First consumer: ww-lab's course book and its UFRJ/DEL project proposal.
See labs/publisher.
cd labs/publisher
just shell # pandoc, xelatex / pdflatex, python + openai on PATH
just versions # what is pinned
just smoke # sample document through both engines, built in the sandbox (CI)
just smoke-docx # the same sample as .docx, the optional non-TeX output (CI)Environment details
| Type | Program |
|---|---|
| Converter | pandoc 3.7 (citeproc, Lua filters) |
| TeX | TeX Live 2025 texliveMedium + babel-portuges, fontspec, DejaVu, fvextra, titlesec, … |
| Engines | xelatex (books, unicode fonts by filename) and pdflatex (T1 templates) |
| Scripting | python 3 + openai (translation against any OpenAI-compatible endpoint) |
| PDF tools | poppler-utils (pdfinfo, pdftotext, pdftoppm), librsvg (rsvg-convert, SVG images in PDFs) |
| Badges & emoji | filters/shields-badges.lua → publisher-badges.sty (shields.io badges as TikZ pills), Noto Color Emoji via luaotfload fallback (lualatex) |
| Japanese | luatexja + Harano Aji Gothic/Mincho (lualatex) |
| Reuse | lib.<system>.{mkPdf, mkDocx, mkDocument} and the composite GitHub Action h0ffmann/picasso/labs/publisher@main |
| docx (optional) | pandoc's docx writer — no TeX, no LaTeX template, styling from a consumer's --reference-doc |
The gate a repository runs before a PR — hadolint, actionlint + shellcheck, ruff, pyflakes,
cloc, coverage.py, pdoc — exported as one list for a consumer's mkShell. See
labs/lint.
cd labs/lint
just shell # the tools on PATH
just versions # what is pinned, built in the sandbox (CI)Environment details
| Type | Program |
|---|---|
| Dockerfile | hadolint |
| Workflows | actionlint + shellcheck for the run: blocks |
| Python | ruff, pyflakes, coverage.py, pdoc |
| Counting | cloc |
| Reuse | lib.<system>.tools (the list), packages.<system>.<tool>, checks.<system>.versions |
Markdown papers to PDF and to arXiv source: pandoc with pandoc-crossref and citeproc, XeLaTeX for
the PDF and pdfLaTeX for the arXiv check, with python, just, git and gh. pandoc-crossref is built
against the same pandoc, so the two always agree. First consumer:
closed-source (Paper Fabric). See
labs/paper.
cd labs/paper
just shell # the tools on PATH
just versions # what is pinned, built in the sandbox (CI)
just smoke # a sample paper through XeLaTeX and pdfLaTeX, built in the sandbox (CI)Environment details
| Type | Program |
|---|---|
| Converter | pandoc 3.7 (citeproc) + pandoc-crossref 0.3.21 (section, table and figure numbers) |
| TeX | TeX Live 2025 texliveMedium + fontspec, unicode-math, Latin Modern, csquotes, fancyhdr, titling, xurl, … |
| Engines | xelatex (the PDF) and pdflatex (the arXiv source check) |
| Scripting | python 3, just, git, gh (releases) |
| PDF tools | poppler-utils (pdfinfo, pdftotext), librsvg (rsvg-convert, SVG images in PDFs) |
| Reuse | lib.<system>.{tools, tex}, packages.<system>.{pandoc-crossref, tex}, checks.<system>.{smoke, versions} |
Static sites in Gleam: the toolchain of picasso's own site (Gleam on the JavaScript target for
Lustre, node, esbuild, just, python), as a lab other repositories borrow. First consumer:
closed-source (the Paper Fabric site). See
labs/gleam-website.
cd labs/gleam-website
just shell # the tools on PATH
just versions # what is pinned, built in the sandbox (CI)
just smoke # a Gleam module to JavaScript, run on node and bundled, in the sandbox (CI)Environment details
| Type | Program |
|---|---|
| Language | Gleam 1.19 on the JavaScript target; sites use Lustre from Hex |
| Runtime | Node.js 24 (gleam test, gleam run for the prerender) |
| Bundler | esbuild |
| Scripting | just, python 3 |
| Reuse | lib.<system>.tools, packages.<system>.{gleam, esbuild}, checks.<system>.{smoke, versions} |
The sandbox for coding agents and the host-side scripts around it: ai-jail (bubblewrap /
Landlock / seccomp), OpenCode, Open Code Review (ocr, built from source), gh, and
jail-run / gh-token / clip / clip-relay — one file each, with a --self-test that
nix flake check runs in the sandbox. See labs/agentic.
cd labs/agentic
just self-test # every script's --self-test, from source
just jco # Claude Code inside ai-jail, in YOUR directory (jcf / jcs for other models, jo for OpenCode)
just jocr # Open Code Review inside ai-jail: project read-only, no GitHub token
just jail-dry-run ls # what ai-jail would runEnvironment details
| Type | Program |
|---|---|
| Agent sandbox | ai-jail (bubblewrap / Landlock / seccomp, Linux) |
| Agents | Claude Code (the consumer's own), OpenCode |
| Reviewer | Open Code Review v1.12.7, buildGoModule from the tag — jail-run ocr gives it no token and a read-only project |
| GitHub | gh; gh-token resolves the token on the host, jail-run forwards it as GH_TOKEN |
| Clipboard | clip (write-only, from inside the jail) → clip-relay → wl-copy / xclip |
| Reuse | lib.<system>.{tools, env, scripts}, packages.<system>.{<script>, ocr}, checks.<system>.{gh-token, jail-run} |
Host setup for a CUDA box, x86_64-linux only: setup-cuda-cache puts the nixos-cuda binary
cache into Determinate Nix's nix.custom.conf (replacing the dead Cachix block that 401s), and
REQUIREMENTS=<file> setup-ml-venv builds a torch venv from PyTorch's wheel index with the nix
libstdc++ and the NVIDIA driver libraries on its path. See labs/cuda.
cd labs/cuda
just self-test # both --self-tests, from source
just cache-setup # the nix.custom.conf block, dry-run; `sudo just cache-setup apply` writes it
just venv path/to/requirements.txt # the venv; call bin/python-cuda afterwardsEnvironment details
| Type | Program |
|---|---|
| Binary cache | cache.nixos-cuda.org via extra-substituters + trusted-users |
| torch | PyTorch's CUDA wheel index (CUDA_INDEX, default cu129), never nixpkgs' torchWithCuda |
| Wrapper | $VENV_ROOT/bin/python-cuda — nix libstdc++ baked in at build, driver libs linked at run |
| Reuse | lib.x86_64-linux.tools, packages.x86_64-linux.{setup-cuda-cache,setup-ml-venv}, checks.x86_64-linux.* |
The desk during a talk or a demo: draw over the screen, screenshot or record it, keep the
webcam looking the same as last time, present from a PDF or a markdown file. Recipes pick
Wayland or X11 by $WAYLAND_DISPLAY; cam saves and restores V4L2 controls, the one thing
nothing packaged does. macOS gets the terminal half only. See labs/present.
cd labs/present
just annotate # gromit-mpx: F9 draw, Shift+F9 hide, F10 clear
just shot / just rec [region] # grim+slurp+satty or flameshot; wf-recorder or ffmpeg x11grab
just cam save meeting # webcam controls → ~/.config/present/cam/meeting.conf; `cam load` after a replug
just pdf deck.pdf / just md deck.md # pdfpc with notes and timer / presenterm in the terminalEnvironment details
| Type | Program |
|---|---|
| Annotate | gromit-mpx, find-cursor, screenkey / wshowkeys |
| Capture | flameshot, grim + slurp + satty |
| Record | wf-recorder, kooha, OBS Studio, ffmpeg + gifski |
| Webcam | v4l-utils + cam (save / load / reset profiles), cameractrls, guvcview |
| Present | pdfpc, presenterm |
| Reuse | lib.<system>.{tools, scripts}, packages.<system>.{cam, <tool>}, checks.<system>.{cam, versions} |
The code graph of a repository with graphify and nothing else: code-only, keyless and offline,
the way marola-devkit's just graph runs it. viz build writes graph.json and graph.html, an
interactive picture of the code, under ~/.cache/marola-graph/<repo>. First consumer:
closed-source (just viz). See labs/viz.
cd labs/viz
just graph build # graph the directory you are in; prints the path of graph.html
just graph update # the same graph, with the Markdown pages, headings and links
just graph query "<q>" # answer from the graph, capped at --budget 400
just smoke # example/ graphed and queried in the sandbox (CI)Environment details
| Type | Program |
|---|---|
| Code graph | graphify 0.9 (tree-sitter extraction, clustering, graph.html), with PYTHONHASHSEED pinned |
| Script | viz (build / query / where / any graphify command), with a --self-test against a fake graphify |
| Reuse | lib.<system>.tools, packages.<system>.{viz, graphify, smoke}, checks.<system>.{viz, smoke} |
.
├── labs/
│ ├── pratico/ WW3 toolchain, zsh-ai pilot, ai-jail (flake, lock, justfile, README, scripts/)
│ ├── publisher/ pandoc + TeX Live, mkPdf, action.yml (flake, lock, justfile, README, example/)
│ ├── lint/ lint toolchain as one list, checks.versions (flake, lock, justfile, README)
│ ├── paper/ pandoc + crossref, XeLaTeX / pdfLaTeX for papers (flake, lock, justfile, README, example/)
│ ├── gleam-website/ Gleam (JavaScript) + node + esbuild for sites (flake, lock, justfile, README, example/)
│ ├── agentic/ ai-jail, OpenCode, ocr, gh, jail-run/gh-token/clip (flake, lock, justfile, README, scripts/)
│ ├── cuda/ CUDA binary cache + torch venv, x86_64 only (flake, lock, justfile, README, scripts/)
│ ├── present/ annotate, capture, record, webcam `cam`, presenters (flake, lock, justfile, README, scripts/)
│ └── viz/ graphify: the code graph of a repo, offline (flake, lock, justfile, README, scripts/, example/)
├── site/ the site, Gleam + Lustre: own flake, `just ci`, deployed to GitHub Pages
├── scripts/ release.sh (`just release`), citation.py (`just citation`),
│ social.py (Threads posts, `just social`; docs/adr/0001)
├── WHITEPAPER.md the determinism whitepaper in ASD-STE100 (and WHITEPAPER.pdf);
│ PICASSOPAPER.pdf is the original, coloured paper
├── docs/ media/ (logo), whitepapers/ and studies/ (the sources of the PDFs under Papers),
│ social/ (social media posts, one directory each, with sources),
│ adr/ (architecture decision records),
│ readiness/ (dated assessments against the ouroboric-dev checklist),
│ rea/ (dated REA reports of the ouroboric-sharingan skill)
├── HUMAN_KNOWLEDGE.md what the human plans and tracks (SOCIAL PLANNING: networks, cadence)
├── notes/ things worth writing down once (legacy NixOS root, …)
├── .github/workflows/ ci.yml: root evaluates, every lab is built and linted
└── flake.nix, configuration.nix, home.nix, … legacy NixOS system configuration (see notes/)
Every lab also carries a lab.json — a one-line summary and the nixpkgs attributes that headline
it — which the profile README at github.com/h0ffmann renders daily
next to the lab's lock date. Each lab's checks.lab-json asserts the card's shape when it
evaluates and resolves every listed attribute in the lab's pinned nixpkgs.
Expand to see available outputs
$ nix flake show github:h0ffmann/picasso?dir=labs/pratico
├───checks
│ └───x86_64-linux
│ ├───kokkos-smoke: CI test # Kokkos + GoogleTest smoke test
│ ├───lab-json: CI test # lab.json's shape and headline versions, pure Nix
│ ├───ptscotch: CI test # PT-Scotch has every file WW3's FindSCOTCH reads
│ └───toolchain: CI test # Fortran + MPI + NetCDF-4 smoke test
├───devShells
│ └───x86_64-linux
│ ├───cuda: development environment # toolchain + Kokkos for CUDA
│ ├───default: development environment # = pratico
│ ├───pratico: development environment # toolchain + zsh-ai + ai-jail
│ └───ww3: development environment # toolchain only
└───packages
└───x86_64-linux
├───ptscotch-static: package # SCOTCH_PATH: PT-Scotch, static, MPI
├───ww3-metis: package # METIS_PATH: METIS + ParMETIS
└───ww3-regtest-data: package # WW3's regtest data, SHA-256 pinned
$ nix flake show github:h0ffmann/picasso?dir=labs/publisher
├───checks
│ └───x86_64-linux
│ ├───docx: CI test # the sample as .docx, asserted to be a real Word document
│ ├───filter: CI test # the shields filter's exact \badge macros, listed in flake.nix
│ ├───lab-json: CI test
│ └───smoke: CI test
├───devShells
│ └───x86_64-linux
│ └───default: development environment
├───lib # tex, python, tools, mkPdf, mkDocx, mkDocument, env per system
└───packages
└───x86_64-linux
├───smoke: package
├───smoke-docx: package
└───tex: package # the TeX Live environment
$ nix flake show github:h0ffmann/picasso?dir=labs/lint
├───checks
│ └───x86_64-linux
│ ├───lab-json: CI test
│ └───versions: CI test # every tool's --version, built in the sandbox
├───devShells
│ └───x86_64-linux
│ └───default: development environment
├───lib # tools (the list) per system
└───packages
└───x86_64-linux
├───versions: package
└───hadolint, actionlint, shellcheck, ruff, pyflakes, cloc, coverage, pdoc: package
$ nix flake show github:h0ffmann/picasso?dir=labs/paper
├───checks
│ └───x86_64-linux
│ ├───lab-json: CI test
│ ├───smoke: CI test # example/paper.md through both engines, references resolved
│ └───versions: CI test # every tool's version, built in the sandbox
├───devShells
│ └───x86_64-linux
│ └───default: development environment
├───lib # tools (the list) and tex per system
└───packages
└───x86_64-linux
├───pandoc-crossref: package
├───smoke: package
├───tex: package # the TeX Live environment
└───versions: package
$ nix flake show github:h0ffmann/picasso?dir=labs/gleam-website
├───checks
│ └───x86_64-linux
│ ├───lab-json: CI test
│ ├───smoke: CI test # example/: gleam -> JavaScript -> node -> esbuild
│ └───versions: CI test # every tool's version, built in the sandbox
├───devShells
│ └───x86_64-linux
│ └───default: development environment
├───lib # tools (the list) per system
└───packages
└───x86_64-linux
├───esbuild: package
├───gleam: package
├───smoke: package
└───versions: package
$ nix flake show github:h0ffmann/picasso?dir=labs/agentic
├───checks
│ └───x86_64-linux
│ ├───gh-token: CI test # the script's --self-test, in the sandbox
│ ├───jail-run: CI test
│ └───lab-json: CI test
├───devShells
│ └───x86_64-linux
│ └───default: development environment
├───lib # tools, env (BWRAP_BIN), scripts per system
└───packages
└───x86_64-linux
├───ocr: package # Open Code Review, from the tagged Go source
└───gh-token, clip, clip-relay, jail-run: package
$ nix flake show github:h0ffmann/picasso?dir=labs/cuda
├───checks
│ └───x86_64-linux
│ ├───lab-json: CI test
│ ├───setup-cuda-cache: CI test # the scripts' --self-test, in the sandbox
│ └───setup-ml-venv: CI test
├───devShells
│ └───x86_64-linux
│ └───default: development environment
├───lib # tools per system (x86_64-linux only)
└───packages
└───x86_64-linux
└───setup-cuda-cache, setup-ml-venv: package
$ nix flake show github:h0ffmann/picasso?dir=labs/present
├───checks
│ └───x86_64-linux
│ ├───cam: CI test # the script's --self-test against a fake v4l2-ctl
│ ├───lab-json: CI test
│ └───versions: CI test # CLI tools answer --version; no GUI launched
├───devShells
│ └───x86_64-linux
│ └───default: development environment
├───lib # tools (desktop half Linux only), scripts.cam per system
└───packages
└───x86_64-linux
├───cam, versions: package
└───gromit-mpx, flameshot, grim, slurp, satty, wf-recorder, kooha, obs-studio, v4l-utils, cameractrls, guvcview, pdfpc, presenterm, ffmpeg, gifski, …: package
$ nix flake show github:h0ffmann/picasso?dir=labs/viz
├───checks
│ └───x86_64-linux
│ ├───lab-json: CI test
│ ├───smoke: CI test # example/ graphed and queried in the sandbox
│ └───viz: CI test # the script's --self-test against a fake graphify
├───devShells
│ └───x86_64-linux
│ └───default: development environment
├───lib # tools (the list) per system
└───packages
└───x86_64-linux
├───default, viz: package
├───graphify: package
└───smoke: package
$ nix flake show github:h0ffmann/picasso # legacy root
├───checks.x86_64-linux.build # NixOS toplevel evaluates
├───devShells.x86_64-linux.default
├───formatter.x86_64-linux
└───nixosConfigurations.nixosaarch64-linux and aarch64-darwin are declared for the labs too (--all-systems).
Using a lab from another repository
Three ways, all used by ww-lab:
# 1. as a flake input (publisher exports lib.<system>.mkPdf; see labs/publisher/README.md)
inputs.publisher.url = "github:h0ffmann/picasso?dir=labs/publisher";
# 2. as a sparse git submodule, only the lab checked out (labs/pratico/scripts/ww-lab-submodule.sh)
nix develop ./nix-config/labs/pratico#ww3
# 3. in GitHub Actions (build, upload and commit PDFs in one step)
- uses: h0ffmann/picasso/labs/publisher@mainUsed by: ww-lab — its
flake.nix consumes labs/publisher
through mkPdf, .github/workflows/pubs.yml
is a complete caller of the action (tests and translation as pre-build, PDFs committed back to
main), and labs/pratico is its sparse submodule.
marola consumes labs/lint as a flake input with
nixpkgs.follows, appending lint.lib.${system}.tools, agentic.lib.${system}.tools and, on x86_64-linux,
cuda.lib.${system}.tools to its own dev shell.
Every PDF in this repository. Each source is next to its PDF, or the table says that there is no source.
| Document | Source | What it is | |
|---|---|---|---|
| Same inputs, same factory (2026) | PICASSOPAPER.pdf | docs/whitepapers/determinism-2026/ (whitepaper.html, node render.mjs) |
The determinism whitepaper, coloured original: why reproducibility matters in 2026 and what the labs can do for ouroboric-dev. |
| Same inputs, same factory, ASD-STE100 text | WHITEPAPER.pdf | WHITEPAPER.md (node docs/whitepapers/determinism-2026/render-ste.mjs) |
The same paper in Simplified Technical English, plain look. |
| picasso and Determinate Nix (2026) | study.pdf | docs/studies/determinate-2026/ (study.html, node render.mjs) |
A side-by-side study of Determinate Systems and picasso, a 6-month plan, and distribution through marola and ouroboric-dev (#96). |
| Site hosting plan | hosting-plan.pdf | none in the repository | The plan for hosting the site (#85); the site links it from its header. |
When you add a PDF, add its row here in the same PR.
Started on 2024-12-30 as nix-config, renamed picasso on 2026-10-12. Every change, era by era,
is on the site (source) (generated by site/history.nix, just history in site/, into site/static/history.json).
| Era | When | PRs | What happened |
|---|---|---|---|
| sic mundus | 2024-12 → 2025-02 | #1–#7 | A NixOS desktop from scratch: Home Manager, DaVinci Resolve, Obsidian, a Python/uv dev shell; the PRs are named after songs. |
| the workstation | 2025-04 → 2025-06 | #8–#29 | Cachix, flake dev shells, a compilation profile, repomix for code quality, aider, Ollama and Bazel, Steam, Haskell, Helix, Python 3.13. |
| the labs | 2026-09 | #30–#67 | The system becomes self-contained labs other repos borrow: pratico, publisher and its Action, lint, agentic, cuda, present; CI per lab, lab.json, AGENTS.md, the MIT licence. |
| back to NixOS | 2026-10 | #68–#78 | nixos-26.05 and Home Manager 26.05, a lighter system, just vm, vendored Nix skills, mcp-nixos, graphify, the cuda doctor; then the rename to picasso. |
64 commits on main, 62 merged pull requests, +21,276 / −4,385 lines.
picasso uses semantic versions, vMAJOR.MINOR.PATCH. The git tags are the only record of the
version: no file in the repository holds it. The commit subjects on main set the bump.
| Change since the last tag | Bump | Bump before 1.0.0 |
|---|---|---|
A break in a lab's exported shape (lib.<system>.tools, mkPdf's arguments, a script's flags), a ! after the type |
major | minor |
A new lab, recipe, script, page or workflow (feat) |
minor | minor |
| A fix, a pin bump or documentation only | patch | patch |
To make a release, do these steps on a clean main that is level with origin/main. The just of
Ubuntu 24.04 (apt) is sufficient. Without just, run scripts/release.sh with the same arguments.
git switch main && git pull
just release # show the unreleased commits and the bump that they need
just release minor --dry-run # do all the checks, make no tag
just release minor # tag vX.Y.Z and push the tag (or: patch, major, 0.1.0)
just releases # list the release tagsThe tag starts release.yml, which does these steps:
- It makes the GitHub release. The notes list the merged PRs by their
type/*label, andWHITEPAPER.pdfandPICASSOPAPER.pdfare attached. - Zenodo archives the release with
.zenodo.jsonand gives it a DOI. This occurs only when the repository is switched on at zenodo.org/account/settings/github before the tag is pushed. Zenodo does not archive an earlier release. v0.1.0 was released before the switch, so it has no DOI; v0.1.1 is the first release on Zenodo. - It starts
pages.ymlonmain. The site then shows the new version and its changelog, whichsite/releases.pymakes from the tags.
A Zenodo DOI is permanent. For this reason, scripts/release.sh stops if main is not clean,
if main has no new commits, or if the new version is not more than the last tag.
Each release from v0.1.1 is archived on Zenodo, which gives it a DOI. Cite
the concept DOI, 10.5281/zenodo.23311208, which always resolves to the latest
version. GitHub's Cite this repository button exports APA and BibTeX from
CITATION.cff. just citation makes that file and the block below from
.zenodo.json.
BibTeX:
@software{hoffmann_2026_picasso,
author = {Hoffmann, Matheus},
title = {{picasso: self-contained Nix flakes for scientific and agentic toolchains, and the NixOS workstation they run on}},
year = {2026},
publisher = {Zenodo},
doi = {10.5281/zenodo.23311208},
url = {https://github.com/h0ffmann/picasso}
}APA:
Hoffmann, M. (2026). picasso: self-contained Nix flakes for scientific and agentic toolchains, and the NixOS workstation they run on [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.23311208
ABNT (NBR 6023):
HOFFMANN, Matheus. picasso: self-contained Nix flakes for scientific and agentic toolchains, and the NixOS workstation they run on. [S. l.]: Zenodo, 2026. DOI 10.5281/zenodo.23311208.
GitHub-hosted runners only. ci.yml:
- NixOS flake evaluates —
nix flake check --no-buildon the root; nothing is built. - labs/ (matrix) —
nix flake checkwith the lock required current: every devShell evaluates and everychecks.*output is built; thennixpkgs-fmt --check,statix,deadnix,shellcheckon every script, justfile parse, and the lab's self-tests when it has them. Lint tools come from the lab's own locked nixpkgs. - site rules — in
site/'s own flake:nix flake check,nixpkgs-fmt, thenjust ci(gleam format --check, the unit tests, the build, andcheck.pyon the built page: both logos hotlinked, Gabriel Volpe credited, the hosting plan linked).pages.ymlruns the samejust ciand publishessite/dist. - release —
release.yml, on a pushedv*tag: the GitHub release, then a new deploy of the site (see Releases). - justfiles parse with Ubuntu's just — on
ubuntu-24.04,apt install just(1.21.0) andjust --summaryon every justfile, sojustworks on stock Ubuntu (AGENTS.md hard rule). - licences (REUSE) —
reuse lint: every file has a licence and a copyright holder, fromREUSE.tomlandLICENSES/. - workflows lint —
actionlint. - profile ping —
profile-ping.yml, a reusable workflow: after a merged PR it sends onerepository_dispatch(activity) to h0ffmann/h0ffmann, which rebuilds the profile's recent-activity list right away instead of at its daily cron. Callers are one job (profile-activity.yml) in picasso, marola, ww3-gpu and gcp-agentic-architect; each needs aPROFILE_DISPATCH_TOKENsecret (fine-grained PAT, Contents: read & write on the profile repo only).
Dependabot bumps the actions monthly.
The layout of this README, the idea of one <details> table per environment, and more than one
habit in these flakes come straight from Gabriel Volpe's nix-config.
Thank you, Gabriel. His repository is better anyway: go read it.
Luck. People say of an artist that he got lucky. The answer that is credited to Pablo Picasso is: "Inspiration does exist, but it must find you working." It is the habit these labs are made for: the work is done every day, and the tools are ready when the idea comes. The saying is credited to Picasso in print only since 2007, and there is no documentary evidence that he said it. In 1977 the Spanish poet Vicente Aleixandre, asked for advice to young poets, said "Inspiration does exist. But you have to find it." Source: Barry Popik, "Inspiration does exist, but it must find you working", The Big Apple, 27 July 2013, read on 13 October 2026.
Matheus Hoffmann (@h0ffmann), who keeps picasso, is a member of ouroboric-dev. picasso follows ouroboric-dev's licence scheme, and its labs, whitepaper and site are written with ouroboric-dev as a reader.
picasso uses the same licences as the ouroboric-dev repositories:
- Code (the flakes, the labs and their scripts,
scripts/, the site's source): AGPL-3.0-only. - Documentation and drawings (the READMEs and other Markdown,
docs/,notes/, the papers, the drawings insite/static/drawings/): CC BY 4.0.
REUSE.toml says which licence and which copyright holder apply to each file, so no
file carries a header; LICENSES/ has every licence text, and CI runs reuse lint.
Releases up to and including v0.1.1 (10.5281/zenodo.23311208) were published under MIT, and those copies stay under MIT. The AGPL-3.0 and CC BY 4.0 terms apply from the next release. What is not mine, and under which terms:
| What | Whose | Terms |
|---|---|---|
| The software each lab pins | nixpkgs and upstream projects | their own licences; nothing here vendors their source |
.claude/skills/ |
ten community skills, each credited there with its upstream commit | MIT, one Apache-2.0; licence texts in .claude/skills/LICENSES/ |
| The README layout | Gabriel Volpe | Apache-2.0 repository; the layout is borrowed, no code was copied |
| The ouroboric-dev readiness items the site quotes | ouroboric-dev/ouroboric, © 2026 Bruno Guilhermo de Barros Valério | CC BY 4.0, fetched live and shown unchanged; the Ouroboric names and logos are trademarks and are not used as this site's brand |
The readiness item ids and levels, the assessment layout and the front matter in docs/readiness/ and docs/rea/ |
ouroboric-dev/asgard@1acab20 (MIT) and ouroboric-dev/ouroboric@b355b1d (CC BY 4.0), © 2026 Bruno Guilhermo de Barros Valério | the approach is borrowed and no text was copied; the short quotations are marked and linked |
| The Marola and Ouroboric logos on the site | marola-dev and ouroboric-dev | trademarks of their projects, loaded from their own repositories to identify them, never copied here |
| The quote on slide 2 of docs/social/instagram_001/ | Fabio Akita, akitaonrails.com | one sentence, quoted with credit and a link; not under this repo's licence |
| Fonts on the site (Fjalla One, Inter, JetBrains Mono) | their designers, served by Google Fonts | SIL Open Font License |
The site's bundled JavaScript dependencies (site/manifest.toml) |
Lustre; the Gleam standard library, gleam_json, gleam_regexp and their dependencies | Lustre MIT, the rest Apache-2.0; compiled into the published app.js, not vendored here |
The drawings in site/static/drawings/ are original works made for this repository,
in homage to Pablo Picasso's one-line drawings. They are not copies of his works, which stay under
copyright until the end of 2043 in most countries (he died in 1973), so none is reproduced here.
"Picasso" names this repository only; it implies no affiliation with or endorsement by the
Picasso estate, and the name is not used for any product or service.