#!/usr/bin/env bash # nb-substituters — manage nixbuild.net account binary-cache substituters and # their trusted public keys, with a guard for nixbuild's URL rules. # # WHY a wrapper: nixbuild's `settings substituters --add` REJECTS path-style # URLs (e.g. https://host/cache-name → "invalid substituter", trailing slash # doesn't help). It accepts only host-root HTTPS (https://host), s3://bucket/ # prefix, or cachix://name. An Attic/nix-serve cache served at a sub-path must # therefore be reached path-LESS — e.g. front it at a host root with a reverse # proxy that rewrites `/.narinfo` and `/nar/*` into the cache namespace # (the oleks fleet does this for Attic at https://nix-cache-custom.oleks.space # via a Caddy root-rewrite). This wrapper catches the path-style mistake before # it hits the shell and explains the fix. # # Also: `builders-use-substitutes = true` on the CLIENT does NOT forward the # client's substituter list to nixbuild — it tells the remote to use ITS OWN # (these, account-side) substituters. So caches must be registered here to take # effect during remote builds. # # Usage: # nb-substituters list # show substituters + trusted keys # nb-substituters add-cache [public-key] # add substituter (+ key if given) # nb-substituters add-key # add a trusted public key only # nb-substituters remove # remove a substituter # nb-substituters remove-key # remove a trusted public key # nb-substituters reset # reset both back to defaults # # Delegates the actual shell I/O to nbshell (sibling script). set -euo pipefail HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" NBSHELL="${HERE}/nbshell" valid_substituter() { # Accept: https://host (no path), s3://bucket[/prefix], cachix://name. # Reject: https://host/path (the nixbuild gotcha), http (warn), bare host. local u="$1" case "$u" in https://*/*/* | https://*/?* ) # has a path component after the host return 1 ;; https://*/ ) return 1 ;; https://* ) return 0 ;; s3://* | cachix://* ) return 0 ;; *) return 1 ;; esac } case "${1:-}" in list) "$NBSHELL" 'settings substituters --show' 'settings trusted-public-keys --show' ;; add-cache) url="${2:-}"; key="${3:-}" [ -n "$url" ] || { echo "add-cache: need a URL" >&2; exit 2; } if ! valid_substituter "$url"; then cat >&2 <.narinfo and /nar/* into the cache namespace, then add that bare host here. EOF exit 1 fi if [ -n "$key" ]; then "$NBSHELL" \ "settings substituters --add ${url}" \ "settings trusted-public-keys --add ${key}" \ 'settings substituters --show' \ 'settings trusted-public-keys --show' else echo "note: no public key given — content from $url will only validate if its signing key is already trusted." >&2 "$NBSHELL" "settings substituters --add ${url}" 'settings substituters --show' fi ;; add-key) key="${2:-}"; [ -n "$key" ] || { echo "add-key: need a public key" >&2; exit 2; } "$NBSHELL" "settings trusted-public-keys --add ${key}" 'settings trusted-public-keys --show' ;; remove) url="${2:-}"; [ -n "$url" ] || { echo "remove: need a URL" >&2; exit 2; } "$NBSHELL" "settings substituters --remove ${url}" 'settings substituters --show' ;; remove-key) key="${2:-}"; [ -n "$key" ] || { echo "remove-key: need a public key" >&2; exit 2; } "$NBSHELL" "settings trusted-public-keys --remove ${key}" 'settings trusted-public-keys --show' ;; reset) "$NBSHELL" \ 'settings substituters --reset' \ 'settings trusted-public-keys --reset' \ 'settings substituters --show' \ 'settings trusted-public-keys --show' ;; ""|-h|--help|help) sed -n '2,32p' "$0" ;; *) echo "nb-substituters: unknown command '${1}'" >&2 exit 2 ;; esac