diff --git a/.well-known/security.txt b/.well-known/security.txt new file mode 100644 index 0000000..32e465c --- /dev/null +++ b/.well-known/security.txt @@ -0,0 +1,6 @@ +Contact: https://github.com/fosterstack/cache/security/advisories/new +Contact: https://github.com/fosterstack/www/security/advisories/new +Expires: 2027-10-08T00:00:00.000Z +Preferred-Languages: en +Canonical: https://fosterstack.com/.well-known/security.txt +Policy: https://fosterstack.com/security/ diff --git a/air-gapped-build-cache-install/index.html b/air-gapped-build-cache-install/index.html new file mode 100644 index 0000000..0f3a50f --- /dev/null +++ b/air-gapped-build-cache-install/index.html @@ -0,0 +1,134 @@ + + + + + +Install a build cache on an air-gapped network + + + + + + + + +
+ +
Infrastructure worth keeping — and kept patched.
+
+
+

Install a build cache on an air-gapped network

+

The server is one static program with nothing to download at install time and nothing it reaches out for at startup. So an air-gapped install is short: move the files across, verify them, run.

+ +

What to bring across

+

On a machine with internet access, download these from the release page:

+ + +

Verify before you cross the gap

+

Do this on the connected side, where the signature check can reach its public log:

+
# 1. Prove the checksums file is ours
+cosign verify-blob \
+  --bundle checksums.txt.bundle \
+  --certificate-identity-regexp='^https://github.com/fosterstack/cache/' \
+  --certificate-oidc-issuer='https://token.actions.githubusercontent.com' \
+  checksums.txt
+
+# 2. Prove your archive matches it
+sha256sum -c <(grep "fscache_X.Y.Z_linux_amd64.tar.gz" checksums.txt)
+

Both steps matter. The checksum alone only shows the archive matches a file you carried beside it. The signature is what ties that file to us.

+ +

Install on the isolated side

+

For the binary: unpack the archive and follow the install guide from the unpack step, including the systemd service. There is no separate offline procedure.

+

For containers:

+
docker load -i fscache-X.Y.Z.tar
+docker run -d -p 127.0.0.1:8080:8080 -v fscache-data:/home/nonroot ghcr.io/fosterstack/cache:X.Y.Z
+

Real deployments terminate HTTPS at a proxy in front of the server, because Gradle sends the password with every request. The offline install guide has the full detail, including what to do for later upgrades.

+ +

It does not call out

+

The server sends no telemetry and has no update or licence check, so it behaves the same with no route to the internet.

+ +

Related: how releases are built and signed and server sizing.

+
+ Gradle and Maven setup + FosterStack home +
+
+ + + diff --git a/bin/check-file-allowlist.sh b/bin/check-file-allowlist.sh index 2d1d85f..b999f58 100755 --- a/bin/check-file-allowlist.sh +++ b/bin/check-file-allowlist.sh @@ -29,6 +29,7 @@ ALLOW_PATTERNS=( '^favicon-(16|32)\.png$' '^apple-touch-icon\.png$' '^\.gitignore$' + '^\.well-known/security\.txt$' '^\.githooks/pre-commit$' '^bin/check-file-allowlist\.sh$' '^\.github/workflows/[A-Za-z0-9._-]+\.ya?ml$' diff --git a/build-cache-node-end-of-life/index.html b/build-cache-node-end-of-life/index.html index 5593f64..4cd3d79 100644 --- a/build-cache-node-end-of-life/index.html +++ b/build-cache-node-end-of-life/index.html @@ -134,7 +134,7 @@

Small, quiet and patched

  • Tiny and quiet. A distroless image with one static program and no shell. It sends no telemetry and makes no outbound connections.
  • A FIPS build. A FIPS variant uses Go's validated FIPS 140-3 cryptographic module.
  • Patched for everyone. We aim to ship dependency fixes within 48 hours of disclosure. That is a target, not a promise. Security patches are never withheld from the free tier.
  • -
  • Report a problem privately. Use GitHub's private vulnerability report.
  • +
  • Report a problem privately. See the security policy.
  • Next step

    diff --git a/build-cache-node-faq/index.html b/build-cache-node-faq/index.html new file mode 100644 index 0000000..8693f86 --- /dev/null +++ b/build-cache-node-faq/index.html @@ -0,0 +1,150 @@ + + + + + +Build Cache Node replacement: questions and answers + + + + + + + + + +
    + +
    Infrastructure worth keeping — and kept patched.
    +
    +
    +

    Build Cache Node replacement: questions and answers

    +

    Short answers about the end of the free Build Cache Node and about FosterStack Cache.

    + +

    What is happening to the Develocity Build Cache Node?

    +

    Gradle Inc. says the free standalone Build Cache Node "will no longer be distributed, supported or available after December 31, 2026." Read the vendor's official notice. We have a short page on what that means.

    + +

    Do I have to move my cached data?

    +

    No. A build cache only holds results your build can always rebuild. Start the new server empty and your first builds fill it. If the cache is down or empty, builds are slower, not wrong.

    + +

    What is FosterStack Cache?

    +

    A self-hosted remote build cache. It is one static binary or one container image, MIT licensed. It speaks Gradle's own HttpBuildCache protocol, and also the remote mode of Apache's Maven Build Cache Extension.

    + +

    What makes FosterStack Cache's security different?

    +

    Every release is signed and carries build provenance, an SBOM and published vulnerability statements. Two independent scanners check each release candidate before it ships, and every published image is scanned again daily. Images are minimal, with one static program and no shell, and the server sends no telemetry. See the security policy and how we build.

    + +

    Does it work with Gradle?

    +

    Yes. Gradle talks to it with its normal remote build cache setting, so moving from the old node is mostly a URL change. Our CI builds a real multi-module Gradle project against it on every change and checks that a second build gets real cache hits. See the setup page.

    + +

    Does it work with Maven?

    +

    Yes, through the Apache Maven Build Cache Extension. The Maven client is Apache's Build Cache Extension, not ours.

    + +

    Is it free?

    +

    Yes. Everything you can download today is the free MIT core, including eviction, size limits and metrics. Paid tiers are described for the future but are not built or sold yet.

    + +

    Is it ready for production?

    +

    It is early. A cache fails safe, since a missing cache means slower builds, but decide for yourself with that in mind. The README lists what has shipped and what has not.

    + +

    Who can write to the cache?

    +

    Anyone holding the read-write password. Turn on Basic Auth anywhere untrusted people could reach the port, and share that password only with CI and developers you trust to change your code. You can also set an optional read-only login for everyone else: it can read but any write is refused. Telling Gradle not to upload is a client choice, not a server rule. Per-person identity is not built.

    + +

    Does it have a web page like the old node?

    +

    No settings page, on purpose. You configure it with flags and environment variables. A read-only status page shows version, size, entries and hit counts, and metrics are available for Prometheus. To empty the cache, remove its volume and start again.

    + +

    How do I check a release is the real one?

    +

    Container images are signed and releases carry build provenance, a software bill of materials and vulnerability statements. The commands are under "Verifying a release" in RELEASING.md. No account is needed for the signature check.

    + +

    Does the server send anything out?

    +

    No. The server sends no telemetry and needs no connection to FosterStack. It can run on a network with no route to the internet; see the disconnected network guide.

    + +

    Can it store its data in S3 or other object storage?

    +

    No. It stores cache entries on a local file system volume, with a small index file beside them. It runs as a single instance, so object storage is not used or supported today. Size the volume from the cache size cap. See server sizing.

    + +

    Can I run it on Kubernetes?

    +

    Yes, with plain manifests in the Kubernetes guide. There is no Helm chart yet.

    + +

    Does it work with Jenkins and other CI?

    +

    It is a plain web server, so any runner that can reach it can use it. See Jenkins and CI build cache.

    + +

    Where do I report a problem or ask for something?

    +

    Open an issue on GitHub. There is no signup, form or mailing list.

    + +
    + Gradle and Maven setup + FosterStack home +
    +
    + + + diff --git a/build-cache-server-sizing/index.html b/build-cache-server-sizing/index.html new file mode 100644 index 0000000..ce8cbfb --- /dev/null +++ b/build-cache-server-sizing/index.html @@ -0,0 +1,131 @@ + + + + + +How big a server does a build cache need? CPU, RAM and disk + + + + + + + + +
    + +
    Infrastructure worth keeping — and kept patched.
    +
    +
    +

    How big a server does a build cache need? CPU, RAM and disk

    +

    You are sizing a disk, not a compute tier. The server is one small program that idles in tens of megabytes, so the question is how much cache you want to keep.

    + +

    Starting sizes

    + + + + + +
    vCPURAMDisk
    Absolute minimum11 GBcache cap + about 5 GB for the system
    Recommended start, about 5 to 20 developers plus CI1 to 22 GB25 to 50 GB SSD
    Larger CI farms2 to 44 to 8 GBcache cap + about 20%
    +

    These are starting points from our deployment guide. Your build graph decides the rest. It is hard to be limited by CPU, because the busy path is reading and writing stored files, not computing.

    + +

    Set a size cap

    +

    Choose how much cache you want, set it with FSCACHE_MAX_BYTES, and make the disk about 20% bigger. When the cache reaches the cap it removes the least recently used entries and never grows past it. The extra room covers file system overhead and uploads in progress.

    +

    The value is in bytes. It is easy to be wrong by a factor of 1024, so copy one:

    + + + + + + +
    CapFSCACHE_MAX_BYTES
    10 GiB10737418240
    20 GiB21474836480
    50 GiB53687091200
    100 GiB107374182400
    + +

    Two mistakes that look like "the cache doesn't help"

    + + +

    Put it near the runners

    +

    On bigger setups the network matters more than the processor. A small server in the same region, ideally the same network, as your CI runners beats a large one far away.

    + +

    Related: Gradle and Maven setup and Jenkins and CI build cache. Full detail is in the Docker deploy guide.

    +
    + Gradle and Maven setup + FosterStack home +
    +
    + + + diff --git a/changelog/index.html b/changelog/index.html new file mode 100644 index 0000000..9bc1baa --- /dev/null +++ b/changelog/index.html @@ -0,0 +1,120 @@ + + + + + +FosterStack Cache changelog: v0.1.0 to v0.2.1 + + + + + + + + +
    + +
    Infrastructure worth keeping — and kept patched.
    +
    +
    +

    FosterStack Cache changelog: v0.1.0 to v0.2.1

    +

    The release notes from GitHub, quoted as published. Newest first. For anything else, the releases page is the source.

    + +

    v0.2.1

    +

    Release name: fscache v0.2.1. Published 2026-09-21.

    +
    See release-manifest.json for the full evidence bundle: image digests (GHCR canonical, Docker Hub mirror — identical digests), archive checksums, requirements baseline, per-AC acceptance results, and the Rekor log index of every verified statement in the release chain.
    +

    v0.2.1 on GitHub

    + +

    v0.2.0

    +

    No release was published for v0.2.0: the tag exists, but its release run failed. Use v0.2.1.

    + +

    v0.1.0

    +

    Published 2026-08-22.

    +
    +

    The v0.1.0 notes begin with a list of the nine commits in the release. That list is on the GitHub release page. The rest of the notes follow here, unedited.

    +

    Note on this release's evidence (added Sep 2026). v0.1.0 was built and published by the pre-remediation release pipeline: a snapshot build was scanned, and a separate build of the same commit was published, so these published images were not scanned before publication. They have been rescanned since at every severity with the published VEX applied — see the rescan runs for verdicts against the exact published digests. The signatures and provenance on this release are accurate about what they say — which CI run produced which digest — and say nothing about scanning. The next release is the first to carry the full evidence chain: one build, scanned and tested by digest, promoted without a rebuild.

    +
    +

    v0.1.0 on GitHub

    +

    What changed since: the v0.2.1 release notes point to its release-manifest.json evidence bundle. The release process, as RELEASING.md describes it, is "One build, one set of digests, promoted without a rebuild", with every scanner run "against the exact candidate digests".

    + +

    Related: how releases are built and signed and security policy.

    +
    + All releases on GitHub + FosterStack home +
    +
    + + + diff --git a/fips-140-3-build-cache/index.html b/fips-140-3-build-cache/index.html new file mode 100644 index 0000000..7518525 --- /dev/null +++ b/fips-140-3-build-cache/index.html @@ -0,0 +1,132 @@ + + + + + +FIPS 140-3 build cache: run the FIPS image and confirm it + + + + + + + + +
    + +
    Infrastructure worth keeping — and kept patched.
    +
    +
    +

    FIPS 140-3 build cache: run the FIPS image and confirm it

    +

    FosterStack Cache has a FIPS build that uses Go's validated FIPS 140-3 cryptographic module. This page shows how to run it and how to see for yourself that it is in FIPS mode. The outputs below are from the real 0.2.1 images.

    + +

    What the FIPS build is

    +

    It is the same source as the standard build, compiled so that it uses Go's validated FIPS 140-3 module (CMVP certificate #5247). It is available as a container image tag ending in -fips, and as Linux binaries for amd64 and arm64. There is no macOS FIPS build.

    + +

    Run it

    +
    docker run -d -p 127.0.0.1:8080:8080 -v fscache-data:/home/nonroot \
    +  ghcr.io/fosterstack/cache:0.2.1-fips
    +

    Pin an exact version, as here, rather than a floating tag. To check you pulled the real image, use the steps in Verify a release yourself. The FIPS image's digest in the release manifest for 0.2.1 is sha256:e125fe3f6ed2c18c478453300ba2b787a42999edf66a39403877efb39865ab1b, and that is what the pull returned.

    + +

    Confirm it is in FIPS mode

    +

    The build says so when it starts, so the property can be seen and not just trusted:

    +
    $ docker logs <container>
    +{"time":"2026-10-09T01:00:54.861529344Z","level":"INFO","msg":"fscache: starting","version":"v0.2.1","addr":":8080","data_dir":"./data","max_bytes":0,"auth":"disabled","fips140":"active (Go validated module v1.0.0, CMVP cert #5247)"}
    +

    The status page reports the same:

    +
    $ curl -s localhost:8080/statusz | grep -E 'version|fips140'
    +  "version": "v0.2.1",
    +  "fips140": true,
    +  "fips140_note": "active (Go validated module v1.0.0, CMVP cert #5247)",
    +

    The standard image, for comparison, reports it as off:

    +
    $ curl -s localhost:8080/statusz | grep -E 'version|fips140'
    +  "version": "v0.2.1",
    +  "fips140": false,
    +  "fips140_note": "off",
    + +

    What this tells you, and what it does not

    + + +

    Related: for teams entering regulated markets and security policy. Details: SECURITY.md.

    +
    + Verify a release yourself + FosterStack home +
    +
    + + + diff --git a/gradle-build-cache-docker-compose/index.html b/gradle-build-cache-docker-compose/index.html new file mode 100644 index 0000000..4f2dd28 --- /dev/null +++ b/gradle-build-cache-docker-compose/index.html @@ -0,0 +1,141 @@ + + + + + +Gradle build cache with Docker Compose + + + + + + + + +
    + +
    Infrastructure worth keeping — and kept patched.
    +
    +
    +

    Gradle build cache with Docker Compose

    +

    Run a shared Gradle build cache for your team in one Compose file, with a password and a size limit. This is the shape we suggest once you go past a first trial.

    + +

    Try it first, on one machine

    +
    docker run -d -p 127.0.0.1:8080:8080 -v fscache-data:/home/nonroot ghcr.io/fosterstack/cache:latest
    +

    The 127.0.0.1: keeps it reachable from this machine only. A bare -p 8080:8080 opens the port to every network interface, and on a cloud host that is a cache anyone can write to the moment the command returns. Pin a version tag instead of latest for anything real.

    + +

    The Compose file

    +

    This one is open to your CI runners, so it has a password on purpose. A cache that untrusted people can write to can feed files into your builds.

    +
    services:
    +  fscache:
    +    image: ghcr.io/fosterstack/cache:latest   # pin a version tag
    +    restart: unless-stopped
    +    ports:
    +      - "8080:8080"                    # all interfaces, so auth is on
    +    volumes:
    +      - fscache-data:/home/nonroot
    +    environment:
    +      FSCACHE_MAX_BYTES: "53687091200"   # 50 GiB, size to your CI volume
    +      FSCACHE_USERNAME: gradle
    +      FSCACHE_PASSWORD: change-me        # generate one: openssl rand -base64 24
    +
    +volumes:
    +  fscache-data:
    + + +

    Check it works, with no Gradle project

    +
    curl -s localhost:8080/healthz                                   # ok
    +curl -s -X PUT --data-binary 'hello' localhost:8080/testkey123   # 201
    +curl -s localhost:8080/testkey123                                # hello
    +

    Add -u gradle:your-password to those calls once you have set a password.

    + +

    Point Gradle at it

    +

    Use the settings from our Gradle and Maven setup page, and turn caching on with org.gradle.caching=true. If nothing comes from the cache, work through Gradle remote build cache not working.

    + +

    Health checks

    +

    The container image has no shell, so there is no health check inside the Compose file. Probe /healthz from outside: your orchestrator's HTTP check, a monitor, or curl -f http://localhost:8080/healthz from the host.

    + +

    Full detail: Docker deploy guide.

    +
    + Gradle and Maven setup + FosterStack home +
    +
    + + + diff --git a/gradle-build-cache-http-status-codes/index.html b/gradle-build-cache-http-status-codes/index.html new file mode 100644 index 0000000..76f6ac8 --- /dev/null +++ b/gradle-build-cache-http-status-codes/index.html @@ -0,0 +1,142 @@ + + + + + +Gradle build cache HTTP status codes: 401, 403, 404 and 413 + + + + + + + + +
    + +
    Infrastructure worth keeping — and kept patched.
    +
    +
    +

    Gradle build cache HTTP status codes: 401, 403, 404 and 413

    +

    Gradle talks to the cache with plain web requests, and it does not fail the build when the cache says no. So the status code is often the only clue. This is what each one means for a FosterStack Cache server.

    + +

    The short table

    + + + + + + + + +
    StatusWhen you get itWhat to do
    200A stored entry was found and sent.Nothing. This is a cache hit.
    201An upload was stored.Nothing.
    404That key is not stored.Usually nothing. It is a cache miss. With a correct password it means the key just is not there yet.
    401No password, or the wrong one.Fix the credentials block. See below.
    403A write sent with the read-only login.Use the read-write login for machines that upload, or set the client not to upload.
    413An upload bigger than the server's size limit for one entry.Raise FSCACHE_MAX_BODY_BYTES if the entry is meant to be cached.
    + +

    Tested on a real server

    +

    These were all run against a 0.2.1 server started with a read-write login, a read-only login, and the body limit set to 1024 bytes. Your numbers for the limit will differ, since the default is 1 GiB.

    +
    GET /healthz (no credentials)                                  200
    +GET /metrics (no credentials)                                  200
    +GET key, no credentials                                        401
    +GET key, wrong password                                        401
    +GET missing key, read-write login                              404
    +PUT 5 bytes, read-write login                                  201
    +GET stored key, read-write login                               200
    +GET stored key, read-only login                                200
    +HEAD stored key, read-only login                               200
    +PUT, no credentials                                            401
    +PUT, read-only login                                           403
    +PUT 2048 bytes (limit set to 1024), read-write login           413
    +GET the too-large key afterwards                               404
    +/statusz, no credentials                                       401
    +

    The health and metrics paths stay open without a password, so monitoring works. The status page does not.

    + +

    401 is the quiet one

    +

    Gradle treats a remote cache failure as non-fatal, so a 401 does not break the build and shows nothing at the default log level. The build just never uses the cache. Run with -i to see it. The usual causes are no credentials block, a password that did not reach Gradle's background daemon, or a typo. To test the server directly:

    +
    curl -s -o /dev/null -w '%{http_code}\n' -u gradle:wrong-password https://cache.example.com/some-key
    +

    A 401 here means the credentials are wrong. A 404 means they are right and the key simply is not stored. More steps are in Gradle remote build cache not working.

    + +

    403 means the login worked but may not write

    +

    The server can have an optional read-only login. A client holding it can read (GET and HEAD return 200 as normal) but any write gets a 403. That is the intended way to give laptops a cache they can read and not change. See who can write to a remote build cache.

    + +

    413 and entry size

    +

    A single upload over the limit is refused with 413 and nothing is stored, so a later request for that key is a 404. The limit is FSCACHE_MAX_BODY_BYTES and defaults to 1 GiB per entry.

    + +

    Related: server sizing and monitoring.

    +
    + Gradle remote cache not working + FosterStack home +
    +
    + + + diff --git a/gradle-build-cache-kubernetes/index.html b/gradle-build-cache-kubernetes/index.html new file mode 100644 index 0000000..9fe2702 --- /dev/null +++ b/gradle-build-cache-kubernetes/index.html @@ -0,0 +1,130 @@ + + + + + +Gradle build cache on Kubernetes: plain manifests, no Helm chart yet + + + + + + + + +
    + +
    Infrastructure worth keeping — and kept patched.
    +
    +
    +

    Gradle build cache on Kubernetes: plain manifests, no Helm chart yet

    +

    Run a shared build cache in your cluster with plain manifests: one Deployment, one volume, one Service. There is no Helm chart yet. It has not been through a beta on a production cluster, so treat it that way.

    + +

    Is there a Helm chart?

    +

    Not yet. Today the supported way to deploy on Kubernetes is the plain manifests in the Kubernetes guide: a Secret, a volume claim, a Deployment and a Service. Copy them into one file and apply it with kubectl apply -f.

    + +

    Run exactly one replica

    +

    Do not scale this out. The cache keeps its index in a database file that one process holds a lock on. A second pod on the same volume does not share the cache; it waits on the lock or fails to start. Two pods on different volumes give you two half-filled caches.

    + +

    A cache is safe to lose for a moment. If it is down during a rollout, builds miss the cache and run slower, and nothing breaks. High availability across nodes is a paid-tier idea that is not built, so do not improvise it with two replicas.

    + +

    Size it in Kubernetes terms

    + + +

    Locked down by default

    +

    The manifests run as a non-root user with a read-only root file system, drop all capabilities, forbid privilege escalation, and use the runtime default seccomp profile. There is no privileged mode, no host networking and no host path. They satisfy the Pod Security restricted profile. The volume must be writable by user 65532, which is what the fsGroup setting is for.

    + +

    Runners in the same cluster

    +

    If your CI runners already run in this cluster, including Jenkins agents, GitHub Actions runner controller and GitLab runners, you need no load balancer and no ingress. Point Gradle at the cluster-internal name, for example http://fscache.fscache-ns.svc.cluster.local/, with isAllowInsecureProtocol = true. Gradle requires that for plain http:// addresses. In our test with Gradle 9.8.0, only the literal 127.0.0.1 was let through without it. Here it is reasonable because the traffic never leaves the cluster network. See Jenkins and CI build cache.

    + +

    Get the manifests

    +

    The four manifests (Secret, volume claim, Deployment, Service) and the steps to reach the cache from outside the cluster are in the Kubernetes guide on GitHub. Copy them into one file and apply it.

    + +
    + Kubernetes guide + Gradle and Maven setup +
    +
    + + + diff --git a/gradle-build-cache-tutorial/index.html b/gradle-build-cache-tutorial/index.html new file mode 100644 index 0000000..40fb942 --- /dev/null +++ b/gradle-build-cache-tutorial/index.html @@ -0,0 +1,184 @@ + + + + + +Gradle build cache tutorial: see FROM-CACHE with a self-hosted cache + + + + + + + + +
    + +
    Infrastructure worth keeping — and kept patched.
    +
    +
    +

    Gradle build cache tutorial: see FROM-CACHE with a self-hosted cache

    +

    Start a cache server, point a small Gradle project at it, build twice, and watch the second build take its work from the cache. Everything below was run for real: release 0.2.1 of the server and Gradle 9.8.0.

    + +

    1. Start the server

    +

    Download and verify the release as shown in Verify a release yourself, unpack it, and run it with a password and a size cap:

    +
    FSCACHE_ADDR=127.0.0.1:8080 FSCACHE_DATA_DIR=./data \
    +  FSCACHE_USERNAME=gradle FSCACHE_PASSWORD=demo-pass \
    +  FSCACHE_MAX_BYTES=1073741824 ./fscache
    +

    Prefer a container? Gradle build cache with Docker Compose has the same settings. (My test run used port 18090 instead of 8080; the outputs below do not show the port.)

    + +

    2. A small project

    +

    Four files. settings.gradle.kts says where the cache is:

    +
    rootProject.name = "demo"
    +
    +// FosterStack Cache — remote Gradle build cache.
    +// https://github.com/fosterstack/cache
    +buildCache {
    +    local { isEnabled = false }          // so a hit can only come from the remote
    +    remote<HttpBuildCache> {
    +        url = uri("http://127.0.0.1:8080/")
    +        isPush = true
    +        credentials {
    +            username = "gradle"
    +            password = System.getenv("FSCACHE_PASSWORD")
    +        }
    +    }
    +}
    +

    Turning the local cache off is only for this test. It means a hit can only have come from the remote. build.gradle.kts is one line, plugins { java }. gradle.properties switches caching on, which the buildCache block does not do by itself:

    +
    org.gradle.caching=true
    +

    And src/main/java/demo/App.java is any small class.

    + +

    3. Build once

    +
    export FSCACHE_PASSWORD=demo-pass
    +gradle build --build-cache
    +
    +> Task :compileJava
    +> Task :processResources NO-SOURCE
    +> Task :classes
    +> Task :jar
    +> Task :assemble
    +> Task :compileTestJava NO-SOURCE
    +> Task :processTestResources NO-SOURCE
    +> Task :testClasses UP-TO-DATE
    +> Task :test NO-SOURCE
    +> Task :check UP-TO-DATE
    +> Task :build
    +BUILD SUCCESSFUL in 5s
    +2 actionable tasks: 2 executed
    +

    Nothing came from the cache, and every cacheable task ran. Gradle stored the compile result on the server. The status page agrees:

    +
    $ curl -s -u gradle:demo-pass localhost:8080/statusz | grep -E '"store_entries"|"cache_hits"|"cache_misses"'
    +  "store_entries": 1,
    +  "cache_hits": 0,
    +  "cache_misses": 1,
    + +

    4. Clean, and build again

    +
    gradle clean
    +gradle build --build-cache
    +
    +> Task :compileJava FROM-CACHE
    +> Task :processResources NO-SOURCE
    +> Task :classes UP-TO-DATE
    +> Task :jar
    +> Task :assemble
    +> Task :compileTestJava NO-SOURCE
    +> Task :processTestResources NO-SOURCE
    +> Task :testClasses UP-TO-DATE
    +> Task :test NO-SOURCE
    +> Task :check UP-TO-DATE
    +> Task :build
    +BUILD SUCCESSFUL in 5s
    +2 actionable tasks: 1 executed, 1 from cache
    +

    There it is: :compileJava FROM-CACHE and "1 from cache". The server counted the hit:

    +
    $ curl -s -u gradle:demo-pass localhost:8080/statusz | grep -E '"store_entries"|"cache_hits"|"cache_misses"'
    +  "store_entries": 1,
    +  "cache_hits": 1,
    +  "cache_misses": 1,
    + +

    The one thing that tripped me up

    +

    My first attempt failed before any build ran, because I used http://localhost:...:

    +
    FAILURE: Build failed with an exception.
    +
    +* What went wrong:
    +Using insecure protocols with remote build cache, without explicit opt-in, is unsupported.
    +

    Gradle refuses plain http:// unless you set isAllowInsecureProtocol = true. With Gradle 9.8.0, the address 127.0.0.1 was allowed without it, but the name localhost was refused, with or without a credentials block. That is why the file above uses 127.0.0.1. For a real server, use HTTPS and skip the opt-in. Gradle sends the password with every request.

    + +

    If your second build shows no FROM-CACHE, work through Gradle remote build cache not working. The status codes are explained in Gradle build cache HTTP status codes.

    +
    + Gradle and Maven setup + FosterStack home +
    +
    + + + diff --git a/gradle-remote-cache-not-working/index.html b/gradle-remote-cache-not-working/index.html new file mode 100644 index 0000000..ddff837 --- /dev/null +++ b/gradle-remote-cache-not-working/index.html @@ -0,0 +1,138 @@ + + + + + +Gradle remote build cache not working: no FROM-CACHE + + + + + + + + +
    + +
    Infrastructure worth keeping — and kept patched.
    +
    +
    +

    Gradle remote build cache not working: no FROM-CACHE

    +

    Your build runs, nothing fails, and nothing comes from the remote cache. Gradle treats a broken remote cache as non-fatal, so it fails quietly. Work down this list.

    + +

    1. Is the build cache switched on?

    +

    The buildCache block in settings.gradle(.kts) says where the cache is. It does not turn caching on. Without this line the block does nothing:

    +
    # gradle.properties
    +org.gradle.caching=true
    +

    Or pass --build-cache on each run. This is the most common cause.

    + +

    2. Is the local cache hiding the problem?

    +

    Gradle's local cache is on by default and gives hits even when the remote is failing. So FROM-CACHE on its own does not prove the remote works. Take the local cache out of the picture and show remote errors:

    +
    rm -rf ~/.gradle/caches/build-cache-1
    +./gradlew --stop
    +./gradlew build --build-cache -i
    +

    Run it twice. The second run is the one that tells you something.

    + +

    3. Is the password reaching Gradle?

    +

    If the server uses a password and Gradle sends none, every request gets a 401, silently. Two things to check:

    + +

    To test the server's password directly, a 401 means the credentials are wrong and a 404 means they are right and the key just is not there yet:

    +
    curl -s -o /dev/null -w '%{http_code}\n' -u gradle:wrong-password \
    +  https://cache.example.com/some-key
    + +

    4. Is the URL right, and is it HTTPS?

    +

    Keep the trailing slash on the URL. Gradle adds the cache key straight onto it. Gradle also refuses plain http:// unless you set isAllowInsecureProtocol = true. In our test with Gradle 9.8.0, the address 127.0.0.1 was allowed without it, but the name localhost was refused. Use HTTPS in real deployments, because Gradle sends the password with every request.

    +

    One trap in test setups: some sample projects run a check that fails any build containing an http:// URL, including the cache URL. That is the check, not the cache.

    + +

    5. Is the server seeing requests?

    +
    curl -s https://cache.example.com/metrics | grep -E 'fscache_cache_(hits|misses)_total'
    +

    Run a build and run this again. If neither counter moves, the requests are not arriving. If misses move and hits do not, the cache is working and has not been filled yet, or something in the task inputs changes on every build.

    + +

    6. Is this client allowed to upload?

    +

    If you set isPush to false, or only to true on CI, a laptop only reads. Nothing fills the cache until a machine that may upload has run the build.

    + +

    Not the same thing: the configuration cache

    +

    Gradle may suggest enabling its "configuration cache". That is a different, local feature. It has nothing to do with a remote build cache.

    + +
    + Gradle and Maven setup + Questions and answers +
    +
    + + + diff --git a/how-we-build/index.html b/how-we-build/index.html new file mode 100644 index 0000000..0157ca3 --- /dev/null +++ b/how-we-build/index.html @@ -0,0 +1,138 @@ + + + + + +How FosterStack Cache is built and signed + + + + + + + + +
    + +
    Infrastructure worth keeping — and kept patched.
    +
    +
    +

    How FosterStack Cache is built and signed

    +

    Every release is built by an automated pipeline, checked, signed, and published with files you can verify yourself. This page says what that means today.

    + +

    Releases are built only in CI

    +

    No release file is ever made on a person's laptop, ours included. A release starts when a signed tag is pushed, and there is no manual release path. That is what makes the signatures mean something: nobody can forge a release that GitHub's own build machines never built.

    + +

    The steps a release goes through

    +
      +
    1. Source check. The tag and the commit must be signed by an allowed signer, sit on the main branch, and have every required check green.
    2. +
    3. Build. The binaries are built, with checksums and a software bill of materials for each archive.
    4. +
    5. Container images. Each image is assembled once, from the unmodified distroless base plus one binary.
    6. +
    7. Reproducibility check. The image is assembled a second time on a separate machine. The results must be identical, or the release fails.
    8. +
    9. Scans and tests. Every scanner on our list runs against the exact images. A finding blocks the release unless a published VEX statement explains why it does not apply. The Gradle and Maven acceptance tests run against the same images.
    10. +
    11. Authorization. One step checks that every earlier step produced signed proof for these exact files. Anything missing stops the release.
    12. +
    13. Publish. The published images are the same digests that were scanned and tested, promoted without a rebuild, and checked again the way a customer would check them.
    14. +
    +

    The full description, as the pipeline actually runs, is in RELEASING.md.

    + +

    What you get with each release

    + +

    All of it is public. You do not need an account or a purchase to check any of it. The commands are under "Verifying a release" in RELEASING.md.

    + +

    Kept patched

    +

    Every published image is scanned again every day, so a vulnerability disclosed against something we already shipped raises an issue without anyone having to remember to look.

    + +

    What we do not claim

    + + +
    + FosterStack Cache on GitHub + FosterStack home +
    +
    + + + diff --git a/index.html b/index.html index 6c172ae..9b8e3d2 100644 --- a/index.html +++ b/index.html @@ -86,6 +86,7 @@ Gradle Inc. has deprecated the free Develocity Build Cache Node: it "will no longer be distributed, supported or available after December 31, 2026." The migration path they offer requires a commercial Develocity subscription. + What that means and your options.

    Your build cache shouldn't die with its vendor.

    @@ -93,7 +94,7 @@

    Your build cache shouldn't die with its vendor.

    FosterStack is a self-hosted remote build cache server — a Develocity Build Cache Node replacement that speaks the same Gradle remote build cache HTTP protocol, so migrating is mostly a URL change. The same server implements the Apache Maven Build Cache Extension's - remote HTTP mode (Maven acceptance coverage is in progress), so a mixed Gradle and Maven + remote HTTP mode, so a mixed Gradle and Maven shop runs one deploy instead of two. Open source core, one-command deploy, and the thing that matters: it stays patched, on a promise.

    @@ -108,11 +109,11 @@

    Try it now

    Migrate off Build Cache Node Read the source

    -

    Where it stands: v0.1 — early. The cache core and +

    Where it stands: v0.2 — early. The cache core and HTTP surface are tested, and the Gradle path is acceptance-tested against a real multi-module build in CI; the release pipeline works and its evidence chain is being rebuilt — the repository's SECURITY.md states exactly what is and is not proven today. - Nobody is running it in a production build pipeline yet except us. Bugs and questions go to + Bugs and questions go to GitHub issues, which is also where the roadmap gets argued with.

    @@ -135,6 +136,33 @@

    What you get that a bare HTTP endpoint doesn't give you

    30-minute migration

    A step-by-step guide for existing Build Cache Node deployments. Same protocol, same CI config shape — migrating is mostly a URL change.

    +

    Security is the difference

    +

    Security is what sets this product apart. The release items below ship in the current release. The supply-chain items describe how we work today, in our own build and CI. You can check each one yourself.

    +

    Signed and verifiable

    + +

    Supply chain

    + +

    Small, quiet and patched

    + +

    Built to be verified, not trusted

    We're a new vendor asking to sit in your build pipeline, so the burden of proof is on us. The answer is to make everything checkable:

    @@ -200,6 +228,33 @@

    Pricing

    without a procurement cycle.

    +

    Guides

    + +

    Roadmap honesty

    Gradle and Maven both run against the same server today; we maintain the cache server, while the Maven client side is Apache's own Build Cache Extension. A dependency cache — @@ -220,7 +275,7 @@

    Roadmap honesty

    of the documented remote build cache HTTP protocol. Apache Maven is a trademark of the Apache Software Foundation; the Maven Build Cache Extension is Apache's project, not ours.

    -

    © 2026 FosterStack · For regulated markets · github.com/fosterstack/cache · hello@fosterstack.com

    +

    © 2026 FosterStack · Build Cache Node end of life · Migrate · FAQ · CI setup · How we build · For regulated markets · github.com/fosterstack/cache · hello@fosterstack.com

    diff --git a/jenkins-ci-build-cache/index.html b/jenkins-ci-build-cache/index.html new file mode 100644 index 0000000..194265b --- /dev/null +++ b/jenkins-ci-build-cache/index.html @@ -0,0 +1,137 @@ + + + + + +Jenkins and CI build cache for Gradle and Maven + + + + + + + + +
    + +
    Infrastructure worth keeping — and kept patched.
    +
    +
    +

    Jenkins and CI build cache for Gradle and Maven

    +

    Point your Jenkins agents, GitHub Actions runners or GitLab runners at one shared build cache, so a fresh agent stops rebuilding what another agent already built.

    + +

    How a CI runner uses a remote build cache

    +

    The cache is a plain web server. Your build tool (Gradle, or Maven with Apache's Build Cache Extension) asks it for finished task results and uploads new ones. Any runner that can reach the server over HTTP can use it. There is no plugin to install on the CI system.

    +

    We have not tested it with any particular CI product. Our own tests run in GitHub Actions. The protocol is the same everywhere, so what matters is that the runner can reach the server and holds a login.

    + +

    Let CI write, let developers read

    +

    The simplest safe setup is that only CI uploads to the cache and laptops only download. The server can enforce this: give laptops the optional read-only login (FSCACHE_RO_USERNAME and FSCACHE_RO_PASSWORD), and any write they attempt is refused. On the Gradle side you can also stop uploads from non-CI machines in settings.gradle.kts:

    +
    isPush = System.getenv("CI") != null
    +

    That line alone is the client choosing not to upload, not a rule on the server. Anyone holding the read-write password can write, so share it only with people and systems you already trust to change your code.

    + +

    Runners inside your Kubernetes cluster

    +

    If your runners already run in the cluster that hosts the cache (including Jenkins agents), you need no load balancer and no ingress. Point the build at the cluster-internal name:

    +
    // FosterStack Cache — remote Gradle build cache.
    +// https://github.com/fosterstack/cache
    +buildCache {
    +    remote<HttpBuildCache> {
    +        url = uri("http://fscache.fscache-ns.svc.cluster.local/")
    +        isAllowInsecureProtocol = true
    +        isPush = true
    +        credentials {
    +            username = System.getenv("FSCACHE_USERNAME")
    +            password = System.getenv("FSCACHE_PASSWORD")
    +        }
    +    }
    +}
    +

    Replace fscache-ns with your namespace. Plain HTTP is acceptable here only because the traffic never leaves the cluster network. The manifests are in the Kubernetes guide.

    + +

    Runners outside the cluster, or on plain servers

    +

    Run the server on a host the runners can reach and put HTTPS in front of it. Gradle sends the password with every request, so over plain HTTP it travels in the clear. Gradle refuses plain HTTP unless you opt in with isAllowInsecureProtocol, which is a useful warning, not a nuisance. (In our test with Gradle 9.8.0, the address 127.0.0.1 was allowed without it, but the name localhost was refused.) Setup is in the install guide.

    + +

    Give the runner the password

    +

    Store it in your CI system's secret store and expose it as the FSCACHE_PASSWORD environment variable. On a long-lived agent, Gradle's background daemon reads its environment once, when it starts. If you change the secret and builds quietly stop using the cache, run ./gradlew --stop so the next build starts a fresh daemon.

    + +

    Check that CI is really using the cache

    +

    Run the same build twice on two different agents. The second should show FROM-CACHE next to tasks. A runner's local cache can hide a broken remote, so run once with -i, which prints remote errors. Gradle treats a broken remote as non-fatal, so it fails silently otherwise. On the server, the hit and miss counters at /metrics should move.

    +

    Maven on CI follows the same pattern with the extension and a <server> entry for the password. See the Gradle and Maven setup page.

    + +
    + Gradle and Maven setup + FosterStack home +
    +
    + + + diff --git a/llms.txt b/llms.txt index 624e4c1..bdf66fd 100644 --- a/llms.txt +++ b/llms.txt @@ -12,7 +12,7 @@ distribution, support, or updates after 2026-12-31. Teams running it on open-sou Gradle, with no Develocity licence, are orphaned on 2027-01-01. FosterStack Cache speaks Gradle's documented `HttpBuildCache` protocol — acceptance-tested against a real multi-module Gradle build in CI — and implements the Apache Maven Build Cache -Extension's remote HTTP mode (Maven acceptance coverage is in progress). Same server, +Extension's remote HTTP mode. Same server, same core. It is a single static Go binary (`CGO_ENABLED=0`), MIT-licensed, distributed as bare @@ -20,6 +20,33 @@ binaries and distroless container images. Every release is keylessly signed with Sigstore and carries SLSA build provenance, so the exact bytes you deploy are verifiable against the CI run that produced them. +## Guides on this site + +- [Build Cache Node end of life](https://fosterstack.com/build-cache-node-end-of-life/): what happens and your options. +- [Migrate from Build Cache Node](https://fosterstack.com/migrate-build-cache-node/): Gradle and Maven setup and how to check it works. +- [Questions and answers](https://fosterstack.com/build-cache-node-faq/): cost, readiness, security, what is not built. +- [Jenkins and CI build cache](https://fosterstack.com/jenkins-ci-build-cache/): one shared cache for CI runners. +- [Gradle remote build cache not working](https://fosterstack.com/gradle-remote-cache-not-working/): six checks when nothing comes from the cache. +- [Maven remote build cache setup](https://fosterstack.com/maven-remote-build-cache-setup/): extension, config, credentials, checking hits. +- [Gradle build cache with Docker Compose](https://fosterstack.com/gradle-build-cache-docker-compose/): password, size cap, volume, health checks. +- [Gradle build cache on Kubernetes](https://fosterstack.com/gradle-build-cache-kubernetes/): one replica, sizing, security context. +- [Security policy](https://fosterstack.com/security/): how to report a vulnerability, what we sign and publish. +- [Verify a release yourself](https://fosterstack.com/verify-a-release/): every command with real output from 0.2.1. +- [FIPS 140-3 build cache](https://fosterstack.com/fips-140-3-build-cache/): run the FIPS image and confirm FIPS mode. +- [Monitor a build cache](https://fosterstack.com/monitor-build-cache-prometheus-grafana/): metrics, queries, Grafana dashboard. +- [Gradle build cache HTTP status codes](https://fosterstack.com/gradle-build-cache-http-status-codes/): 401, 403, 404, 413 explained. +- [Reset a build cache](https://fosterstack.com/reset-gradle-build-cache/): how to empty it. +- [Is a remote build cache safe](https://fosterstack.com/remote-build-cache-security/): who can write to it. +- [Changelog](https://fosterstack.com/changelog/): release notes for v0.1.0 and v0.2.1. +- [Gradle build cache tutorial](https://fosterstack.com/gradle-build-cache-tutorial/): a real first build and FROM-CACHE hit. +- [Verify a container image signature with cosign](https://fosterstack.com/verify-container-image-signature-cosign/): the two commands and what they prove. +- [For regulated markets](https://fosterstack.com/regulated/): the FIPS build and the public release evidence. +- [Server sizing](https://fosterstack.com/build-cache-server-sizing/): CPU, RAM, disk and the size cap. +- [Air-gapped install](https://fosterstack.com/air-gapped-build-cache-install/): download, verify, transfer, run. +- [What to check in a replacement build cache](https://fosterstack.com/verify-replacement-build-cache/): eight questions to ask. +- [Long-term support](https://fosterstack.com/long-term-support-build-cache/): what is supported today. +- [How FosterStack Cache is built and signed](https://fosterstack.com/how-we-build/): the release steps and what you can verify. + ## Product - [FosterStack Cache on GitHub](https://github.com/fosterstack/cache): MIT-licensed source, issues, releases. @@ -49,14 +76,13 @@ verifiable against the CI run that produced them. ## Status -FosterStack Cache is at v0.1 — early. The cache core, HTTP surface, eviction, +FosterStack Cache is at v0.2 — early. The cache core, HTTP surface, eviction, metrics and the signed release pipeline are shipped and testable today. There is no published schedule for what follows; the roadmap below says direction, not dates. Not yet shipped: -- Production use beyond the project's own CI. Nobody runs this in a real build - pipeline yet except the maintainer. +- Production use beyond the project's own CI. - A Helm chart. Deploying to Kubernetes today means applying plain manifests. - The paid tiers. Team ($49/mo: multi-user access control, SSO, per-project analytics, email support), Business ($199/mo: adds HA/replication, priority diff --git a/long-term-support-build-cache/index.html b/long-term-support-build-cache/index.html new file mode 100644 index 0000000..f06ee00 --- /dev/null +++ b/long-term-support-build-cache/index.html @@ -0,0 +1,123 @@ + + + + + +Long-term support for a build cache: what is supported today + + + + + + + + +
    + +
    Infrastructure worth keeping — and kept patched.
    +
    +
    +

    Long-term support for a build cache: what is supported today

    +

    "LTS" means a version that keeps getting fixes for a stated period, so you can stay on it. Here is what that means for FosterStack Cache today, with no promises beyond what exists.

    + +

    What is supported today

    +

    The latest minor release. That is the whole policy for now, and our security policy says so. There is no separate long-term-support line yet, no list of older versions that get fixes, and no support period to quote.

    + +

    What that means for you

    + + +

    How to run it with that in mind

    + + +

    What we are not saying

    +

    We are not announcing an LTS line, a support period, or a date. If one ever exists, it will be described here, in present tense, once it ships. The project is early.

    + +

    Related: how releases are built and signed and what to check in a replacement build cache.

    +
    + Questions and answers + FosterStack home +
    +
    + + + diff --git a/maven-remote-build-cache-setup/index.html b/maven-remote-build-cache-setup/index.html new file mode 100644 index 0000000..c054a6e --- /dev/null +++ b/maven-remote-build-cache-setup/index.html @@ -0,0 +1,177 @@ + + + + + +Maven build cache extension: remote server setup + + + + + + + + +
    + +
    Infrastructure worth keeping — and kept patched.
    +
    +
    +

    Maven build cache extension: remote server setup

    +

    Share Maven build results across machines using Apache's Build Cache Extension and a self-hosted cache server. About ten minutes.

    + +

    Who owns what

    +

    The client is Apache's Maven Build Cache Extension. We maintain the server. If your hit rate looks low, that is usually the extension's own project settings (which files count as inputs, what to skip), not the server. Start with the extension's configuration guide.

    + +

    1. Add the extension

    +

    In .mvn/extensions.xml (create it if it is missing):

    +
    <extensions>
    +  <extension>
    +    <groupId>org.apache.maven.extensions</groupId>
    +    <artifactId>maven-build-cache-extension</artifactId>
    +    <version>1.2.3</version>
    +  </extension>
    +</extensions>
    +

    Check Maven Central for the current version. Pin one rather than a range. The versions we test against are the ones in our Maven doc; a version outside that list is not something we claim to have tested.

    + +

    2. Point it at the server, and switch uploads on

    +

    Create .mvn/maven-build-cache-config.xml. Two details matter, and both are easy to miss:

    + +
    <?xml version="1.0" encoding="UTF-8"?>
    +<cache xmlns="http://maven.apache.org/BUILD-CACHE-CONFIG/1.2.0"
    +       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    +       xsi:schemaLocation="http://maven.apache.org/BUILD-CACHE-CONFIG/1.2.0 https://maven.apache.org/xsd/build-cache-config-1.2.0.xsd">
    +  <configuration>
    +    <enabled>true</enabled>
    +    <!-- FosterStack Cache — remote Maven build cache.
    +         https://github.com/fosterstack/cache -->
    +    <remote enabled="true" saveToRemote="true" id="fosterstack-cache">
    +      <url>https://cache.example.com/</url>
    +    </remote>
    +  </configuration>
    +</cache>
    +

    Keep the comment: a cache setting that points at an unfamiliar host looks like data theft to a reviewer without it. Keep the trailing slash on the URL. Use HTTPS for a real server.

    + +

    3. Add the password, if the server has one

    +

    In ~/.m2/settings.xml, or your CI system's equivalent. The id must match the id in step 2:

    +
    <settings>
    +  <servers>
    +    <server>
    +      <id>fosterstack-cache</id>
    +      <username>maven</username>
    +      <password>your-password-or-CI-secret</password>
    +    </server>
    +  </servers>
    +</settings>
    +

    If the server has no password set, skip this step. For laptops, the server's optional read-only login lets them read the cache without being able to write to it.

    + +

    4. Build, then build again

    +

    This is a real run: Maven 3.10.0, extension 1.2.3, and a 0.2.1 server, on a one-file project. First build:

    +
    mvn package
    +
    +[INFO] Attempting to restore project demo:demo from build cache
    +[ERROR] Error downloading cache item: https://cache.example.com//v1.1/demo/demo/ebb9bceba2f7a6bc/buildinfo.xml
    +[INFO] Remote cache is incomplete or missing, trying local build for demo:demo
    +[INFO] Saved to remote cache https://cache.example.com//v1.1/demo/demo/ebb9bceba2f7a6bc/demo.jar
    +[INFO] Saved to remote cache https://cache.example.com//v1.1/demo/demo/ebb9bceba2f7a6bc/buildinfo.xml
    +[INFO] BUILD SUCCESS
    +[INFO] Saved to remote cache https://cache.example.com//v1.1/demo/demo/9d9dcaf9-3b6b-40fb-9cb5-da97c975eab5/build-cache-report.xml
    +

    The line starting [ERROR] Error downloading cache item is the normal first-time miss, the server saying it does not have that entry yet. The build then ran for real and saved its result to the server. The server's status page showed it:

    +
    $ curl -s -u maven:demo-pass localhost:8080/statusz | grep -E '"store_entries"|"cache_hits"|"cache_misses"'
    +  "store_entries": 3,
    +  "cache_hits": 0,
    +  "cache_misses": 3,
    +

    Now delete target and the extension's local cache folder, so the only copy left is on the server, and build again:

    +
    rm -rf target build-cache   # 'build-cache' is the extension's local folder, next to your local repository
    +mvn package
    +
    +[INFO] Attempting to restore project demo:demo from build cache
    +[INFO] Found cached build, restoring demo:demo from cache by checksum ebb9bceba2f7a6bc
    +[INFO] BUILD SUCCESS
    +

    "Found cached build, restoring from cache" is the hit. Maven restored the result from the server instead of building it again.

    +

    Maven 3.10 also prints a warning that the credentials for fosterstack-cache are being used for an address with no matching repository entry. In our run the build worked regardless.

    +

    If you see no hit, check that the id in the password file matches, that saveToRemote is on for the machine that built first, and that the second run starts from a clean checkout.

    + +

    Mixed Gradle and Maven shop? Both can use the same server. See Gradle and Maven setup. For CI runners, see Jenkins and CI build cache.

    +
    + Gradle and Maven setup + FosterStack home +
    +
    + + + diff --git a/migrate-build-cache-node/index.html b/migrate-build-cache-node/index.html new file mode 100644 index 0000000..b20235f --- /dev/null +++ b/migrate-build-cache-node/index.html @@ -0,0 +1,212 @@ + + + + + +Migrate from Build Cache Node: Gradle and Maven setup + + + + + + + + +
    + +
    Infrastructure worth keeping — and kept patched.
    +
    +
    +

    Migrate from Build Cache Node: Gradle and Maven setup

    +

    Stand up a replacement cache, change one URL, check that it works. About 30 minutes for Gradle. There is no data to move.

    + +

    Before you start

    +

    A build cache holds copies of things your builds can always rebuild, so there is nothing to export from the old node. Your first build on the new server just fills it. If you have not read why the node is going away, see Build Cache Node end of life.

    + +

    Step 1: Run the new cache server

    +
    docker run -d -p 127.0.0.1:8080:8080 -v fscache-data:/home/nonroot ghcr.io/fosterstack/cache:latest
    +

    For anything beyond a first look, pin a specific version instead of latest. This command listens on this machine only. For other machines to use it, put an HTTPS proxy in front. Other ways to run it: bare binary with systemd, Kubernetes, or a disconnected network.

    +

    If the server can be reached by anyone you do not trust, turn on Basic Auth. Anyone who can write to the cache can change what your later builds use.

    + +

    Step 2: Gradle build cache setup

    +

    Turn the cache on, in gradle.properties:

    +
    org.gradle.caching=true
    +

    Then point Gradle at the new server in settings.gradle.kts. If you used the old node, you only change the URL:

    +
    // FosterStack Cache — remote Gradle build cache.
    +// https://github.com/fosterstack/cache
    +buildCache {
    +    remote<HttpBuildCache> {
    +        url = uri("https://cache.example.com/")
    +        isPush = true
    +        credentials {
    +            username = "gradle"
    +            password = System.getenv("FSCACHE_PASSWORD")
    +        }
    +    }
    +}
    +

    Or in settings.gradle (Groovy):

    +
    // FosterStack Cache — remote Gradle build cache.
    +// https://github.com/fosterstack/cache
    +buildCache {
    +    remote(HttpBuildCache) {
    +        url = 'https://cache.example.com/'
    +        push = true
    +        credentials {
    +            username = 'gradle'
    +            password = System.getenv('FSCACHE_PASSWORD')
    +        }
    +    }
    +}
    +

    Keep the comment lines: a cache block that points at an unfamiliar host looks like data theft to a reviewer without them. Keep the trailing slash on the URL. Leave the credentials block in even if the server has no password yet; it is ignored then.

    + +

    Step 3: Maven build cache setup

    +

    Maven uses Apache's Build Cache Extension. FosterStack Cache implements the server side of it.

    +

    Add the extension in .mvn/extensions.xml (check Maven Central for the current version):

    +
    <extensions>
    +  <extension>
    +    <groupId>org.apache.maven.extensions</groupId>
    +    <artifactId>maven-build-cache-extension</artifactId>
    +    <version>1.2.3</version>
    +  </extension>
    +</extensions>
    +

    Point it at the server in .mvn/maven-build-cache-config.xml:

    +
    <?xml version="1.0" encoding="UTF-8"?>
    +<cache xmlns="http://maven.apache.org/BUILD-CACHE-CONFIG/1.2.0"
    +       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    +       xsi:schemaLocation="http://maven.apache.org/BUILD-CACHE-CONFIG/1.2.0 https://maven.apache.org/xsd/build-cache-config-1.2.0.xsd">
    +  <configuration>
    +    <enabled>true</enabled>
    +    <!-- FosterStack Cache — remote Maven build cache.
    +         https://github.com/fosterstack/cache -->
    +    <remote enabled="true" saveToRemote="true" id="fosterstack-cache">
    +      <url>https://cache.example.com/</url>
    +    </remote>
    +  </configuration>
    +</cache>
    +

    The remote element must be inside configuration, and saveToRemote="true" is what makes the machine upload; without it the server stays empty. If the server uses Basic Auth, add a matching <server> with the same id (fosterstack-cache) in ~/.m2/settings.xml. Then run mvn install.

    +

    The extension itself is Apache's project. If your hit rate looks low, start with the extension's own configuration guide.

    + +

    Step 4: Check that it works

    +

    For Gradle, run the build twice:

    +
    ./gradlew build --build-cache
    +

    On the second run, look for FROM-CACHE next to tasks. Gradle also has a local cache that gives hits even when the remote is broken, so to test the remote itself, clear the local one and run with -i:

    +
    rm -rf ~/.gradle/caches/build-cache-1
    +./gradlew --stop
    +./gradlew build --build-cache -i
    +

    On the server, the hit and miss counters should move:

    +
    curl -s https://cache.example.com/metrics | grep -E 'fscache_cache_(hits|misses)_total'
    +

    For Maven, build twice from a clean checkout and look for cache hits in the log for unchanged modules.

    + +

    Step 5: Retire the old node

    +

    Once builds hit the new server, stop the old Build Cache Node container.

    + +

    Security is the difference

    +

    Security is what sets this product apart. The release items below ship in the current release. The supply-chain items describe how we work today, in our own build and CI. You can check each one yourself.

    +

    Signed and verifiable

    + +

    Supply chain

    + +

    Small, quiet and patched

    + + +

    What you give up

    +

    The old node had a small web page with a purge button. FosterStack Cache has no settings page. You set it with flags and environment variables, and a read-only status page shows size and hit counts. To empty the cache, remove its volume and start again.

    + +

    Full details: Gradle docs, Maven docs, migration guide on GitHub.

    +
    + FosterStack Cache on GitHub + FosterStack home +
    +
    + + + diff --git a/monitor-build-cache-prometheus-grafana/index.html b/monitor-build-cache-prometheus-grafana/index.html new file mode 100644 index 0000000..ee4477d --- /dev/null +++ b/monitor-build-cache-prometheus-grafana/index.html @@ -0,0 +1,162 @@ + + + + + +Monitor a Gradle build cache with Prometheus and Grafana + + + + + + + + +
    + +
    Infrastructure worth keeping — and kept patched.
    +
    +
    +

    Monitor a Gradle build cache with Prometheus and Grafana

    +

    The cache server exposes Prometheus metrics and a read-only status page. This page lists what is there, a scrape setting, a few queries, and where to get a ready-made Grafana dashboard.

    + +

    Metrics need no password

    +

    The /metrics and /healthz paths stay open even when Basic Auth is on, so a Prometheus scraper needs no credentials. The status page, /statusz, sits behind Basic Auth when it is enabled.

    + +

    A scrape setting

    +
    scrape_configs:
    +  - job_name: fscache
    +    static_configs:
    +      - targets: ['cache.example.com:8080']
    +

    This is a plain example. Use your own address, and HTTPS if you have put the server behind a proxy.

    + +

    The metrics

    +

    These are the names a running 0.2.1 server reports:

    + + + + + + + + + + + +
    MetricTypeWhat it tells you
    fscache_cache_hits_totalcounterRequests answered from the cache
    fscache_cache_misses_totalcounterRequests for something not stored
    fscache_store_bytesgaugeHow much is stored now
    fscache_store_entriesgaugeHow many entries are stored
    fscache_evicted_entries_totalcounterEntries removed to stay under the size cap
    fscache_http_requests_totalcounterRequests, by method and status
    fscache_http_request_duration_secondshistogramHow long requests take
    fscache_bytes_read_totalcounterBytes sent to clients
    fscache_bytes_written_totalcounterBytes received from clients
    + +

    Queries worth having

    +
    # hit rate over the last 5 minutes
    +sum(rate(fscache_cache_hits_total[5m]))
    +  / clamp_min(sum(rate(fscache_cache_hits_total[5m])) + sum(rate(fscache_cache_misses_total[5m])), 0.0001)
    +
    +# entries removed per second to stay under the cap
    +sum(rate(fscache_evicted_entries_total[5m]))
    +
    +# 95th percentile request time
    +histogram_quantile(0.95, sum by (le, method) (rate(fscache_http_request_duration_seconds_bucket[5m])))
    +

    A hit rate that never climbs, together with constant evictions, usually means the size cap is too small. See server sizing.

    + +

    A ready-made Grafana dashboard

    +

    The repository includes a dashboard with eight panels: hit rate, cache size against the cap, entries, evictions, hits and misses, requests by status, throughput, and request latency. Download grafana-dashboard.json and import it in Grafana.

    + +

    The status page

    +

    For a quick look without Prometheus, /statusz returns the same numbers in one place, in a browser or as JSON. This is what a freshly started 0.2.1 server returned after one stored entry and two requests:

    +
    $ curl -s localhost:8080/statusz
    +{
    +  "version": "v0.2.1",
    +  "revision": "0bfc498ecfca3e38cb4a456acda44359a1620c5a",
    +  "fips140": false,
    +  "fips140_note": "off",
    +  "uptime_seconds": 2.118684875,
    +  "uptime": "2s",
    +  "store_bytes": 5,
    +  "max_bytes": 1073741824,
    +  "store_entries": 1,
    +  "cache_hits": 1,
    +  "cache_misses": 1,
    +  "hit_ratio": 0.5,
    +  "evicted_entries": 0,
    +  "auth_enabled": false
    +}
    + +

    Related: Gradle remote build cache not working and Gradle build cache with Docker Compose.

    +
    + Grafana dashboard + FosterStack home +
    +
    + + + diff --git a/regulated/index.html b/regulated/index.html index 6d21657..1e90f42 100644 --- a/regulated/index.html +++ b/regulated/index.html @@ -93,7 +93,7 @@

    For teams entering regulated markets

    What our piece does — and does not do

    FosterStack Cache is one component in your pipeline: a self-hosted remote - build cache. For that component, we do the evidence work a compliance + build cache. Maven too, through the Apache Maven Build Cache Extension. For that component, we do the evidence work a compliance program needs: the -fips build links Go's FIPS 140-3 validated cryptographic module (CMVP certificate #5247), and every release ships with an SBOM, SLSA provenance, keyless signatures, and @@ -108,16 +108,39 @@

    What our piece does — and does not do

    signed, and checkable instead of a shrug.

    -

    The Compliance tier

    +

    Security is the difference

    +

    Security is what sets this product apart. The release items below ship in the current release. The supply-chain items describe how we work today, in our own build and CI. You can check each one yourself.

    +

    Signed and verifiable

    + +

    Supply chain

    + +

    Small, quiet and patched

    + + +

    What is free, and what is not available yet

    - The evidence underneath is public and free — SBOMs, SLSA provenance, - signatures, VEX statements, the FIPS 140-3 validated module and its - certificate number — verifiable by anyone, no account, no purchase. What - the Compliance tier sells is the authored work on - top: a FIPS applicability statement mapping the validated module boundary - onto this product, per-release attestation letters addressed to you and - signed by FosterStack LLC, security-questionnaire support up to 8 hours - per year (async), and named-version LTS. + The evidence underneath is public and free: SBOMs, build provenance, + signatures, VEX statements, and the FIPS 140-3 validated module and its + certificate number. Anyone can verify it, with no account and no purchase. + Paid support tiers are not available yet.

    Verify everything for free first

    @@ -130,9 +153,15 @@

    Verify everything for free first

    — including what it says is not yet proven — and check the requirements matrix that maps every product promise to its evidence. If the free - evidence doesn't hold up under your audit, the paid tier wouldn't either. + evidence doesn't hold up under your audit, nothing we add on top would either. That's the test we invite.

    +

    + Step-by-step, with the output of each command from a real release: + Verify a release yourself. To run the + -fips image and confirm FIPS mode from its own startup log: + FIPS 140-3 build cache. +