Skip to content
fosterstackPublic

About

Self-hosted remote build cache for Gradle and Maven — drop-in replacement for the EOL Develocity Build Cache Node

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

FosterStack Cache

CI CodeQL OpenSSF Scorecard Latest release

A self-hosted, drop-in remote build cache for Gradle — and for Maven through the Apache Maven Build Cache Extension (acceptance-tested on every push and release). Ships as a single static binary or a distroless container image, MIT-licensed, with security patches under a standing policy (SECURITY.md). Every image is keylessly signed (Sigstore); every binary archive is covered by a signed checksums file; both carry SLSA provenance naming the CI run that built them. "Verifying a release" has the commands.

Website: fosterstack.com · Docs: Install · Docker · Kubernetes · Gradle · Maven

Why this exists

Gradle Inc. is discontinuing the free, standalone Develocity Build Cache Node — no further distribution, support, or updates after December 31, 2026. Teams running the free cache node on open-source Gradle (no Develocity subscription) need a maintained, drop-in replacement. FosterStack Cache speaks Gradle's documented HttpBuildCache protocol (a plain content-addressed GET/PUT over HTTP) and the Apache Maven Build Cache Extension's remote HTTP mode (GET/PUT/HEAD) — same server, same core. The Gradle path is acceptance-tested against a real multi-module build in CI, and so is the Maven path: a real multi-module Maven project with the extension (versions 1.2.0 and 1.2.3 ) restores its second build from the remote cache with byte-identical jars, and wrong or missing credentials fail the documented way. The Maven suite is a release-blocking check.

Status

Shipped:

  • Cache server core: content-addressed filesystem blob store, bbolt metadata index, size-capped LRU eviction — all tested.
  • HTTP surface: GET/PUT/HEAD, optional Basic Auth, Prometheus metrics at /metrics, liveness at /healthz.
  • CGO_ENABLED=0 static binary — builds and runs today (see below).
  • CI on every push/PR: tests (race-enabled), go vet, golangci-lint (staticcheck + a repo-wide crypto/md5/crypto/sha1 import ban), gosec, govulncheck, CodeQL, dependency review on PRs, OpenSSF Scorecard, and a public-repo file allowlist.
  • Release pipeline: signed, provenance-attested container images (production, -debug, -fips) on GHCR, plus bare binaries and a signed checksums file. Every release is built only by CI, once. The images are assembled once, and the scans, the acceptance tests and an independent reproducibility rebuild all run on that exact digest; the published images are that same digest, promoted without a rebuild. The daily rescan then re-checks the current release. RELEASING.md describes the pipeline stage by stage.
  • Acceptance-tested against a real multi-module Gradle project on every push/PR — a from-scratch second build must produce real FROM-CACHE hits, with the sample's local build cache disabled so a hit is remote evidence. There is no benchmark suite yet; this is a correctness gate, not a performance number.
  • Deployment docs: Install (binaries + systemd), Docker (with sizing), Kubernetes, Gradle, Maven, and disconnected networks.

Not yet shipped:

  • Production use beyond our own CI. Nobody is running this in a real build pipeline yet except us.
  • A Helm chart. Deploying to Kubernetes today means applying the plain manifests in docs/kubernetes.md.
  • The paid tiers. Single sign-on, high-availability replication, and the license key that unlocks them are not built. Everything in this repository is the free MIT core.
  • A CVE patch commitment you can hold us to. We aim to ship fixes for dependency CVEs within 48 hours of public disclosure. That is a stated intention, not a contractual promise, and it will not be one until the paid tiers exist. What already runs today is the detection half: the current release's images are rescanned daily, so a CVE disclosed against bytes we already shipped raises an issue without anyone remembering to look.

Tracked in this repo's issues, which is also where the roadmap gets argued with. There is no published schedule: the list above is direction, not dates.

There is no waitlist and no signup. Pull the image and run it — the quickstart below is the whole gate.

Each item above names the workflow or command that demonstrates it — Actions history for the CI claims, the commands in RELEASING.md for signatures and provenance. What those commands prove, and what they do not, is stated in RELEASING.md itself.

Full core (eviction, size limits, metrics) is free forever under MIT — see LICENSE. Paid tiers add SSO, HA/replication, a documented CVE SLA, and compliance support on top of the same open core; nothing is withheld from the free tier for security.

The security evidence itself is public and free: SBOMs, SLSA provenance, signatures, VEX statements, and the FIPS 140-3 module certificate number are published with every release and verifiable by anyone, with no account and no purchase. Scan verdicts are visible in the public CI logs; they are not yet attached to releases as durable, digest-bound evidence. The FIPS-mode image is publicly pullable too. What the Compliance tier sells is the authored analysis, the vendor signature, and the hours — never access to the bytes or the evidence.

Quickstart (Docker)

docker run -d -p 8080:8080 ghcr.io/fosterstack/cache:latest
curl localhost:8080/healthz   # -> ok

Pin a specific version for anything beyond a first look — the badge above shows the current one, and docs/docker-deploy.md covers pinning by digest. The same images, same digests, are mirrored at docker.io/fosterstack/cache for tooling that defaults to Docker Hub; GHCR is canonical, and if Docker Hub's anonymous pull limits bite (they key on the puller, not on us), authenticate or pull from GHCR.

That's the whole install. No registration, no license key for the Community tier — pull, run, point your build tool at it (below).

Supported platforms

Every release builds this whole matrix from the same source in the same CI run — there is no primary platform.

Artifact Platforms
Binaries (fscache) linux/amd64, linux/arm64, darwin/amd64, darwin/arm64
FIPS binaries (fscache-fips) linux/amd64, linux/arm64
Container images (prod, -debug, -fips) multi-arch: linux/amd64 + linux/arm64

Image variants

Tag Shell Use
X.Y.Z none Production
X.Y.Z-debug busybox at /busybox/sh Interactive troubleshooting
X.Y.Z-fips none Production, FIPS 140-3 validated crypto module

Production and -fips are distroless: the fscache binary and nothing else, so there is no shell to exec into and no package manager to patch. :debug adds busybox for troubleshooting, and its shell is at /busybox/sh — distroless has no /bin/sh, unlike Debian, Alpine or Wolfi. See the variant docs and troubleshooting.

FIPS is Linux-only on purpose: the compliance buyer it serves deploys on Linux, so a macOS FIPS build would double CI time for a configuration nobody assesses. Container images ship as multi-arch manifests, so docker pull resolves the right image by itself on Apple Silicon and Graviton. Windows is not built and not planned — this is a server that lives next to your CI runners; develop on Windows via WSL2, which is linux/amd64.

Full install instructions, including which archive to download and a systemd unit: docs/install.md.

Debugging a running container

The production and -fips images contain only the fscache binary, with no shell, so you debug them from the outside by attaching a second container that carries the tools. Three ways below. The two Docker ones were run against this image on Oct 9 2026; kubectl debug was checked against the Kubernetes documentation only (details at the end of this section).

Kubernetes: kubectl debug. An ephemeral debug container joins the running pod (stable since Kubernetes v1.25):

kubectl debug -it <pod> --image=busybox:1.37 --target=<container> -- sh
# inside it:
wget -qO- http://127.0.0.1:8080/healthz
ps

--target points the debug container at the fscache container so it can see its processes (the container runtime has to support that). The pod's network is shared, so 127.0.0.1:8080 is the cache. Use any image that already has the tools you need. The debug container inherits the pod's security context: with the Deployment in docs/kubernetes.md (runAsNonRoot, user 65532) it runs as that non-root user, so it cannot install packages (an image such as wolfi-base with apk add needs a pod that allows root). An ephemeral container cannot be changed or removed once added; it goes away with the pod. To debug a modified copy instead, use kubectl debug <pod> -it --copy-to=<new-name> --image=..., and for the node itself kubectl debug node/<node> -it --image=....

Docker: docker debug.

docker debug <container>

This opens a shell in a toolbox attached to the container, even though the image has no shell. It ships with Docker Desktop and was a paid feature (Pro, Team or Business) until Docker Desktop 4.50 (announced 12 Nov 2025), when Docker made it free for all users; Docker's earlier announcement (Docker Desktop 4.33, July 2024) described it as available with a Pro, Team or Business license, so an older Docker Desktop may still need one. See Docker's announcement and the command reference.

Docker without docker debug works on any Docker, with no plan or sign-in: start a tools container that shares the cache container's network and process namespaces. (This is the no-shell route; the :debug image variant and its /busybox/sh are described in the troubleshooting section.)

docker run --rm -it --network=container:<name> --pid=container:<name> \
  cgr.dev/chainguard/wolfi-base sh
# inside it: apk add curl procps, then curl http://127.0.0.1:8080/healthz

What was tested: docker debug (Docker Desktop's debug plugin 0.0.47 ) gave a root shell on a running fscache container; the plain-Docker command above reached /healthz (HTTP 200) on 127.0.0.1:8080 and listed fscache as PID 1; wolfi-base runs as root and apk add curl works in it; busybox:1.37 run as user 65532 (as the pod's security context would) read /healthz with wget and listed fscache. The tool images in the examples are not pinned; pin them by digest in anything you automate. kubectl debug was checked against the Kubernetes documentation but not run against a cluster here.

Build and run from source

git clone https://github.com/fosterstack/cache.git
cd cache
CGO_ENABLED=0 go build -o fscache ./cmd/fscache
./fscache

Configuration is via environment variables (flags are not yet wired):

Variable Default Meaning
FSCACHE_ADDR :8080 Listen address
FSCACHE_DATA_DIR ./data Where blobs and the metadata index live
FSCACHE_MAX_BYTES 0 (unbounded) Size cap; oldest-unused entries evicted first
FSCACHE_USERNAME / FSCACHE_PASSWORD unset (auth disabled) HTTP Basic Auth, required together
FSCACHE_RO_USERNAME / FSCACHE_RO_PASSWORD unset Optional read-only pair: GET/HEAD only, writes get 403. Requires the read-write pair; usernames must differ
FSCACHE_MAX_BODY_BYTES 1073741824 (1 GiB) Max accepted blob size per PUT
FSCACHE_MAX_CONCURRENT_UPLOADS 32 (0 disables the bound) Max PUTs in flight; the request past the limit gets 429 with Retry-After and stores nothing
FSCACHE_TLS_CERT / FSCACHE_TLS_KEY unset (plain HTTP) PEM certificate file (it may hold the chain) and its private key file, required together. When both are set the server serves HTTPS only, on FSCACHE_ADDR, with TLS 1.2 as the minimum version; HTTP/2 is not offered. Each file may be at most 1 MiB. The files are read only at startup: replace them, then restart

A variable that starts with FSCACHE_ and is not in this table is ignored with one WARN log line naming it (never its value), and, when it is a near miss for a real name, a did_you_mean hint. The server still starts.

Gradle setup

In settings.gradle / settings.gradle.kts, point the remote cache at your server (the whole request path is the cache key, so a trailing slash on the URL is all that's needed):

buildCache {
    remote<HttpBuildCache> {
        url = uri("https://cache.example.com/")
        isPush = true
        // credentials { username = "..."; password = "..." } // if auth is enabled
    }
}

Maven setup

Basic Auth credentials come from settings.xml <server> conventions, the Apache extension's standard pattern. See Maven setup in 10 minutes for the full walkthrough.

Product promises and proof

Every externally observable behavior of this server is a written requirement with measurable acceptance criteria, and the matrix at docs/quality/traceability.md maps each criterion to its evidence — including, honestly, the criteria that have no sufficient evidence yet. The baseline was extracted from the code and docs and owner-approved; since Sep 8, 2026, acceptance criteria are written before implementation. CI fails if the matrix drifts from its sources.

Documentation

Security

  • Static binary, zero CGO, no OS package surface to patch.
  • Approved-only cryptography is enforced by the validated module itself, not by a list: CI runs the full test suite under GODEBUG=fips140=only, where Go's FIPS 140-3 module refuses any non-approved algorithm at runtime. A depguard allowlist covers the static side, permitting only in-boundary crypto imports and denying third-party crypto outright. Both are described in SECURITY.md, including what each does and does not prove.
  • No telemetry and no required FosterStack connection. The server reports nothing to us — no usage reporting, no licence check, no update ping — and today's free core initiates no outbound connections at all, so it runs identically on a host with no route to the internet. When paid features ship, traffic you configure will exist (an identity provider for SSO, peer replicas for replication) — connections to endpoints you choose, never to FosterStack. Check the current behavior yourself: lsof -nP -a -p <pid> -i against a running server shows one listening socket and accepted inbound connections, nothing else.
  • See SECURITY.md for the vulnerability disclosure process and patch SLA once published.

License

MIT — see LICENSE.

About

Self-hosted remote build cache for Gradle and Maven — drop-in replacement for the EOL Develocity Build Cache Node

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages