cachew

module
v0.8.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 10, 2026 License: Apache-2.0

README

Cachew

Cachew (pronounced "cashew") is a tiered, protocol-aware, caching HTTP proxy for software engineering infrastructure. It understands higher-level protocols (Git, Docker, Go modules, etc.) and makes smarter caching decisions than a naive HTTP proxy.

Strategies

Git

Caches Git repositories with two complementary techniques:

  1. Snapshots — periodic .tar.zst archives that restore 4–5x faster than git clone.
  2. Pack caching — passthrough caching of packs from git-upload-pack for incremental pulls.

Redirect Git traffic through cachew:

[url "https://cachew.example.com/git/github.com/"]
  insteadOf = https://github.com/

Restore a repository from a snapshot (with automatic delta bundle to reach HEAD):

cachew git restore https://github.com/org/repo ./repo
git {
  snapshot-interval = "1h"
  repack-interval   = "1h"
}
GitHub Releases

Caches public and private GitHub release assets. Private orgs use a token or GitHub App for authentication.

URL pattern: /github-releases/{owner}/{repo}/{tag}/{asset}

github-releases {
  token        = "${GITHUB_TOKEN}"
  private-orgs = ["myorg"]
}
Go Modules

Go module proxy (GOPROXY-compatible). Private modules are fetched via git clone.

URL pattern: /gomod/...

export GOPROXY=http://cachew.example.com/gomod,direct
gomod {
  proxy         = "https://proxy.golang.org"
  private-paths = ["github.com/myorg/*"]
}
Hermit

Caches Hermit package downloads. GitHub release URLs are automatically routed through the github-releases strategy.

URL pattern: /hermit/{host}/{path...}

hermit {}
Artifactory

Caches artifacts from JFrog Artifactory with host-based or path-based routing.

artifactory "example.jfrog.io" {
  target = "https://example.jfrog.io"
}
Host

Generic reverse-proxy caching for arbitrary HTTP hosts, with optional custom headers.

host "https://ghcr.io" {
  headers = {
    "Authorization": "Bearer QQ=="
  }
}

host "https://w3.org" {}

Optional fallback blocks select trusted mirrors in declaration order. Cachew tries the next mirror only after a 429, a 5xx response, or a transport error. It never retries a 404 against a mirror: the primary is authoritative and mirrors can lag. Cancellation stops retries. Failures after response streaming starts cannot retry.

host "https://packages.example.com" {
  fallback "https://mirror.example.com/archive" {
    path-prefix = "/releases"
  }
}

This maps /releases/pkg.tar.gz to https://mirror.example.com/archive/pkg.tar.gz. path-prefix defaults to / and matches complete path segments. Unmatched paths do not use that fallback. Query strings are preserved.

Fallbacks receive the same request headers and are trusted like the primary for shared cache content. Configure only mirrors that serve byte-identical content. Successful responses use the primary URL's cache key, regardless of which upstream served them. Non-200 and incomplete responses are not cached.

HTTP Proxy

Caching proxy for clients that use absolute-form HTTP requests (e.g. Android sdkmanager --proxy_host).

proxy {}
OCI

Read-only pull-through cache for Docker/OCI image registries, with one block per registry. Pushes are rejected.

URL patterns: /oci/{registry}/v2/..., and /v2/... on each of hosts.

oci "docker.io" {
  upstream = "https://registry-1.docker.io"
  hosts    = ["docker.cachew.example.com"]
  rewrite "^([^/]+)$" { replacement = "library/$1" }
}
  • Blobs and digest-addressed manifests are checked against their digest and cached until evicted.
  • Tag manifests are rechecked with a HEAD request once they are older than tag-ttl (default 5m). If the registry is unavailable or rate limiting, the cached copy is served.
  • username and password authenticate to the upstream for all clients. Clients' own credentials are never forwarded. Without them, access is anonymous.
  • blob-chunk-size-mb (default 32) and blob-concurrency (default 4) control parallel range requests when filling large blobs.
  • Tag lists and referrers are not cached.

containerd can use the path-prefixed form, e.g. in /etc/containerd/certs.d/docker.io/hosts.toml:

server = "https://registry-1.docker.io"

[host."https://cachew.example.com/oci/docker.io/v2"]
  capabilities = ["pull", "resolve"]
  override_path = true

Docker and Podman cannot use a path prefix, so they need a hostname from hosts that resolves to cachew. Docker's registry-mirrors setting only applies to Docker Hub. In /etc/docker/daemon.json:

{ "registry-mirrors": ["https://docker.cachew.example.com"] }

Cache Backends

Multiple backends can be configured simultaneously — they are automatically combined into a tiered cache. Cache blocks are ordered from lowest/nearest to highest/authoritative. Reads check each tier in order and backfill lower tiers on a hit. Writes go to all tiers in parallel. Replica invalidations evict only non-authoritative tiers; the final cache block is authoritative. Tiered caches use the metadata backend to track authoritative ETags and invalidate stale lower-tier copies before falling through to the authoritative tier.

Memory

In-memory LRU cache.

memory {
  limit-mb = 1024   # default
  max-ttl  = "1h"   # default
}
Disk

On-disk LRU cache with TTL-based eviction.

disk {
  limit-mb = 250000
  max-ttl  = "8h"
}
S3

S3-compatible object storage (AWS S3, MinIO, etc.).

s3 {
  bucket   = "my-cache-bucket"
  endpoint = "s3.amazonaws.com"
  region   = "us-east-1"
}

Request Coalescing

Concurrent requests for the same uncached object share one upstream fetch, and every client streams the response while it downloads. The fetch continues and fills the cache even if every client disconnects.

Error responses are shared only when HTTP caching rules allow reuse, and for at most error-window.

spool {
  dir          = "${CACHEW_STATE}/spools"  # Response files for backends that cannot be read while writing
  fill-timeout = "30m"                     # 0 disables the limit
  error-window = "5s"                      # 0 shares errors only with requests already waiting
}

Upstream Rate Limits

After an upstream returns 429, cachewd pauses HTTP requests to that outbound hostname across strategies, schemes, and ports until a cooldown ends. Each redirect destination is checked independently. Cache hits and other hostnames remain available. OCI blob, manifest, authentication, and pass-through requests use the same shared cooldowns.

Configured fallbacks are tried on the first 429 and on subsequent cache misses while the primary is cooling. A mirror on another hostname remains available until it returns its own 429; its 404 or 5xx does not start a cooldown. Requests to cooling hosts get a local 429 with Retry-After and Cache-Control: no-store. Cooldowns do not schedule retries.

The cooldown uses the upstream's Retry-After (seconds or HTTP date) when it is valid, and upstream-backoff otherwise. Cooldowns are held in memory per process and apply to clients that use the shared HTTP transport, not subprocess traffic. Other replicas and requests already in flight are unaffected.

upstream-backoff = "1m"  # Must be positive

Authorization (OPA)

Cachew uses Open Policy Agent for request authorization. The default policy allows all requests from localhost and restricts remote access to non-admin paths (/api/*, /admin/*).

Policies must be in package cachew.authz and define an allow rule. If allow is true the request proceeds; otherwise it is rejected with 403.

opa {
  policy = <<EOF
    package cachew.authz
    default allow := false
    allow if input.headers["authorization"]
  EOF
}

Or reference an external file with optional data:

opa {
  policy-file = "./policy.rego"
  data-file   = "./opa-data.json"
}

Input fields: input.method, input.path (string array), input.headers, input.remote_addr (includes port — use startswith to match by IP).

Testing policies

The test field holds a Rego test module that is run against the policy when cachewd starts. Any rule prefixed with test_ is executed; if a test fails, cachewd exits.

opa {
  policy = <<EOF
    package cachew.authz
    default allow := false
    allow if input.method == "POST"
  EOF
  test = <<EOF
    package cachew.authz_test
    import data.cachew.authz

    test_post_allowed if authz.allow with input as {"method": "POST"}
    test_get_denied if not authz.allow with input as {"method": "GET"}
  EOF
}

GitHub App Authentication

For private Git repositories and GitHub release assets, configure a GitHub App:

github-app {
  app-id           = "12345"
  private-key-path = "./github-app.pem"
  installations    = { "myorg": "67890" }
}

Installations can also be discovered dynamically via the GitHub API.

CLI

Server (cachewd)
cachewd --config cachew.hcl
cachewd --schema  # print config schema
Client (cachew)
# Object operations
cachew get <namespace> <key> [-o file]
cachew put <namespace> <key> [file] [--ttl 1h]
cachew stat <namespace> <key>
cachew delete <namespace> <key>
cachew namespaces

# Directory snapshots
cachew save <namespace> <directory> [paths...] (--key <key> | -H <glob>) [--ttl 1h] [--exclude pattern]
cachew restore <namespace> <directory> (--key <key> | -H <glob>)  # exit 0 hit, 2 miss, 1 error

# Git
cachew git restore <repo-url> <directory> [--no-bundle]

Global flags: --url (CACHEW_URL), --authorization (CACHEW_AUTHORIZATION), --platform (prefix keys with os-arch), --daily/--hourly (prefix keys with date).

Observability

log {
  level = "info"  # debug, info, warn, error
}

metrics {
  service-name = "cachew"
}

Admin endpoints: /_liveness, /_readiness, PUT /admin/log/level, /admin/pprof/.

GitHub App installations are checked at startup and every minute via /rate_limit. The poller includes dynamically discovered installations and the configured fallback organization.

Metric Type Labels
github.ratelimit_remaining Gauge app, installation_id, owner, resource
github.ratelimit_limit Gauge app, installation_id, owner, resource
github.ratelimit_used Gauge app, installation_id, owner, resource
github.api_calls Counter owner, operation

app is the configured App ID. owner identifies the installation actually used, including fallback credentials. Installation discovery uses owner=unknown because it authenticates as the App before resolving an installation. API calls for release metadata/assets, installation discovery, token creation and quota polling are counted. Responses with less than 10% of their API quota remaining produce a warning. Static release tokens receive API call counts and warnings, but are not polled for quota gauges.

Quota gauges describe the installation's shared API budget; do not sum them across replicas. They do not track Git transport or LFS limits. Polling does not consume the primary API budget, but can count toward GitHub's secondary limits.

Artifact cache metrics

Generic artifact handlers (host, Artifactory, Hermit, HTTP proxy, OCI blobs and manifests, and GitHub release downloads) export:

OpenTelemetry instrument Type Extra labels
cachew.origin.requests Counter status_code, error_type
cachew.origin.request_duration_seconds Histogram status_code, error_type
cachew.cache.fills Counter purpose, result
cachew.cache.tier_writes Counter backend, tier, purpose, result

All carry strategy and registry. Registry values come from configured host/Artifactory targets or fixed strategy labels; dynamic proxy/Hermit hosts use other. URLs, paths, cache keys, credentials and caller namespaces are not labels. Prometheus converts dots to underscores and appends _total to counters.

The HTTP server metrics of these handlers carry cachew.cache.result (hit, miss, coalesced or error). Hits also carry cachew.cache.source, the backend that served them (disk, s3, memory, remote, or other), after stale and conditional probes.

Origin metrics count HTTP exchanges that reach an origin through http.DefaultTransport, including redirect hops and transport failures. Requests rejected during an upstream 429 cooldown are not counted. status_code=none means no HTTP response; error_type is none, transport, timeout, or canceled. Duration ends at response headers or transport failure. Registry identifies the logical artifact provider, including its redirects, rather than each physical destination. These metrics do not count every low-level connection retry, Git transport, Go-module fetch, GitHub metadata lookup, OCI tag-list or referrer pass-through, or S3 operation. OCI registry ping and token exchanges are counted, because they go to the registry for the artifact. Internal Hermit forwarding to another Cachew route is excluded from origin/fill counts; the receiving handler measures its own work.

Fills record a terminal outcome, rather than treating an origin 200 as successful publication. purpose separates origin, backfill and ranged heal. Results distinguish successful completion from creation, write, commit, transfer or read errors, cancellation, and superseded heals. Tier writes report backend outcomes separately: a failed S3 write can coexist with a successful disk write. Early close or canceled backfill is not a successful fill. Client responses continue normally when cache writes fail.

Use cachew.cache.result and cachew.cache.source to compare local/shared hits and misses, origin requests by status to measure dependency traffic, and fills/tier writes to find failed publication. An origin error percentage must use origin exchanges as its denominator; client response percentages include cache hits. Idle periods have no percentage.

Full Configuration Example

state = "./state"
bind  = "0.0.0.0:8080"
url   = "http://cachew.example.com:8080/"

log {
  level = "info"
}

opa {
  policy = <<EOF
    package cachew.authz
    default allow := false
    allow if startswith(input.remote_addr, "127.0.0.1:")
  EOF
}

metrics {}

github-app {
  app-id           = "12345"
  private-key-path = "./github-app.pem"
}

git-clone {}

git {
  snapshot-interval = "1h"
  repack-interval   = "1h"
}

github-releases {
  token        = "${GITHUB_TOKEN}"
  private-orgs = ["myorg"]
}

gomod {
  proxy         = "https://proxy.golang.org"
  private-paths = ["github.com/myorg/*"]
}

hermit {}

host "https://ghcr.io" {
  headers = {
    "Authorization": "Bearer ${GHCR_TOKEN}"
  }
}

disk {
  limit-mb = 250000
  max-ttl  = "8h"
}

proxy {}

Directories

Path Synopsis
Package client provides a standalone HTTP client for the Cachew cache server.
Package client provides a standalone HTTP client for the Cachew cache server.
cmd
cachew command
cachewd command
internal
cache
Package cache provides a framework for implementing and registering different cache backends.
Package cache provides a framework for implementing and registering different cache backends.
config
Package config loads HCL configuration and uses that to construct the cache backend, and proxy strategies.
Package config loads HCL configuration and uses that to construct the cache backend, and proxy strategies.
featureflags
Package featureflags provides typed feature flags backed by environment variables.
Package featureflags provides typed feature flags backed by environment variables.
gitclone
Package gitclone provides reusable git clone management with lifecycle control, concurrency management, and large repository optimizations.
Package gitclone provides reusable git clone management with lifecycle control, concurrency management, and large repository optimizations.
githubapp
Package githubapp provides GitHub App authentication and token management.
Package githubapp provides GitHub App authentication and token management.
httputil
Package httputil contains utilities for HTTP clients and servers.
Package httputil contains utilities for HTTP clients and servers.
jobscheduler
Package jobscheduler provides a means to schedule work across multiple queues while limiting overall work.
Package jobscheduler provides a means to schedule work across multiple queues while limiting overall work.
logging
Package logging provides logging configuration and utility functions.
Package logging provides logging configuration and utility functions.
metadatadb
Package metadatadb provides a metadata store for coordinating state across cachew replicas.
Package metadatadb provides a metadata store for coordinating state across cachew replicas.
metadatadb/s3
Package s3 implements the S3 metadata backend as an append-only journal of immutable op segments plus a compacted rollup snapshot, replayed in a canonical order all replicas agree on.
Package s3 implements the S3 metadata backend as an append-only journal of immutable op segments plus a compacted rollup snapshot, replayed in a canonical order all replicas agree on.
opa
Package opa provides OPA-based HTTP request authorization middleware.
Package opa provides OPA-based HTTP request authorization middleware.
reaper
Package reaper provides a background zombie process reaper.
Package reaper provides a background zombie process reaper.
s3client
Package s3client provides shared S3 connection configuration and minio client construction.
Package s3client provides shared S3 connection configuration and minio client construction.
s3client/s3clienttest
Package s3clienttest provides a reusable MinIO test server via Docker.
Package s3clienttest provides a reusable MinIO test server via Docker.
snapshot
Package snapshot provides streaming directory archival and restoration using tar and zstd.
Package snapshot provides streaming directory archival and restoration using tar and zstd.
spool
Package spool shares one in-flight HTTP response between concurrent requests, which stream it while it is still being produced.
Package spool shares one in-flight HTTP response between concurrent requests, which stream it while it is still being produced.
strategy
Package strategy provides a framework for implementing and registering different caching strategies.
Package strategy provides a framework for implementing and registering different caching strategies.
strategy/git
Package git implements a protocol-aware Git caching proxy strategy.
Package git implements a protocol-aware Git caching proxy strategy.
tracing
Package tracing wires up the OpenTelemetry tracer provider so callers can use the standard otel.Tracer(...) API to create spans.
Package tracing wires up the OpenTelemetry tracer provider so callers can use the standard otel.Tracer(...) API to create spans.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL