From 83fee31487ae7e9f8e3dfdc612a9fa8b79f505fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey=20=28they/them=29?= Date: Tue, 10 Mar 2026 14:02:33 -0700 Subject: [PATCH 1/3] feat: self-host Attic binary cache on glyph Add atticd service on glyph (port 8199) with SQLite backend, chunked deduplication, and 30-day garbage collection. Expose via cache.zx.dev reverse proxy on spore. Replace Cachix with Attic in CI workflow. - hosts/glyph/services/attic.nix: atticd service config - hosts/spore/services/web: cache.zx.dev nginx virtualHost - .github/workflows/ci.yml: attic login/use/push replaces cachix-action - flake.nix: attic input + cache.zx.dev substituter - lib/hosts.nix: wire atticd NixOS module into host builder - lib/secrets/glyph.nix: register attic-credentials secret Co-Authored-By: Claude Opus 4.6 --- .github/workflows/ci.yml | 14 ++-- flake.lock | 121 ++++++++++++++++++++++++++- flake.nix | 5 ++ hosts/glyph/services/attic.nix | 36 ++++++++ hosts/glyph/services/default.nix | 2 + hosts/spore/services/web/default.nix | 11 +++ lib/hosts.nix | 2 + lib/secrets/glyph.nix | 1 + 8 files changed, 184 insertions(+), 8 deletions(-) create mode 100644 hosts/glyph/services/attic.nix diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 20dabe71..4c9c188e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,15 +29,19 @@ jobs: - uses: cachix/install-nix-action@v31 with: github_access_token: ${{ secrets.GITHUB_TOKEN }} - - uses: cachix/cachix-action@v16 - with: - name: stackptr - authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} + - name: Configure Attic cache + run: | + nix profile install github:zhaofengli/attic#attic-client + attic login rc https://cache.zx.dev ${{ secrets.ATTIC_TOKEN }} + attic use rc:main - run: nix flake check - - name: Build NixOS system + - name: Build system configuration run: | if [ "${{ matrix.system }}" = "aarch64-darwin" ]; then nix build .#darwinConfigurations.${{ matrix.host }}.system else nix build .#nixosConfigurations.${{ matrix.host }}.config.system.build.toplevel fi + - name: Push to Attic + if: github.ref == 'refs/heads/main' + run: attic push rc:main ./result diff --git a/flake.lock b/flake.lock index 7368de6c..791b7cbe 100644 --- a/flake.lock +++ b/flake.lock @@ -45,6 +45,31 @@ "type": "github" } }, + "attic": { + "inputs": { + "crane": "crane", + "flake-compat": "flake-compat", + "flake-parts": "flake-parts", + "nix-github-actions": "nix-github-actions", + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1758711588, + "narHash": "sha256-0nZlCCDC5PfndsQJXXtcyrtrfW49I3KadGMDlutzaGU=", + "owner": "zhaofengli", + "repo": "attic", + "rev": "12cbeca141f46e1ade76728bce8adc447f2166c6", + "type": "github" + }, + "original": { + "owner": "zhaofengli", + "repo": "attic", + "type": "github" + } + }, "brew-src": { "flake": false, "locked": { @@ -62,6 +87,21 @@ "type": "github" } }, + "crane": { + "locked": { + "lastModified": 1751562746, + "narHash": "sha256-smpugNIkmDeicNz301Ll1bD7nFOty97T79m4GUMUczA=", + "owner": "ipetkov", + "repo": "crane", + "rev": "aed2020fd3dc26e1e857d4107a5a67a33ab6c1fd", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, "disko": { "inputs": { "nixpkgs": [ @@ -83,6 +123,22 @@ } }, "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1747046372, + "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_2": { "flake": false, "locked": { "lastModified": 1767039857, @@ -99,6 +155,27 @@ } }, "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "attic", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1751413152, + "narHash": "sha256-Tyw1RjYEsp5scoigs1384gIg6e0GoBVjms4aXFfRssQ=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "77826244401ea9de6e3bac47c2db46005e1f30b5", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_2": { "inputs": { "nixpkgs-lib": [ "nixpkgs" @@ -120,7 +197,7 @@ }, "git-hooks-nix": { "inputs": { - "flake-compat": "flake-compat", + "flake-compat": "flake-compat_2", "gitignore": "gitignore", "nixpkgs": [ "nixpkgs" @@ -273,6 +350,27 @@ "type": "github" } }, + "nix-github-actions": { + "inputs": { + "nixpkgs": [ + "attic", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1737420293, + "narHash": "sha256-F1G5ifvqTpJq7fdkT34e/Jy9VCyzd5XfJ9TO8fHhJWE=", + "owner": "nix-community", + "repo": "nix-github-actions", + "rev": "f4158fa080ef4503c8f4c820967d946c2af31ec9", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nix-github-actions", + "type": "github" + } + }, "nix-homebrew": { "inputs": { "brew-src": "brew-src" @@ -343,6 +441,22 @@ } }, "nixpkgs-stable": { + "locked": { + "lastModified": 1751741127, + "narHash": "sha256-t75Shs76NgxjZSgvvZZ9qOmz5zuBE8buUaYD28BMTxg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "29e290002bfff26af1db6f64d070698019460302", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-25.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-stable_2": { "locked": { "lastModified": 1772822230, "narHash": "sha256-yf3iYLGbGVlIthlQIk5/4/EQDZNNEmuqKZkQssMljuw=", @@ -361,8 +475,9 @@ "root": { "inputs": { "agenix": "agenix", + "attic": "attic", "disko": "disko", - "flake-parts": "flake-parts", + "flake-parts": "flake-parts_2", "git-hooks-nix": "git-hooks-nix", "golink": "golink", "home-manager": "home-manager", @@ -374,7 +489,7 @@ "nix-index-database": "nix-index-database", "nixos-hardware": "nixos-hardware", "nixpkgs": "nixpkgs", - "nixpkgs-stable": "nixpkgs-stable", + "nixpkgs-stable": "nixpkgs-stable_2", "systems": "systems", "zx-dev": "zx-dev" } diff --git a/flake.nix b/flake.nix index 2b49ddb7..3224d4c4 100644 --- a/flake.nix +++ b/flake.nix @@ -43,6 +43,10 @@ inputs.nixpkgs.follows = "nixpkgs"; inputs.systems.follows = "systems"; }; + attic = { + url = "github:zhaofengli/attic"; + inputs.nixpkgs.follows = "nixpkgs"; + }; zx-dev = { url = "github:stackptr/zx.dev"; inputs.nixpkgs.follows = "nixpkgs"; @@ -115,6 +119,7 @@ nixConfig = { experimental-features = ["nix-command" "flakes"]; extra-substituters = [ + "https://cache.zx.dev" "https://stackptr.cachix.org" ]; extra-trusted-public-keys = [ diff --git a/hosts/glyph/services/attic.nix b/hosts/glyph/services/attic.nix new file mode 100644 index 00000000..19e2d1ae --- /dev/null +++ b/hosts/glyph/services/attic.nix @@ -0,0 +1,36 @@ +{config, ...}: { + age.secrets.attic-credentials = { + file = ./../secrets/attic-credentials.age; + mode = "440"; + owner = "atticd"; + group = "atticd"; + }; + + services.atticd = { + enable = true; + environmentFile = config.age.secrets.attic-credentials.path; + + settings = { + listen = "[::]:8199"; + + database.url = "sqlite:///var/lib/atticd/server.db?mode=rwc"; + + storage = { + type = "local"; + path = "/var/lib/atticd/storage"; + }; + + chunking = { + nar-size-threshold = 65536; + min-size = 16384; + avg-size = 65536; + max-size = 262144; + }; + + garbage-collection = { + interval = "12 hours"; + default-retention-period = "30 days"; + }; + }; + }; +} diff --git a/hosts/glyph/services/default.nix b/hosts/glyph/services/default.nix index 4dc145b3..be23d643 100644 --- a/hosts/glyph/services/default.nix +++ b/hosts/glyph/services/default.nix @@ -4,6 +4,7 @@ ... }: { imports = [ + ./attic.nix ./avahi.nix ./dns.nix ./filebrowser.nix @@ -84,6 +85,7 @@ rc.backup = { enable = true; paths = [ + "/var/lib/atticd/server.db" "/var/lib/basic-memory" "/var/lib/open-webui" "/var/lib/roon-server/backup" diff --git a/hosts/spore/services/web/default.nix b/hosts/spore/services/web/default.nix index 87b2d997..66952981 100644 --- a/hosts/spore/services/web/default.nix +++ b/hosts/spore/services/web/default.nix @@ -86,6 +86,17 @@ proxyWebsockets = true; }; }; + "cache.zx.dev" = { + forceSSL = true; + useACMEHost = "zx.dev"; + locations."/" = { + proxyPass = "http://glyph.rove-duck.ts.net:8199"; + extraConfig = '' + client_max_body_size 0; + proxy_read_timeout 300; + ''; + }; + }; "jellyfin.zx.dev" = { forceSSL = true; useACMEHost = "zx.dev"; diff --git a/lib/hosts.nix b/lib/hosts.nix index 2d37a484..68407e57 100644 --- a/lib/hosts.nix +++ b/lib/hosts.nix @@ -11,6 +11,7 @@ inputs @ { llm-profile, nix-index-database, zx-dev, + attic, disko, golink, ... @@ -76,6 +77,7 @@ inputs @ { { nixpkgs.overlays = overlays; } + attic.nixosModules.atticd golink.nixosModules.default zx-dev.nixosModules.default ]; diff --git a/lib/secrets/glyph.nix b/lib/secrets/glyph.nix index c9b2dc6a..f45bd768 100644 --- a/lib/secrets/glyph.nix +++ b/lib/secrets/glyph.nix @@ -8,4 +8,5 @@ in { "hosts/glyph/secrets/context7-api-key.age".publicKeys = keys; "hosts/glyph/secrets/open-webui-env.age".publicKeys = keys; "hosts/glyph/secrets/graphite-auth-token.age".publicKeys = keys; + "hosts/glyph/secrets/attic-credentials.age".publicKeys = keys; } From 2aa5e18d64d9d63de0a030ab4f5aa929ef9350a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey=20=28they/them=29?= Date: Tue, 10 Mar 2026 14:08:26 -0700 Subject: [PATCH 2/3] attic-credentials --- hosts/glyph/secrets/attic-credentials.age | Bin 0 -> 426 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 hosts/glyph/secrets/attic-credentials.age diff --git a/hosts/glyph/secrets/attic-credentials.age b/hosts/glyph/secrets/attic-credentials.age new file mode 100644 index 0000000000000000000000000000000000000000..7d24076457e09d98cfeee3493c5372b812e7eb20 GIT binary patch literal 426 zcmYdHPt{G$OD?J`D9Oyv)5|YP*Do{V(zR14F3!+RO))YxHMCSH3NF$va#ScR_B3(~ zOEb@PPtFa_32?P6^9U{r@TsZ_G4=HfvZ(M%NlVRg^e8c}DCR1xNXp9b_sa4!DvZny zs|rkv%<~8d&MXKmDz;4Z%=Gi~2~9RFax3vGGC{Y^*fl&OFHoVx$-ucXrNA+_+_xw- zEyy{vD8jPRBO)!;DL>86GB>lRGTW=rEy^j>&6CT-G9WRnGSJP_C_SqvGT7TB-6bPA zAjmhkpvcA5voy*xpeVp1N8c#aBAH89S69K)D7Y-d)i5$GH_$J`$J4~wtT?aGtTN2S zH#ES!B00$0%P-NbG$q%~AfKy>H`c8vZ`RqwG{33SQr}#vJLMa^^>A(z!yUaP8zs)I zxT_{+{n|Ls`o!vcCvPoS5_QPC*~((Qn2dDJT1CgQA& z-`0i4?6(KX>Kv=!p1w}pagLR4w-;xXa>8luo42<4*cvZisg<@uH)zWDV!QjB7$ Date: Tue, 10 Mar 2026 14:28:18 -0700 Subject: [PATCH 3/3] feat(attic): add cache signing public key to trusted keys Add the Attic cache signing key (main:sbkS1Xz6P4g66iyttRGj/...) to extra-trusted-public-keys so local builds trust artifacts from cache.zx.dev. Co-Authored-By: Claude Opus 4.6 --- flake.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/flake.nix b/flake.nix index 3224d4c4..d90c6220 100644 --- a/flake.nix +++ b/flake.nix @@ -123,6 +123,7 @@ "https://stackptr.cachix.org" ]; extra-trusted-public-keys = [ + "main:sbkS1Xz6P4g66iyttRGj/o8aPODE6bVG9oKT98/ULKI=" "stackptr.cachix.org-1:5e2q7OxdRdAtvRmHTeogpgJKzQhbvFqNMmCMw71opZA=" ]; };