Sitelet https://github.com/underwhelmingperformance/cupboard
Skip to content

Latest commit

 

History

1,481 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cupboard

cupboard is a Nix binary cache that you run on your own Cloudflare account.

  • It runs on Cloudflare Workers and R2, on the free tier. There are no servers to run, and a small deployment costs nothing.
  • One deployment hosts many tenants. Each tenant has its own caches, signing key, credentials and retention settings, so one deployment can serve every team in an organisation, and no tenant can see another's data.
  • A tenant can have as many caches as it needs, public or private. Private reads need authorised credentials. Each cache can have its own static read credential.
  • CI publishes without a stored push credential. A GitHub Actions job signs in with the OIDC token that GitHub already gives it. A job can also use OIDC for private reads when its trust rules permit content reads. Static read credentials remain available. See Private reads in CI. A trust rule on your tenant says which repository, branch or pull request to accept, and what that job may do. The signing key never leaves the server.
  • Store paths are kept by named retention roots, which can expire, and a cache can also keep paths for a grace period. Garbage collection deletes every path that no root or grace period keeps, so a cache that CI fills every day doesn't grow forever.
  • The reusable workflow gives every pull request its own cache, reuses those builds when the change reaches main, and signs build provenance for what it builds.
  • It's a standard Nix binary cache. Any Nix that can decompress zstd can substitute from it, with nothing extra installed.

Why cupboard compares it with Cachix, Attic and a plain bucket, and lists what it can't do yet.

Quick start

  1. Install the CLI:

    nix profile install github:underwhelmingperformance/cupboard/vX.Y.Z

    Replace vX.Y.Z with a published release tag, and use that same tag for the reusable workflow below. There are also prebuilt archives on the releases page. Installing the CLI covers both, including how to verify a release.

  2. In the Cloudflare dashboard, enable R2, create a bucket called cupboard-blobs, and create an R2 API token that can read and write it. Then deploy:

    cupboard init --instance-name cupboard

    This walkthrough uses browser sign-in and an existing R2 key. With a Cloudflare API token that can manage account tokens, init can create the bucket and a key scoped to writes in that bucket. See R2 credentials for that alternative.

    init signs you in to Cloudflare through your browser, creates the Workers and their storage, and creates your first tenant. The first init from a terminal also makes you the deployment's admin, after you confirm the identity that it shows. Run it from a terminal: a first init without one leaves the deployment without an admin. init finishes by printing the tenant's read credential and the lines to add to nix.conf. Deploying cupboard explains each step and each choice.

    A later init upgrades the deployment and needs the admin's token. At a terminal it signs you in if needed. From CI, it needs a control-plane trust rule, as Updating from CI describes.

  3. Sign in to the new tenant, then push something. init saves a session for the deployment, and pushing needs a session for the tenant:

    cupboard login https://cupboard.example.workers.dev/t/acme
    cupboard push https://cupboard.example.workers.dev/t/acme ./result
  4. Add the lines that init printed to nix.conf. If the cache is private, also save the netrc line that init printed, which contains the read credential, in a netrc file outside the Nix store. Using a cache shows where the lines go on NixOS, nix-darwin, Home Manager and plain Nix installs, and Private caches shows how to give Nix the credential.

Publishing from GitHub Actions

First, add the trust rules and reuse view for the repository to your tenant:

cupboard github setup https://cupboard.example.workers.dev/t/acme \
  --repo acme/app \
  --workflow-ref 'underwhelmingperformance/cupboard/.github/workflows/cupboard-flake-publish.yml@refs/tags/v*'

The workflow builds the targets that the flake lists in its cupboardOutputs output, so define that first; the quickstart shows how. Then add a workflow to the repository that calls cupboard's. This is the core of it:

on:
  pull_request:
    types: [opened, synchronize, reopened, closed]
  push:
    branches: [main]

jobs:
  publish:
    permissions:
      attestations: write
      contents: read
      id-token: write
    uses: underwhelmingperformance/cupboard/.github/workflows/cupboard-flake-publish.yml@vX.Y.Z
    with:
      url: https://cupboard.example.workers.dev/t/acme
      preset: pull-request-and-branch
      trusted-public-key: cupboard-acme-1:...

Every pull request now builds the flake's outputs into its own cache, and every push to main publishes to the default cache, reusing the pull request's builds where they match. Copy the complete workflow file from Add the workflow rather than this excerpt: it also skips pull requests from forks, which the preset refuses, and cancels a pull request's previous run when a new commit arrives.

How it's built

The control plane is one Worker with a D1 database. Each tenant is a Durable Object, which keeps the tenant's narinfos, roots, keys and trust rules in its own SQLite database. NARs and attestations are in R2, stored once however many tenants publish them. R2 doesn't charge for egress, so serving builds costs storage and requests, not bandwidth. An hourly cron job queues garbage collection and key retirement for up to 100 due tenants. An active tenant becomes due when it has work to do or six hours have passed since its maintenance eligibility was last reconciled. Queue delivery, other due tenants and maintenance failures can delay completion. Architecture goes through it in detail.

Status

cupboard is pre-1.0. Releases are numbered v0.0.x, and a release can change the format of stored data. When one does, upgrading runs a staged migration, and once tenants have migrated you can't roll back. Why cupboard compares it with the alternatives and lists the things it can't do yet.

Documentation

The documentation index lists every page. The pages to start with are:

Security

To report a vulnerability, see the security policy. The security model describes who cupboard trusts and how tenants are kept apart.

Licence

cupboard is licensed under the GNU Affero General Public License v3.0 or later.

About

A place for all your nixy bits

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages