{
description = "atproto github";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-26.05";
microvm = {
url = "github:microvm-nix/microvm.nix";
inputs.nixpkgs.follows = "nixpkgs";
};
fenix = {
url = "github:nix-community/fenix";
inputs.nixpkgs.follows = "nixpkgs";
};
gomod2nix = {
url = "github:nix-community/gomod2nix";
inputs.nixpkgs.follows = "nixpkgs";
};
flake-compat = {
url = "https://git.lix.systems/lix-project/flake-compat/archive/main.tar.gz";
flake = false;
};
indigo = {
url = "github:oppiliappan/indigo";
flake = false;
};
timeless-fonts-src = {
url = "https://media.timeless.co/type/Timeless-Type-Family-1.094.zip";
flake = false;
};
ibm-plex-mono-src = {
url = "https://github.com/IBM/plex/releases/download/%40ibm%2Fplex-mono%401.1.0/ibm-plex-mono.zip";
flake = false;
};
sqlite-lib-src = {
url = "https://sqlite.org/2024/sqlite-amalgamation-3450100.zip";
flake = false;
};
fetch-tangled = {
url = "git+https://tangled.org/isabelroses.com/fetch-tangled";
inputs.nixpkgs.follows = "nixpkgs";
};
# the stable pds refuses oauth when it shares a site with the web, as dev hostnames do
nixpkgs-pds.url = "github:nixos/nixpkgs/nixos-unstable";
# hydrant has to match go.mod, hydrant-dev follows main
hydrant-dev = {
url = "git+https://tangled.org/ptr.pet/hydrant";
flake = false;
};
hydrant = {
url = "git+https://tangled.org/ptr.pet/hydrant";
flake = false;
};
};
outputs = {
self,
nixpkgs,
fenix,
gomod2nix,
indigo,
timeless-fonts-src,
sqlite-lib-src,
ibm-plex-mono-src,
microvm,
fetch-tangled,
hydrant,
hydrant-dev,
nixpkgs-pds,
...
}: let
supportedSystems = ["x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin"];
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
tangledPdsFor = system: let
pdsPkgs = nixpkgs-pds.legacyPackages.${system};
in
pdsPkgs.bluesky-pds.overrideAttrs (old: {
postInstall =
(old.postInstall or "")
+ ''
cp ${./pds/preauthorize.mjs} $out/lib/pds/tangled-preauthorize.mjs
substituteInPlace $out/lib/pds/index.ts \
--replace-fail "import pkg from" "import { mountPreauthorize } from './tangled-preauthorize.mjs'
import pkg from" \
--replace-fail "onCreated: (pds) => {" "onCreated: (pds) => {
mountPreauthorize(pds)"
'';
# a pds bump that drops what the route calls fails the build, not the pds at startup
doInstallCheck = true;
installCheckPhase = ''
runHook preInstallCheck
cd $(mktemp -d)
cp ${./pds}/* .
ln -s $out/lib/pds/node_modules node_modules
TANGLED_PDS_BIN=$out/bin/pds ${pdsPkgs.nodejs_22}/bin/node --test preauthorize.test.mjs
runHook postInstallCheck
'';
__darwinAllowLocalNetworking = true;
});
pdsFor = system:
(tangledPdsFor system).overrideAttrs (old: {
postInstall =
old.postInstall
+ ''
node ${./localinfra/pds-patch.mjs} $out/lib/pds ${./localinfra/pds-theme.css} ${timeless-fonts-src}/Sans-Grotesk/TimelessSansVF.woff2
'';
});
# go 1.26.10 refuses chains through the leaked emudhra intermediates, and nixpkgs
# doesn't ship it yet. only our own builds get it, because swapping go for all of
# nixpkgs rebuilds the world (libcap builds with go)
goFor = pkgs: let
go = pkgs.go_1_26;
in
if nixpkgs.lib.versionOlder go.version "1.26.10"
then
go.overrideAttrs (_: rec {
version = "1.26.10";
src = pkgs.fetchurl {
url = "https://go.dev/dl/go${version}.src.tar.gz";
hash = "sha256-/WPog7ovK/ypPBZGsx6+9j4rHk0rMXyXidl2po2B828=";
};
})
else go;
# flake registry entries every spindle image gets: nixpkgs is the rev the images are built from
spindleRegistry = let
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
locked = lock.nodes.${lock.nodes.root.inputs.nixpkgs}.locked;
in {
# a tarball, not github:, as nix 2.34 ignores narHash on github inputs
nixpkgs = {
type = "tarball";
url = "https://github.com/${locked.owner}/${locked.repo}/archive/${locked.rev}.tar.gz";
inherit (locked) rev lastModified narHash;
};
unstable = {
type = "tarball";
url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.zst";
};
};
nixpkgsFor = forAllSystems (system:
import nixpkgs {
inherit system;
overlays = [
fetch-tangled.overlays.default
(final: prev:
prev.lib.optionalAttrs prev.stdenv.hostPlatform.isDarwin {
# tsup's dts worker races the cjs build on darwin (EBADF)
wrangler = prev.wrangler.overrideAttrs (old: {
preBuild =
(old.preBuild or "")
+ ''
substituteInPlace packages/wrangler/tsup.config.ts --replace-fail "dts: true" "dts: false"
'';
});
})
];
});
mkPackageSet = pkgs:
pkgs.lib.makeScope pkgs.newScope (self: {
# docs and blog render web/src's components and theme
mkSiteSrc = dir:
pkgs.lib.fileset.toSource {
root = ./.;
fileset = pkgs.lib.fileset.unions [(./. + "/${dir}") ./web/src ./web/tsconfig.json];
};
mkGoSrc = import ./nix/go-src.nix {
inherit (pkgs) lib;
root = ./.;
};
go = goFor pkgs.buildPackages;
buildGoModule = pkgs.buildGoModule.override {inherit (self) go;};
buildGoApplication = args:
(self.callPackage "${gomod2nix}/builder" {
gomod2nix = gomod2nix.legacyPackages.${pkgs.stdenv.hostPlatform.system}.gomod2nix;
# proxy.golang.org can't fetch from knot2 yet, so sum.golang.org has
# no entry for hydrant and the module fetch fails. the fixed output
# hash pins its content anyway. the builder only uses this stdenv for
# module fetches
stdenvNoCC =
pkgs.stdenvNoCC
// {
mkDerivation = attrs: pkgs.stdenvNoCC.mkDerivation (attrs // {GONOSUMDB = "tangled.org/ptr.pet";});
};
}).buildGoApplication ({inherit (self) go;} // args);
rustPlatform = pkgs.makeRustPlatform {
inherit (fenix.packages.${pkgs.stdenv.hostPlatform.system}.stable) rustc cargo;
};
rustToolchainStatic = let
system = pkgs.stdenv.hostPlatform.system;
muslTarget = pkgs.pkgsStatic.stdenv.hostPlatform.rust.rustcTarget;
in
fenix.packages.${system}.combine [
fenix.packages.${system}.stable.cargo
fenix.packages.${system}.stable.rustc
fenix.packages.${system}.targets.${muslTarget}.stable.rust-std
];
rustPlatformStatic = pkgs.pkgsStatic.makeRustPlatform {
cargo = self.rustToolchainStatic;
rustc = self.rustToolchainStatic;
};
cargoNix = self.callPackage ./nix/cargo-nix.nix {
inherit (fenix.packages.${pkgs.stdenv.hostPlatform.system}.stable) rustc cargo;
};
cargoNixStatic = pkgs.pkgsStatic.callPackage ./nix/cargo-nix.nix {
rustc = self.rustToolchainStatic;
cargo = self.rustToolchainStatic;
};
crateBin = cargoNix: crate:
cargoNix.workspaceMembers.${crate}.build.overrideAttrs (old: {
meta = (old.meta or {}) // {mainProgram = crate;};
});
modules = ./nix/gomod2nix.toml;
sqlite-lib = self.callPackage ./nix/pkgs/sqlite-lib.nix {
inherit sqlite-lib-src;
};
lexgen = self.callPackage ./nix/pkgs/lexgen.nix {inherit indigo;};
goat = self.callPackage ./nix/pkgs/goat.nix {inherit indigo;};
web-static-files = self.callPackage ./nix/pkgs/web-static-files.nix {
inherit timeless-fonts-src ibm-plex-mono-src;
};
ogre-fonts = self.callPackage ./nix/pkgs/ogre-fonts.nix {
inherit timeless-fonts-src;
};
blog = self.callPackage ./nix/pkgs/blog.nix {
src = self.mkSiteSrc "blog";
};
docs = self.callPackage ./nix/pkgs/docs.nix {
src = self.mkSiteSrc "docs";
};
hydrant-ffi = self.callPackage ./nix/pkgs/hydrant-ffi.nix {
inherit hydrant;
# spindle's static builds link this too, and fenix's default toolchain has no musl std
rustPlatform =
if pkgs.stdenv.hostPlatform.isStatic
then self.rustPlatformStatic
else self.rustPlatform;
};
hydrant-bin = self.callPackage ./nix/pkgs/hydrant-bin.nix {
hydrant = hydrant-dev;
inherit (self) rustPlatform;
};
spindle = self.callPackage ./nix/pkgs/spindle.nix {
src = self.mkGoSrc "cmd/spindle";
};
shuttle = self.crateBin self.cargoNix "shuttle";
shuttle-static = self.crateBin self.cargoNixStatic "shuttle";
knot-rs = self.crateBin self.cargoNix "knot-server";
knot-migrate = self.crateBin self.cargoNix "knot-migrate";
dolly = self.callPackage ./nix/pkgs/dolly.nix {};
deliberi = self.callPackage ./nix/pkgs/deliberi.nix {
src = self.mkGoSrc "cmd/deliberi";
};
migrator = self.callPackage ./nix/pkgs/migrator.nix {
src = self.mkGoSrc "cmd/migrator";
};
sitesd = self.callPackage ./nix/pkgs/sitesd.nix {
src = self.mkGoSrc "cmd/sitesd";
};
cilogs = self.callPackage ./nix/pkgs/cilogs.nix {
src = self.mkGoSrc "cmd/cilogs";
};
knotmirror = self.cargoNix.workspaceMembers.knotmirror.build;
bobbin = self.crateBin self.cargoNix "bobbin";
# no ci workflow runs these, so the package build does
tranquil-gate = (self.crateBin self.cargoNix "tranquil-gate").override {
runTests = true;
testPreRun = "export SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
# these clear the env, so they need CA roots in /etc, which the sandbox lacks
testCrateFlags = ["--skip" "toml_and_environment_overrides" "--skip" "existing_environment_only_configuration_works"];
};
zoekt-webserver = self.callPackage ./nix/pkgs/zoekt-webserver.nix {};
attic-server = pkgs.callPackage ./nix/pkgs/attic-server.nix {};
zoekt-tngl-indexserver = self.callPackage ./nix/pkgs/zoekt-tngl-indexserver.nix {
src = self.mkGoSrc "cmd/zoekt-tngl-indexserver";
};
});
spindleNixosFor = system:
nixpkgs.lib.nixosSystem {
inherit system;
modules = [self.nixosModules.spindle-nixos];
};
devTools = system: let
pkgs = nixpkgsFor.${system};
packages' = self.packages.${system};
in
[
pkgs.go
pkgs.air
pkgs.gopls
pkgs.httpie
pkgs.litecli
pkgs.websocat
pkgs.tailwindcss
pkgs.nixos-shell
pkgs.redis
pkgs.worker-build
pkgs.cargo-generate
pkgs.qemu
pkgs.cdrkit
pkgs.buf
pkgs.protobuf
pkgs.protoc-gen-prost
pkgs.protoc-gen-prost-crate
pkgs.protoc-gen-prost-serde
pkgs.protoc-gen-go
pkgs.protoc-gen-go-grpc
(fenix.packages.${system}.combine [
fenix.packages.${system}.stable.cargo
fenix.packages.${system}.stable.rustc
fenix.packages.${system}.stable.rust-src
fenix.packages.${system}.stable.clippy
fenix.packages.${system}.stable.rustfmt
fenix.packages.${system}.stable.rust-analyzer
fenix.packages.${system}.targets.wasm32-unknown-unknown.stable.rust-std
])
pkgs.coreutils # for those of us who are on systems that use busybox (alpine)
packages'.lexgen
packages'.goat
packages'.treefmt-wrapper
pkgs.e2fsprogs
pkgs.util-linux
pkgs.pnpm
# node dropped corepack in 25, and the web setup needs it
pkgs.corepack
# e2e needs node 26, whose fetch accepts undici's dispatcher
pkgs.nodejs_26
# the blog's link check (pnpm check:links)
pkgs.lychee
]
++ pkgs.lib.optionals pkgs.stdenv.isLinux [
pkgs.parted
pkgs.slirp4netns
pkgs.iproute2
# cargo wants a plain cc and the static shell's compiler is prefixed
pkgs.stdenv.cc
# jemalloc builds with make, which the static shell lacks
pkgs.gnumake
];
in {
overlays.default = final: prev: {
inherit
(mkPackageSet final)
lexgen
goat
sqlite-lib
spindle
shuttle
knot-rs
knot-migrate
docs
dolly
deliberi
migrator
sitesd
cilogs
knotmirror
bobbin
tranquil-gate
zoekt-webserver
zoekt-tngl-indexserver
;
};
packages = forAllSystems (system: let
pkgs = nixpkgsFor.${system};
linuxPkgs = nixpkgsFor."x86_64-linux";
imageSystem = "${nixpkgs.lib.head (nixpkgs.lib.splitString "-" system)}-linux";
imagePkgs = nixpkgsFor.${imageSystem};
packages = mkPackageSet pkgs;
staticPackages = mkPackageSet pkgs.pkgsStatic;
crossPackages = mkPackageSet pkgs.pkgsCross.gnu64.pkgsStatic;
spindle-image-helpers = let
shuttle = (mkPackageSet linuxPkgs).shuttle-static;
in
linuxPkgs.callPackage ./nix/pkgs/spindle-image-helpers.nix {
inherit shuttle spindleRegistry;
};
in {
inherit
(packages)
web-static-files
ogre-fonts
blog
lexgen
goat
spindle
hydrant-ffi
hydrant-bin
knot-rs
knot-migrate
sqlite-lib
docs
shuttle
shuttle-static
dolly
deliberi
migrator
sitesd
cilogs
knotmirror
bobbin
tranquil-gate
zoekt-webserver
attic-server
zoekt-tngl-indexserver
;
tangled-pds = tangledPdsFor system;
pkgsStatic-spindle = staticPackages.spindle;
pkgsStatic-sqlite-lib = staticPackages.sqlite-lib;
pkgsStatic-dolly = staticPackages.dolly;
pkgsCross-gnu64-pkgsStatic-spindle = crossPackages.spindle;
pkgsCross-gnu64-pkgsStatic-dolly = crossPackages.dolly;
treefmt-wrapper = pkgs.treefmt.withConfig {
settings.formatter = {
alejandra = {
command = pkgs.lib.getExe pkgs.alejandra;
includes = ["*.nix"];
excludes = ["nix/Cargo.nix"];
};
gofmt = {
command = pkgs.lib.getExe' pkgs.go "gofmt";
options = ["-w"];
includes = ["*.go"];
};
# web formats via pnpm lint (pinned prettier, svelte-aware); treefmt
# prettier would fight it on union types
rustfmt = {
command = pkgs.lib.getExe' fenix.packages.${system}.stable.rustfmt "rustfmt";
options = ["--edition" "2024"];
includes = ["*.rs"];
excludes = ["**/src/_lex/**" "**/src/gen/**" "knot2/third_party/gix-imara-diff/**"];
};
};
};
spindle-nixos-image = imagePkgs.callPackage ./nix/pkgs/spindle-nixos-image.nix {
nixosSystem = spindleNixosFor imageSystem;
};
spindle-nixos-large-image = self.packages.${system}.spindle-nixos-image.override {
vcpus = 6;
memoryMiB = 8192;
};
spindle-nixos-image-tarball = imagePkgs.runCommand "spindle-nixos-image-tarball.tar.gz" {} ''
tar -S -C ${self.packages.${system}.spindle-nixos-image} -h -czf $out .
'';
spindle-alpine-image = let
branch = "3.24";
version = "${branch}.0";
arch = "x86_64";
cdn = "https://dl-cdn.alpinelinux.org/alpine/v${branch}/releases/${arch}";
in
linuxPkgs.callPackage ./nix/pkgs/spindle-alpine-image.nix {
inherit arch spindle-image-helpers;
repositories = [
"https://dl-cdn.alpinelinux.org/alpine/v${branch}/main"
"https://dl-cdn.alpinelinux.org/alpine/v${branch}/community"
];
rootfs = linuxPkgs.fetchurl {
url = "${cdn}/alpine-minirootfs-${version}-${arch}.tar.gz";
hash = "sha256-3poRwODn6clNs+2K97RQ6vwLE2h71+kZnVUFDyCqCok=";
};
kernel = linuxPkgs.fetchurl {
url = "${cdn}/netboot-${version}/vmlinuz-virt";
hash = "sha256-Hmv5Ancgx1w+0NeRcfIbV5HuQMqXldB8fG4E3F6irpA=";
};
initramfs = linuxPkgs.fetchurl {
url = "${cdn}/netboot-${version}/initramfs-virt";
hash = "sha256-ZCWGSaVMOYOmLz1Gwsf2RhYarMqk+tFVA6MMDWiHVJQ=";
};
modloop = linuxPkgs.fetchurl {
url = "${cdn}/netboot-${version}/modloop-virt";
hash = "sha256-p3yO7yU28k04iT01sOzhDmEYi+Yl7VZs5r3RYsWCBX0=";
};
};
spindle-alpine-image-tarball = linuxPkgs.runCommand "spindle-alpine-image-tarball.tar.gz" {} ''
tar -S -C ${self.packages.${system}.spindle-alpine-image} -h -czf $out .
'';
spindle-almalinux10-image = let
branch = "10";
version = "${branch}.2";
arch = "x86_64";
goarch = "amd64";
kver = "6.12.0-211.34.1.el10_2.${arch}";
# Every time a new point release comes out, and the previous one is deprecated,
# those repos get moved to the vault URL. However, for CI purposes, it
# would be annoying if the image completely stopped working from a new
# release being available, so we just fall back to the vault URL until
# the RPMs can be updated.
makeRpmUrls = rpm: [
"https://repo.almalinux.org/almalinux/${version}/BaseOS/${arch}/os/Packages/${rpm}"
"https://vault.almalinux.org/almalinux/${version}/BaseOS/${arch}/os/Packages/${rpm}"
];
in
linuxPkgs.callPackage ./nix/pkgs/spindle-almalinux-image.nix {
inherit arch kver spindle-image-helpers;
rootfs = linuxPkgs.fetchurl {
url = "https://github.com/AlmaLinux/container-images/raw/cd5582c41aaf7f695b2473264b322f4080066796/default/${goarch}/almalinux-${branch}-default-${goarch}.tar.xz";
hash = "sha256-60TIMFmaCoPv//XLAm6Cm/wkpmtbwH996zgM7CB/ypQ=";
};
kernel-modules-core = linuxPkgs.fetchurl {
urls = makeRpmUrls "kernel-modules-core-${kver}.rpm";
hash = "sha256-KYR/dnm/oCglQ4uvFTZlW6WBR1HBUNh/yS64PxyIQvk=";
};
kernel-modules = linuxPkgs.fetchurl {
urls = makeRpmUrls "kernel-modules-${kver}.rpm";
hash = "sha256-n2SPQuM+dmTQn4Ustkl9GyF8xH3godU0fjJO8oz4JDY=";
};
uki = linuxPkgs.fetchurl {
urls = makeRpmUrls "kernel-uki-virt-${kver}.rpm";
hash = "sha256-ds2+uSPpuHHoVyYaUrh8DSwWgVWYMijgGoneAE+JP9I=";
};
# doas-sudo-shim 0.1.x in the repos requires util-linux, which isn't in the
# container and is annoying to get inside, so just get it ourselves.
doas-sudo-shim-src = linuxPkgs.fetchFromGitHub {
owner = "jirutka";
repo = "doas-sudo-shim";
rev = "v0.2.0";
sha256 = "sha256-USSakVUzCbUY1DJLmDCiwdq/xjOwwnm3VtXBBeXeV1A=";
};
};
spindle-almalinux10-image-tarball = linuxPkgs.runCommand "spindle-almalinux10-image-tarball.tar.gz" {} ''
tar -S -C ${self.packages.${system}.spindle-almalinux10-image} -h -czf $out .
'';
});
checks = forAllSystems (system: {
tangled-pds = self.packages.${system}.tangled-pds;
});
devShells = forAllSystems (system: let
pkgs = nixpkgsFor.${system};
packages' = self.packages.${system};
staticShell = args:
(pkgs.mkShell.override {
stdenv = pkgs.pkgsStatic.stdenv;
}) (args
// {
nativeBuildInputs =
args.nativeBuildInputs
++ pkgs.lib.optionals pkgs.stdenv.isDarwin [
pkgs.darwin.cctools
];
});
in {
default = staticShell {
nativeBuildInputs = devTools system;
shellHook = ''
export CC=${pkgs.stdenv.cc}/bin/cc
unset NIX_CFLAGS_LINK
export SQLX_OFFLINE=true
(
cd "$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
mkdir -p web/static
rm -rf web/static/fonts web/static/logos
cp -fr --no-preserve=ownership,mode ${packages'.web-static-files}/* web/static
# the docs and blog sites serve the same fonts and logos from their own public dirs
rm -rf docs/public/fonts blog/public/fonts blog/public/logos
mkdir -p docs/public blog/public
cp -fr --no-preserve=ownership,mode ${packages'.web-static-files}/fonts docs/public/fonts
cp -fr --no-preserve=ownership,mode ${packages'.web-static-files}/{fonts,logos} blog/public/
rm -rf ogre/fonts
cp -fr --no-preserve=ownership,mode ${packages'.ogre-fonts} ogre/fonts
)
export TANGLED_OAUTH_CLIENT_KID="$(date +%s)"
export TANGLED_OAUTH_CLIENT_SECRET="$(${packages'.goat}/bin/goat key generate -t P-256 | grep -A1 "Secret Key" | tail -n1 | awk '{print $1}')"
# Make xcrun (Nix stub) able to find ld from the system Command Line Tools.
# Without this, worker-build/cargo fails with "error: tool 'ld' not found".
# it goes last because its make is a shim that looks for gnumake inside
# DEVELOPER_DIR, which is a nix sdk without one, so it broke every C build.
if [ -d /Library/Developer/CommandLineTools/usr/bin ]; then
export PATH=$PATH:/Library/Developer/CommandLineTools/usr/bin
fi
'';
env.CGO_ENABLED = 1;
# spindle embeds hydrant, so anything that links it needs the staticlib
env.CGO_LDFLAGS = "-L${packages'.hydrant-ffi}/lib";
};
});
apps = forAllSystems (system: let
pkgs = nixpkgsFor."${system}";
packages' = self.packages.${system};
air-watcher = name: arg:
pkgs.writeShellScriptBin "run"
''
export PATH=${pkgs.go}/bin:$PATH
${pkgs.air}/bin/air -c ./.air/${name}.toml \
-build.args_bin "${arg}"
'';
in {
watch-spindle = {
type = "app";
program = ''${air-watcher "spindle" ""}/bin/run'';
};
watch-blog = {
type = "app";
program = toString (pkgs.writeShellScript "watch-blog" ''
# astro reloads on post and component changes; drafts show up here only
cd blog
[ -d node_modules ] || ${pkgs.nodejs}/bin/npm install
exec ${pkgs.nodejs}/bin/npm run dev -- --host 0.0.0.0 --port 3001
'');
};
serve-docs = {
type = "app";
program = toString (pkgs.writeShellScript "serve-docs" ''
echo "building docs..."
docsOut=$(nix build --no-link --print-out-paths .#docs)
echo "serving docs at http://localhost:1414"
cd "$docsOut"
exec ${pkgs.python3}/bin/python3 -m http.server 1414
'');
};
watch-docs = {
type = "app";
program = toString (pkgs.writeShellScript "watch-docs" ''
export PATH=${pkgs.nodejs}/bin:$PATH
cd docs
[ -d node_modules ] || npm install
exec npm run dev
'');
};
regenerate-proto = {
type = "app";
program =
(pkgs.writeShellApplication {
name = "regenerate-proto";
runtimeInputs = with pkgs; [git buf coreutils];
text = ''
rootDir=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
cd "$rootDir"
echo ">>> regenerating protobuf files.."
buf generate
echo ">>> generating file descriptor set for shuttle..."
buf build -o shuttle/src/gen/file_descriptor_set.bin
echo ">>> done"
'';
})
+ "/bin/regenerate-proto";
};
cargo-nix = {
type = "app";
program =
(pkgs.writeShellApplication {
name = "cargo-nix";
runtimeInputs = [pkgs.crate2nix pkgs.git fenix.packages.${system}.stable.cargo];
text = ''
cd "$(git rev-parse --show-toplevel)"
crate2nix generate -f ./Cargo.toml -o ./nix/Cargo.nix -h ./nix/crate-hashes.json
'';
})
+ "/bin/cargo-nix";
};
gomod2nix = {
type = "app";
program = toString (pkgs.writeShellScript "gomod2nix" ''
${gomod2nix.legacyPackages.${system}.gomod2nix}/bin/gomod2nix generate --outdir ./nix
'');
};
localinfra = {
type = "app";
program =
(pkgs.writeShellApplication {
name = "localinfra";
runtimeInputs = with pkgs;
[process-compose postgresql_14 caddy dnsmasq dnsutils curl jq git nodejs_22 yarn openssl packages'.hydrant-bin (pdsFor system)]
++ lib.optionals stdenv.hostPlatform.isLinux [socat slirp4netns util-linux iproute2 iptables procps gawk qemu_kvm e2fsprogs patchelf]
++ lib.optionals stdenv.hostPlatform.isDarwin [stdenv.cc]
++ lib.concatMap (name: let
file = ./localinfra/pc/services + "/${name}/packages";
in
lib.optionals (builtins.pathExists file)
(map (attr: pkgs.${attr}) (lib.filter (attr: attr != "") (lib.splitString "\n" (builtins.readFile file)))))
(lib.attrNames (builtins.readDir ./localinfra/pc/services))
++ devTools system;
text = ''
rootDir=$(jj --ignore-working-copy root 2>/dev/null || git rev-parse --show-toplevel) || (echo "error: can't find repo root?"; exit 1)
# only macos ships the sqlite the libsqlite3 tag needs
export CGO_CFLAGS="-I${packages'.sqlite-lib}/include''${CGO_CFLAGS:+ $CGO_CFLAGS}"
export CGO_LDFLAGS="-L${packages'.sqlite-lib}/lib''${CGO_LDFLAGS:+ $CGO_LDFLAGS}"
${pkgs.lib.optionalString pkgs.stdenv.hostPlatform.isDarwin ''
# .cargo/config.toml names the static shell's prefixed cc, which this app doesn't have
export CARGO_TARGET_AARCH64_APPLE_DARWIN_LINKER=cc
# rusqlite links the system sqlite, which the nix toolchain can't see
export LIBRARY_PATH="${packages'.sqlite-lib}/lib''${LIBRARY_PATH:+:$LIBRARY_PATH}"
''}
${pkgs.lib.optionalString pkgs.stdenv.hostPlatform.isLinux ''
# the static shell leaves -static in NIX_CFLAGS_LINK, which breaks glibc links
unset NIX_CFLAGS_LINK
export OPENSSL_INCLUDE_DIR=${pkgs.openssl.dev}/include
export OPENSSL_LIB_DIR=${pkgs.openssl.out}/lib
# rusqlite would link the system sqlite
export SQLITE3_LIB_DIR=${packages'.sqlite-lib}/lib
export SQLITE3_STATIC=1
# npm's workerd needs a nix loader, bin/patch-workerd reads these
export TNGL_DYNAMIC_LINKER=${pkgs.stdenv.cc.bintools.dynamicLinker}
export TNGL_NIX_LIBS=${pkgs.lib.makeLibraryPath [pkgs.stdenv.cc.cc.lib pkgs.stdenv.cc.libc]}
# e2e/run uses this chromium because playwright's own won't run on nixos
export E2E_CHROMIUM=${pkgs.chromium}/bin/chromium
''}
exec "$rootDir/localinfra/pc/bin/up" "$@"
'';
})
+ /bin/localinfra;
};
lexgen = {
type = "app";
program =
(pkgs.writeShellApplication {
name = "lexgen";
runtimeInputs = [pkgs.stdenv.cc pkgs.gnused];
text = ''
if ! command -v lexgen > /dev/null; then
echo "error: must be executed from devshell"
exit 1
fi
# pin CC to a glibc gcc so a stray musl cc cant mess up CGO
export CC=${pkgs.stdenv.cc}/bin/cc
rootDir=$(jj --ignore-working-copy root || git rev-parse --show-toplevel) || (echo "error: can't find repo root?"; exit 1)
cd "$rootDir"
# preserve hand-written extensions and tests
find api/tangled api/org_tangled -maxdepth 1 -type f -not -name '*_ext.go' -not -name '*_test.go' -delete
lexgen --build-file lexicon-build-config.json lexicons
# disable type registration temporarily while running cborgen
find api/tangled api/org_tangled -maxdepth 1 -name '*.go' -not -name '*_ext.go' -exec \
sed -i.bak 's/\tutil/\/\/\tutil/' {} +
# lexgen generates incomplete Marshaler/Unmarshaler for union types
find api/tangled api/org_tangled -maxdepth 1 -name '*.go' -not -name '*_ext.go' -not -name "cbor_gen.go" -exec \
sed -i '/^func.*\(MarshalCBOR\|UnmarshalCBOR\)/,/^}/ s/^/\/\/ /' {} +
for f in api/tangled/*_ext.go api/org_tangled/*_ext.go; do [ -e "''$f" ] || continue; mv "''$f" "''$f.bak"; done
${pkgs.gotools}/bin/goimports -w api/tangled/* api/org_tangled/*
CGO_ENABLED=1 go run ./cmd/cborgen/
for f in api/tangled/*_ext.go.bak api/org_tangled/*_ext.go.bak; do [ -e "''$f" ] || continue; mv "''$f" "''${f%.bak}"; done
lexgen --build-file lexicon-build-config.json lexicons
go run ./cmd/lexpostgen/
rm -f api/tangled/*.bak api/org_tangled/*.bak
'';
})
+ /bin/lexgen;
};
});
nixosModules.deliberi = {
lib,
pkgs,
...
}: {
imports = [./nix/modules/deliberi.nix];
services.tangled.deliberi.package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.deliberi;
};
nixosModules.tranquil-gate = {
lib,
pkgs,
...
}: {
imports = [./nix/modules/tranquil-gate.nix];
services.tangled.tranquil-gate.package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.tranquil-gate;
};
nixosModules.migrator = {
lib,
pkgs,
...
}: {
imports = [./nix/modules/migrator.nix];
services.tangled.migrator.package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.migrator;
};
nixosModules.sitesd = {
lib,
pkgs,
...
}: {
imports = [./nix/modules/sitesd.nix];
services.tangled.sitesd.package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.sitesd;
};
nixosModules.cilogs = {
lib,
pkgs,
...
}: {
imports = [./nix/modules/cilogs.nix];
services.tangled.cilogs.package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.cilogs;
};
nixosModules.knotmirror = {
lib,
pkgs,
...
}: {
imports = [./nix/modules/knotmirror.nix];
services.tangled.knotmirror.package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.knotmirror;
};
nixosModules.zoekt-tnglserver = {
lib,
pkgs,
...
}: {
imports = [./nix/modules/zoekt-tnglserver.nix];
services.tangled.zoekt.package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.zoekt-tngl-indexserver;
services.tangled.zoekt.zoekt-webserver-package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.zoekt-webserver;
};
nixosModules.knot-rs = {
lib,
pkgs,
...
}: {
imports = [./nix/modules/knot-rs.nix];
services.tangled.knot-rs.package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.knot-rs;
};
nixosModules.spindle = {
lib,
pkgs,
...
}: {
imports = [./nix/modules/spindle.nix];
services.tangled.spindle.package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.spindle;
};
nixosModules.shuttle = {
lib,
pkgs,
...
}: {
imports = [./nix/modules/shuttle.nix];
services.tangled.shuttle.package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.shuttle;
};
nixosModules.spindle-nixos = import ./nix/microvm/spindle-vm.nix {inherit self microvm spindleRegistry;} ./nix/microvm/qemu.nix;
formatter = forAllSystems (system: self.packages.${system}.treefmt-wrapper);
nixosConfigurations = {
spindle-nixos = spindleNixosFor "x86_64-linux";
spindle-nixos-aarch64 = spindleNixosFor "aarch64-linux";
};
};
}