External Systems' artifact flake: pinned, source-built packages and source materials (charts, images, rendered manifests) for downstream flakes — primarily external-systems/clanServices — to consume via a locked flake input. This repo contains packaging only: no inventory, no clan vars, no fleet policy.
The consuming contract is defined by clanServices SPEC.md (D26 two-layer
rendering, D34 version policy, D40 Cilium lifecycle, D41 Node IPAM, §8 phase
2). Scope after the round-13 narrowing: only the distro runtimes (last 3
Kubernetes minors per distro), the IfState package overlay, the Cilium chart +
committed inflated manifest + airgap image set, the CoreDNS image (D46 uniform
clan-rendered CoreDNS), and the go-httpbin test fixture. Everything day-2
(Envoy Gateway, cert-manager, Flux, external-dns, Multus, Sealed Secrets) is
GitOps-owned (SPEC §3.3) and no longer carried here.
Outside that contract, the repo also builds the workstation and agent tooling
the fleet flakes (external-systems/home) used to build themselves: the
overlays.tools packages and the code-agent sandbox image (see below).
# Name the input org-pkgs, never pkgs (pkgs is the nixpkgs package set).
inputs.org-pkgs.url = "github:external-systems/pkgs/<immutable-rev>";- Packages —
packages.<system>.<name>for direct builds onx86_64-linuxandaarch64-linux:nix build github:external-systems/pkgs#rke2 - Overlays —
overlays.defaultcomposes the Kubernetes, IfState, tools, Odoo, VS Code extension and third-party overlays;overlays.kubernetes,overlays.ifstate,overlays.tools,overlays.odoo,overlays.python-apps,overlays.vscode-extensions(nix-community/nix-vscode-extensions) andoverlays.third-party(zen-browserandmoscripts, taken from their own flakes; with nix-vscode-extensions, this flake's only non-core inputs) are also exported separately. Applying the default overlay gives you the pinned artifacts aspkgs.rke2/pkgs.k3s_1_36/pkgs.ifstate. The builder expressions come from the consumer's nixpkgs: rke2 needs any nixpkgs shippingpkgs/applications/networking/cluster/rke2/builder.nix(all version data is pinned locally inpkgs/rke2/versions/); k3s rides the consumer'sk3s_1_35/k3s_1_36/k3s_1_37builders, so consumer nixpkgs must ship those minors at the pinned patches. Following this flake'snixpkgsinput is the supported way to keep that contract aligned. - Artifact metadata —
lib.versions.matrixexposes the full last-3-minors matrix per distro (kubernetes,k3s,rke2, commits per minor);lib.versions.kubernetes/.k3s/.rke2are latest-minor aliases.lib.cilium.manifestis the committed base manifest,lib.cilium.imagePackageNameslists its per-system tarball outputs, andlib.coredns.imageRefis the digest-pinned CoreDNS reference.
The last 3 Kubernetes minors per distro, latest patch each, with per-minor
parity enforced by checks.k8s-version-parity-<minor>:
| minor | rke2 | k3s |
|---|---|---|
| 1.35 | v1.35.9+rke2r1 (source-built, local table) | v1.35.9+k3s1 (nixpkgs) |
| 1.36 | v1.36.5+rke2r1 (source-built, local table) | v1.36.5+k3s1 (nixpkgs) |
| 1.37 | v1.37.1+rke2r1 (source-built, local table) | v1.37.1+k3s1 (nixpkgs) |
packages.rke2 / packages.k3s alias the latest minor; per-minor attrs are
rke2_1_35 / k3s_1_35 / …. Release-tag pins only. rke2 ldflag component
versions come from each tag's own scripts/version.sh.
Support-window gate (D34): lib.versions emits an eval warning (never a
failure) when Cilium's upstream support matrix does not list a pinned k8s
minor. Today: no warnings — Cilium 1.20 supports 1.33–1.37.
The Cilium chart is rendered at update time with the canonical
fleet-uniform profile and committed as source under manifests/.
Consumers read the committed file (builtins.readFile / kubenix
importYAML); nothing in this repo's outputs requires a build at consumer
eval time — zero IFD downstream. Per-cluster variation is layer 2 (kubenix
module merging in clanServices), never re-run helm values here.
| Component | Artifacts |
|---|---|
manifests/cilium/1.20.3.yaml |
Cilium 1.20.3, canonical profile (pkgs/cilium/values.nix): KPR, native routing, multi-pool IPAM, auto-direct-node-routes off, Cilium iptables rules off, BPF masquerade+tproxy, Maglev, pmtu, dual-stack, clustermesh-apiserver, Gateway/Ingress off (D1) |
The committed manifest is regenerated with nix run .#update-manifests. It
has no freshness check for now: the chart generates its Hubble and
clustermesh TLS Secrets on every render, so a fresh render never matches the
committed file. Those certs are replaced by the clanServices Kubernetes
services and are never used.
Every container image the committed Cilium manifest references is exported as
a digest-pinned docker-archive tarball (dockerTools.pullImage):
image-cilium-agent, image-cilium-envoy, image-cilium-operator,
image-cilium-clustermesh-apiserver, plus the aggregate
cilium-airgap-images directory for VM tests to preload. Each supported
system selects its matching amd64 or arm64 image and output hash.
checks.cilium-airgap-completeness enforces that the pinned set in
pkgs/cilium/versions.nix matches the manifest's image references exactly.
CoreDNS (SPEC D46): image-coredns — the digest-pinned upstream
registry.k8s.io/coredns/coredns tarball for the clan-rendered
cluster.local DNS.
Test fixture (SPEC §6): go-httpbin (source-built v2.26.0) +
image-go-httpbin.
nix run .#update-rke2 -- 1.35 # bump one rke2 minor to its latest patch:
# tag, commit, tarball hash, ldflags from the
# tag's scripts/version.sh, vendor-hash dance,
# verifying build — one atomic table rewrite
nix run .#update-manifests # re-render the committed manifest from the
# current pins; review with git diffCilium chart bumps: edit pkgs/cilium/versions.nix (new version + hash via
nix store prefetch-file --json <url>, refresh images from a fresh
render's image references), then nix run .#update-manifests. k3s bumps ride
nix flake update nixpkgs — verify the minors' patches still match upstream
latest afterwards, then run sync-locks in the same commit so
dev/flake.lock and devenv.yaml/devenv.lock follow (checks.lock-sync
enforces it).
Needs Nix and devenv. The shell is the devenv CLI (devenv.yaml imports the
org shell from ./devenv); apps, checks, the formatter, docs and slides live
in the dev flake-parts partition. Its one extra input (external-systems/
modules, for the code-agent image) is locked separately in dev/flake.lock,
so consumers of packages, overlays or lib never fetch it.
devenv allow # auto-activation (after `devenv hook`)
fmt # treefmt: alejandra, deadnix, nixf-diagnose,
# shellcheck, sizelint, statix
check # nix flake check: treefmt, lock-sync, docs and
# slides, per-minor k8s parity, cilium
# airgap completeness
docs-build # static docs to ./result-docs (nix build
# .#documentation); open index.html from disk
devenv up # live docs :8981 and slides :8982git add new files before evaluating — Nix reads the git tree. This repo is
the organization's nixpkgs authority: downstream flakes follow pkgs/nixpkgs
so routine artifact and nixpkgs updates remain independent of consumer update
lifecycles. Every fetch is a fixed-output derivation from a public URL with a
committed hash; airgap image sets cover the committed Cilium manifest.
pkgs also carries what every external-systems repo builds on:
flakeModules.treefmt, flakeModules.docs (MkDocs site + reveal.js deck),
flakeModules.vmTests (NixOS VM tests from vm-tests/, the vm-test runner),
flakeModules.default (all three, plus lock-sync), the org devenv shell (devenv/) and templates.default. A repo
imports inputs.org-pkgs.flakeModules.default and needs no dev inputs of its
own. Start a repo with
nix flake init -t github:external-systems/pkgs. The full description is in
the docs site (documentation/docs/flake-standard.md) and the nix-style
skill (external-systems/skills).
overlays.tools (also in overlays.default, and as packages.<system>.*
where the platform is supported) carries the repo-built CLIs and agent
tooling. Each source is pinned in its own pkgs/<name>/package.nix, so the
overlay adds no flake input to consumers' locks.
| Package | What |
|---|---|
artifacthub-mcp |
MCP server for Artifact Hub (Helm charts) |
kubernetes-mcp-server |
MCP server for Kubernetes and OpenShift |
longhornctl |
Longhorn CLI (release binary, x86_64) |
hcloud-ip |
Hetzner Cloud floating IP CLI (release binary, x86_64) |
tfctl, devenv, mcp-nixos and github-mcp-server come straight from
nixpkgs.
overlays.python-apps (also in overlays.default, and as
packages.<system>.* on Linux) builds Python apps with uv2nix from a uv
wrapper project vendored next to each package.nix
(pyproject.toml pins the app, uv.lock pins every dependency), against
the consumer's nixpkgs and python313. It closes over this flake's
uv2nix, pyproject-nix and pyproject-build-systems inputs, lazily.
| Package | What |
|---|---|
headroom |
headroom-ai, context compression proxy + MCP server for AI agents: upstream's all profile (torch, CPU build) |
headroom-lean |
headroom's torch-free lean profile: all minus image, ml, memory, evals and voice |
headroom-sandbox |
upstream's deprecated alias of lean, kept while modules default to it |
Upgrade: bump the pin in pkgs/headroom/pyproject.toml, run uv lock in
that directory (nix shell --inputs-from . nixpkgs#uv nixpkgs#python313),
then build all three and their passthru.tests (help, proxy).
overlays.odoo (also in overlays.default, and as packages.<system>.odoo19)
replaces nixpkgs' odoo19, which does not evaluate or build as shipped:
its pypdf2 dependency is marked insecure, and its makeWrapperArgs break
the wrapper. The fixed package uses pypdf (Odoo 19 has a backend for it)
and passes the wrapper arguments correctly. Each fix is documented in
pkgs/odoo/package.nix and drops once nixpkgs carries it.
external-systems/modules hostSpec.services.odoo runs it.
overlays.llm-agents (also in overlays.default, and as
packages.<system>.*) takes the agents from
numtide/llm-agents.nix,
updated daily upstream, built against the org nixpkgs (its
shared-nixpkgs overlay). They replace the nixpkgs attributes consumers
already read:
| Package | From llm-agents.nix |
|---|---|
claude-code |
claude-code |
claude-desktop |
claude-desktop: Anthropic's official Linux .deb in an FHS env, Cowork's OVMF firmware and virtiofsd included (QEMU comes from the host PATH) |
pi-coding-agent |
pi |
kimi-code |
kimi-code |
opencode |
opencode2 (v2 only), installed as bin/opencode |
The full set is pkgs.llm-agents.<name>. Update with
nix flake update llm-agents. The input follows the org nixpkgs, so
cache.numtide.com only hits when the nixpkgs revisions match.
overlays.vscode-extensions (also in overlays.default) adds
nix-community/nix-vscode-extensions'
sets: pkgs.open-vsx, pkgs.open-vsx-release, pkgs.vscode-marketplace
and pkgs.vscode-marketplace-release, every extension on Open VSX and the
VS Code Marketplace as <set>.<publisher>.<name> (lowercase). The
-release sets skip pre-releases. Upstream re-indexes daily;
nix flake update nix-vscode-extensions moves every extension to its
latest version. The input is lazy: a consumer that never reads these
attributes never fetches it. external-systems/modules
homeModules.vscodium takes its extensions from here.
The OpenShell code-agent sandbox image (agent CLIs, nix, the skills tree from
external-systems/skills, home-manager-generated dotfiles) lives in the dev
partition (images/code-agent/), so its inputs (external-systems/modules,
which brings home-manager and skills) are locked only in dev/flake.lock.
nix build .#code-agent-image # streamed docker-archive script
nix run .#load-code-agent-image # docker load
nix run .#push-code-agent-image -- docker://ghcr.io/<org>/code-agent:<tag>