Files
openwrt-feeds/README.md
T
jackos1998 53720becf1 feeds: Vendor repository indexes
Keep the native APK indexes alongside the expanded package
metadata so image builds do not fetch mutable repository state.

Generate pins atomically and retry transient generation failures.
2026-08-23 23:04:26 +01:00

3.8 KiB

openwrt-feeds

Pinned OpenWrt package feed hashes for use with astro/nix-openwrt-imagebuilder.

Take this repo as a flake input and pass one of its cachePaths to that project's cachePath, and image builds resolve their packages from these pins instead of from whatever the download server is serving today:

openwrt-imagebuilder.lib.build {
  inherit pkgs;
  release = "snapshot";
  cachePath = openwrt-feeds.cachePaths.snapshot;
  target = "realtek";
  variant = "rtl930x";
  profile = "xikestor_sks8300-8x";
  packages = [ "luci" ];
}

cachePaths has one entry per pinned release. A flake's source is copied to the store, so each is an importable store path.

Everything under cache/ is generated. Nothing here is hand-written.

Why this exists

nix-openwrt-imagebuilder ships hashes for the OpenWrt download server, but only at the level of each feed's index (packages.adb), not the packages inside it. When an index hash no longer matches, two things happen:

  • The build fails outright with hash mismatch in fixed-output derivation.
  • Resolving the package list falls back to import-from-derivation, so evaluation needs the network and can fail for reasons unrelated to what is being evaluated. In a flake that also describes other systems, that turns an OpenWrt feed rebuild into an evaluation failure for everything else in the flake.

Those indexes move constantly. Snapshot is rebuilt daily, and release feeds are no better: releases/<version>/packages/ is a symlink to the rolling packages-<major> feed shared by every point release, so it changes whenever any package in it is rebuilt. Upstream refreshes its hashes once a day and routinely loses that race.

Regenerating locally writes the expanded per-package files (cache/<release>/packages/<arch>/), which nix-openwrt-imagebuilder imports directly. It also vendors the repository indexes and rewrites their generated sourceInfo.url values to flake-local file:// URLs. That removes the import-from-derivation, keeps image builds from fetching mutable indexes, leaves every .apk as a plain pinned fetchurl, and makes the pin something to move deliberately rather than something upstream moves daily.

The generated files are large — a few MB per release, rewritten wholesale each time — which is why they are kept in a repository of their own rather than in the history of whatever consumes them.

Regenerating

From a checkout:

nix run .#update

This rewrites cache/ for every entry in pins in flake.nix. Review and commit the result, then update the pin in the consuming flake.

Pass a release to regenerate only that pin:

nix run .#update -- 25.12.5

Each release is generated in a temporary directory and replaces its existing cache only after all metadata and repository indexes have been fetched successfully. Transiently inconsistent upstream metadata therefore leaves the existing pin intact.

Adding a release or target means adding it to pins and re-running. Note that cache/<release>/default.nix lists every target in the release, but only the ones generated here exist on disk; the others are referenced lazily and are never evaluated.

Layout

Path Contents
cache/<release>/default.nix Per-target entry points for the release
cache/<release>/indexes/ Vendored package repository indexes used by the image build
cache/<release>/targets/<target>/<variant>/ ImageBuilder hash, profiles.json extract, kmod and target package feeds
cache/<release>/packages/<arch>.nix Feed index hashes for the target's architecture
cache/<release>/packages/<arch>/<feed>.nix Expanded per-package versions, dependencies and hashes

Mirroring

git.nul.ie/dev/openwrt-feeds is canonical; it is push-mirrored to github.com/devplayer0/openwrt-feeds.