Push to Juspay's OSS Nix cache
Wire a GitHub repo into the shared Attic cache at cache.nixos.asia/oss. CI builds the flake's default package on every push to the default branch and pushes the resulting closure, warming both CI and local nix builds for everyone. Reference: juspay/kolu#1731, #1733.
Three pieces are needed: the cache as a substituter (so the repo pulls from it), a workflow (so CI pushes to it), and the ATTIC_TOKEN secret (so the push authenticates). Do all three.
Initial setup questionnaire
Before touching any files, use the Ask tool (AskUserQuestion) to settle the trigger scope — which branches should trigger a build-and-push?
- Default branch only — push on the default branch (
main/master) pluspull_requestandworkflow_dispatch. Warms the cache from merged, reviewed code. Recommended for most repos. - All branches — push on every branch too, so feature branches warm the cache. Costs more CI minutes and can push closures from unreviewed code.
Assume the user already has an ATTIC_TOKEN push token (they obtain it from the cache admin — Juspay infra — via atticadm make-token, scoped to the oss cache). Don't block on it here; step 3 confirms it's set on the repo and loops until it is.
1. Add the cache as a substituter
In the repo's top-level flake.nix, add (or extend) nixConfig:
nixConfig = {
extra-substituters = "https://cache.nixos.asia/oss";
extra-trusted-public-keys = "oss:KO872wNJkCDgmGN3xy9dT89WAhvv13EiKncTtHDItVU=";
};
2. Add the workflow
Set the on: triggers to match the trigger scope answer from the questionnaire. The template below is the default-branch-only variant.
Already have a nix build workflow? Piggyback on it.
If the repo already has a workflow that runs nix build (e.g. a CI job), don't add a second one — just drop the ryanccn/attic-action step into the existing job, before the build step, so its end-of-job hook pushes whatever that job builds:
- uses: ryanccn/attic-action@5635a15ef0c5462194ffbd05d1daeddc74625c3a # v0.5.0
with:
endpoint: https://cache.nixos.asia
cache: oss
token: ${{ secrets.ATTIC_TOKEN }}
The action pushes every store path realised during the job, so no explicit push step is needed. Only create the standalone nix-cache.yml below when there's no existing build job to hook into. (Note: if the existing job uses the DeterminateSystems installer rather than nix-quick-install-action, verify attic-action still finds a new-enough Nix for nix profile add.)
Standalone workflow
Create .github/workflows/nix-cache.yml. ryanccn/attic-action handles install, login, substituter config, and pushing every store path the job produced at job end.
# Build the default package on linux + darwin and push each closure to the
# shared Attic cache (https://cache.nixos.asia/oss).
name: nix-cache
on:
push:
branches: [master] # set to the repo's default branch
pull_request:
workflow_dispatch:
# Least privilege — this job only needs the checkout and ATTIC_TOKEN secret.
permissions:
contents: read
concurrency:
group: nix-cache-${{ github.ref }}
cancel-in-progress: true
jobs:
build-and-push:
strategy:
fail-fast: false
matrix:
# ubuntu-latest → x86_64-linux; macos-latest → aarch64-darwin.
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
# Pin by commit (CodeQL: unpinned third-party action). v35 → 9f63be7.
# v35 defaults to Nix 2.34.7, new enough for attic-action's `nix profile add`.
- uses: nixbuild/nix-quick-install-action@9f63be77f412a248c9d9a65a4c82cf066cdf8f0c # v35
with:
nix_conf: |
extra-substituters = https://cache.nixos.asia/oss
extra-trusted-public-keys = oss:KO872wNJkCDgmGN3xy9dT89WAhvv13EiKncTtHDItVU=
accept-flake-config = true
- uses: ryanccn/attic-action@5635a15ef0c5462194ffbd05d1daeddc74625c3a # v0.5.0
with:
endpoint: https://cache.nixos.asia
cache: oss
token: ${{ secrets.ATTIC_TOKEN }}
- name: Build
run: nix build -L --print-out-paths
Rules:
- For default branch only, set the
push.branchesfilter to the repo's actual default branch (kolu usesmaster; many repos usemain). For all branches, drop thebranches:filter sopush:fires everywhere. - Keep both actions pinned by commit SHA (CodeQL flags unpinned third-party actions). The
# v35/# v0.5.0trailing comments record the tag. Prefer the SHAs above unless a newer release is needed; do not replace them with floating tags.
3. Set the ATTIC_TOKEN secret
The token is a repo secret you can't set for the user — it's their token, entered interactively so it stays out of shell history. Give them this command and ask them to run it, then confirm:
gh secret set ATTIC_TOKEN --repo <owner>/<repo>
gh prompts for the value interactively (never paste the token into a command line that gets logged). After they say they've run it, re-check and re-ask until it's present:
gh secret list --repo <owner>/<repo> # ATTIC_TOKEN should appear
If it's missing, tell them and ask again — loop until gh secret list shows ATTIC_TOKEN. The workflow fails loudly without this secret; there is no silent skip.
4. Open a PR
Commit the flake.nix and workflow changes on a branch and open a PR:
gh pr create --title "Push Nix builds to the OSS Attic cache" --body "..."
The workflow's pull_request trigger means the run fires on the PR itself, so you get a green/red signal before merge — no need to merge blind.
5. Monitor the run to success
Watch the run and don't declare success until it's actually green:
gh run list --workflow nix-cache.yml --limit 1 # find the run
gh run watch <run-id> # stream to completion
If it fails, diagnose with gh run view <run-id> --log-failed and fix. Common causes:
ATTIC_TOKENmissing / unauthorized — the secret isn't set or lacks push scope; loop back to step 3.- Build failure —
nix builditself is broken; that's a repo problem, not a cache one. Fix the build. - Unpinned-action / permissions warnings — check the pins and
permissions:block from step 2.
A green run means the closure was pushed. To confirm the pull side, on another machine run nix build and check the log shows paths fetched from cache.nixos.asia/oss.