Skip to content

Cache server

jdx/mr-boxington-cache is the self-hostable remote cache server. It implements version 1 of the mbx action-cache protocol — immutable blobs, atomic action-result commits, namespace isolation, and streaming blob packs — and also serves mise's task cache. Any server implementing the protocol works with mbx; this page documents the reference implementation.

Run it

The repository's development stack starts the service, PostgreSQL, and MinIO:

sh
docker compose up --build

For a standalone instance with filesystem storage:

sh
cargo install mbx-cache --locked
mbx-cache \
  --allow-anonymous \
  --data-dir ./data \
  --listen 127.0.0.1:8080

Anonymous access is intended only for a trusted local network. Production installations should terminate TLS at an ingress or proxy and configure tokens. The repository ships a Helm chart for horizontally scaled Kubernetes deployments and a Terraform-managed single-host example.

Clients connect as described in remote cache: a [remote] URL, a required namespace, and a token or OIDC audience.

Configuration

Every option has a matching environment variable and CLI flag; run mbx-cache --help for the complete list.

Environment variableDefaultPurpose
MBX_CACHE_LISTEN0.0.0.0:8080Listen address
MBX_CACHE_STORAGEfilesystemfilesystem or s3
MBX_CACHE_DATA_DIR/var/lib/mbx-cacheFilesystem blob root
MBX_CACHE_DATABASE_URLmemory://PostgreSQL URL or development memory store
MBX_CACHE_S3_BUCKETRequired for S3 storage
MBX_CACHE_S3_PREFIXv1Object-key prefix
MBX_CACHE_S3_ENDPOINTAWS defaultS3-compatible endpoint
MBX_CACHE_S3_REGIONus-east-1S3 region
MBX_CACHE_S3_PATH_STYLEfalseEnable for MinIO and similar services
MBX_CACHE_TOKENS_JSONStatic token grants
MBX_CACHE_OIDC_PROVIDERS_JSONTrusted OIDC providers and claim grants
MBX_CACHE_ALLOW_ANONYMOUSfalseAllow access without configured grants
MBX_CACHE_MAX_BLOB_BYTES5368709120Maximum upload size

AWS credentials use the standard SDK credential chain, including environment variables, workload identity, ECS, and EC2 roles.

Authorization

Authorization is deny-by-default. Namespace patterns may be an exact name, *, or a prefix ending in /*.

Static tokens

MBX_CACHE_TOKENS_JSON is an array of grants:

json
[
  {
    "token": "replace-with-a-secret",
    "read": ["acme/*", "public"],
    "write": ["acme/project-a"]
  }
]

Rotate tokens by deploying the old and new grants together, moving clients to the new token, then removing the old grant. Inject the JSON through a secret rather than writing it into deployment configuration.

OIDC

OIDC lets CI use short-lived identity tokens instead of stored secrets. Configure trusted issuers, acceptable audiences, and claim-based grants:

json
[
  {
    "issuer": "https://token.actions.githubusercontent.com",
    "audiences": ["https://cache.example.com"],
    "rules": [
      {
        "claims": {
          "repository": "acme/backend",
          "repository_owner_id": "12345"
        },
        "read": ["acme/backend"],
        "write": ["acme/backend"]
      }
    ]
  }
]

The server discovers the issuer's JWKS endpoint and verifies the signature, issuer, audience, expiry, not-before time, and subject before applying the first matching rule. Rules are alternatives; every claim within a rule must match exactly. Pin stable identity claims such as GitHub's numeric repository_owner_id alongside the repository name — names can be reclaimed, the ID cannot — and add ref or environment claims when only a narrower workflow identity should write. Symmetric JWT algorithms are never accepted.

On the client side, mbx acquires the GitHub Actions job token itself: set MBX_REMOTE_OIDC_AUDIENCE, or use jdx/mr-boxington-action with backend: server as shown in GitHub Action.

Operations

Blob storage is expired by the bucket's own lifecycle rule; the server removes the metadata those objects leave behind and exits without serving:

sh
mbx-cache --sweep-metadata-older-than-days 35

Keep the sweep age longer than the storage lifecycle so objects expire first. A dangling reference is never fatal — a client that cannot fetch a blob treats the action as a miss and recompiles.

Multiple stateless replicas can run against the same PostgreSQL database and S3 bucket. Readiness and liveness probes use /v1/status, and /metrics exposes Prometheus counters for actions, blobs, and blob-pack transfers with fixed, low-cardinality labels — namespaces, tokens, and digests never appear as labels. The server has no deletion endpoint; retention and disaster recovery are administrative concerns (back up PostgreSQL, use bucket versioning or replication as required).

A server advertising action promises can coordinate identical cold compilations across replicas. Claims are short-lived database leases scoped by namespace and invocation digest; completion is accepted only after the named action result is durable. This state is coordination rather than cache content: abandoned claims expire, completed promises are immutable for their retention window, and no promise endpoint bypasses the namespace's write authorization.

Protocol

The wire protocol — endpoints, media types, canonical hashing, and the evolution rules — is documented in protocol compatibility. The repository README covers implementation details beyond it, including blob-pack framing and validation behavior.

MIT LicenseCopyright © 2026jdx.dev