From 533c5bee41355fdca2048a1757e7668472631843 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:16:48 -0400 Subject: [PATCH 01/78] Page: migrate from Build Cache Node, Gradle and Maven setup --- migrate-build-cache-node/index.html | 183 ++++++++++++++++++++++++++++ sitemap.xml | 4 + 2 files changed, 187 insertions(+) create mode 100644 migrate-build-cache-node/index.html diff --git a/migrate-build-cache-node/index.html b/migrate-build-cache-node/index.html new file mode 100644 index 0000000..8c33bd1 --- /dev/null +++ b/migrate-build-cache-node/index.html @@ -0,0 +1,183 @@ + + + + + +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 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. Put the server behind HTTPS before real builds use it. Other ways to run it (bare binary with systemd, Kubernetes, offline networks) are in the install guide.

+

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. The server part is built; its acceptance testing is still in progress.

+

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">
+  <configuration>
+    <enabled>true</enabled>
+  </configuration>
+  <!-- FosterStack Cache — remote Maven build cache.
+       https://github.com/fosterstack/cache -->
+  <remote enabled="true" id="fosterstack-cache">
+    <url>https://cache.example.com/</url>
+  </remote>
+</cache>
+

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.

+ +

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/sitemap.xml b/sitemap.xml index 8c1e470..1f932e9 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -16,4 +16,8 @@ weekly 1.0 + + https://fosterstack.com/migrate-build-cache-node/ + 2026-10-08 + From 9e597fa052ec31a89b0d6d1449c0a7d26918cb1e Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:17:46 -0400 Subject: [PATCH 02/78] Page: Jenkins and CI build cache for Gradle and Maven --- jenkins-ci-build-cache/index.html | 137 ++++++++++++++++++++++++++++++ sitemap.xml | 4 + 2 files changed, 141 insertions(+) create mode 100644 jenkins-ci-build-cache/index.html diff --git a/jenkins-ci-build-cache/index.html b/jenkins-ci-build-cache/index.html new file mode 100644 index 0000000..2134259 --- /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 the password.

+ +

Let CI write, let developers read

+

The simplest safe setup is that only CI uploads to the cache and laptops only download. In settings.gradle.kts:

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

This is the Gradle client choosing not to upload. It is not a permission on the server. Today the server has one password, and anyone holding it can write. 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 to anything but localhost unless you opt in, which is a useful warning, not a nuisance. 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. The server side for Maven is built; its acceptance testing is still in progress. See the Gradle and Maven setup page.

+ +
+ Gradle and Maven setup + FosterStack home +
+
+ + + diff --git a/sitemap.xml b/sitemap.xml index 8c1e470..9762aef 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -16,4 +16,8 @@ weekly 1.0 + + https://fosterstack.com/jenkins-ci-build-cache/ + 2026-10-08 + From 8a84ddc9513b89001abc16fc868040164fdbfc92 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:18:16 -0400 Subject: [PATCH 03/78] Migrate page: localhost-only docker command, direct doc links --- migrate-build-cache-node/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/migrate-build-cache-node/index.html b/migrate-build-cache-node/index.html index 8c33bd1..dfd3d53 100644 --- a/migrate-build-cache-node/index.html +++ b/migrate-build-cache-node/index.html @@ -85,8 +85,8 @@

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 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. Put the server behind HTTPS before real builds use it. Other ways to run it (bare binary with systemd, Kubernetes, offline networks) are in the install guide.

+
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

From 144be6c7e453ba817082cfc7a737d575cbb84c05 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:18:42 -0400 Subject: [PATCH 04/78] Page: Build Cache Node replacement FAQ --- build-cache-node-faq/index.html | 144 ++++++++++++++++++++++++++++++++ sitemap.xml | 4 + 2 files changed, 148 insertions(+) create mode 100644 build-cache-node-faq/index.html diff --git a/build-cache-node-faq/index.html b/build-cache-node-faq/index.html new file mode 100644 index 0000000..4047fcc --- /dev/null +++ b/build-cache-node-faq/index.html @@ -0,0 +1,144 @@ + + + + + +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.

+ +

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?

+

The Maven side is built, but its acceptance testing is still in progress, so treat it as newer than the Gradle side. 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. Nobody runs it in a real build pipeline yet except us. 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 password. The server has one password and one permission level today. Turn on Basic Auth anywhere untrusted people could reach the port, and share the password only with CI and developers you trust to change your code. Telling Gradle not to upload is a client choice, not a server rule.

+ +

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 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. That is also where the roadmap gets argued with.

+ +
+ Gradle and Maven setup + FosterStack home +
+
+ + + diff --git a/sitemap.xml b/sitemap.xml index 8c1e470..0591707 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -16,4 +16,8 @@ weekly 1.0 + + https://fosterstack.com/build-cache-node-faq/ + 2026-10-08 + From 987f8517afc863d6c5a0e105c58f0a6ea944b15a Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:19:44 -0400 Subject: [PATCH 05/78] FAQ: drop a chatty sentence --- build-cache-node-faq/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build-cache-node-faq/index.html b/build-cache-node-faq/index.html index 4047fcc..08f3586 100644 --- a/build-cache-node-faq/index.html +++ b/build-cache-node-faq/index.html @@ -9,7 +9,7 @@ - + + + +
+ +
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 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 approved images are copied to the public registry 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/sitemap.xml b/sitemap.xml index 8c1e470..de6df3f 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -16,4 +16,8 @@ weekly 1.0 + + https://fosterstack.com/how-we-build/ + 2026-10-08 + From b983dc2d74db6c0ae633a2466e43f7ea8ad08e70 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:20:16 -0400 Subject: [PATCH 07/78] How we build: drop the no-rebuild phrase pending the README conflict --- how-we-build/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/how-we-build/index.html b/how-we-build/index.html index cba6653..885d74e 100644 --- a/how-we-build/index.html +++ b/how-we-build/index.html @@ -95,7 +95,7 @@

The steps a release goes through

  • Reproducibility check. The image is assembled a second time on a separate machine. The results must be identical, or the release fails.
  • 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.
  • Authorization. One step checks that every earlier step produced signed proof for these exact files. Anything missing stops the release.
  • -
  • Publish. The approved images are copied to the public registry without a rebuild, and checked again the way a customer would check them.
  • +
  • Publish. The approved images are published to the public registry and checked again the way a customer would check them.
  • The full description, as the pipeline actually runs, is in RELEASING.md.

    From 24e61a6961b8c32eda40fd6a58feea6f6cd901b5 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:20:24 -0400 Subject: [PATCH 08/78] Home: link the new pages from the notice and footer --- index.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index 185979b..c921950 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.

    @@ -220,7 +221,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

    From ff6147989cef3f72e464e736ea44a87f1af37e2d Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:20:35 -0400 Subject: [PATCH 09/78] llms.txt: list the new guides; version line v0.2 --- llms.txt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/llms.txt b/llms.txt index 624e4c1..a31db02 100644 --- a/llms.txt +++ b/llms.txt @@ -20,6 +20,14 @@ 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. +- [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,7 +57,7 @@ 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. From e76ba88fec57273a169e62bca61e10348e8dbc3e Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:20:53 -0400 Subject: [PATCH 10/78] Page: Gradle remote build cache not working --- gradle-remote-cache-not-working/index.html | 138 +++++++++++++++++++++ sitemap.xml | 4 + 2 files changed, 142 insertions(+) create mode 100644 gradle-remote-cache-not-working/index.html diff --git a/gradle-remote-cache-not-working/index.html b/gradle-remote-cache-not-working/index.html new file mode 100644 index 0000000..6144c77 --- /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:// to anything except localhost unless you set isAllowInsecureProtocol = true. 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/sitemap.xml b/sitemap.xml index 8c1e470..b949e0f 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -16,4 +16,8 @@ weekly 1.0 + + https://fosterstack.com/gradle-remote-cache-not-working/ + 2026-10-08 + From 935792935c6f4d3ae8dab6051c33889c12f2cfa0 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:21:27 -0400 Subject: [PATCH 11/78] Page: what to check in a replacement build cache --- sitemap.xml | 4 + verify-replacement-build-cache/index.html | 125 ++++++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 verify-replacement-build-cache/index.html diff --git a/sitemap.xml b/sitemap.xml index 8c1e470..4ff7cda 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -16,4 +16,8 @@ weekly 1.0 + + https://fosterstack.com/verify-replacement-build-cache/ + 2026-10-08 + diff --git a/verify-replacement-build-cache/index.html b/verify-replacement-build-cache/index.html new file mode 100644 index 0000000..cd13c5d --- /dev/null +++ b/verify-replacement-build-cache/index.html @@ -0,0 +1,125 @@ + + + + + +What to check in a replacement build cache + + + + + + + + +
    + +
    Infrastructure worth keeping — and kept patched.
    +
    +
    +

    What to check in a replacement build cache

    +

    A build cache sits inside your supply chain: what it stores ends up in your builds. Before you move to a replacement, ask these eight questions of any candidate. We answer each one for FosterStack Cache, including where the answer is not good yet.

    + +

    1. Does it speak your build tool's protocol?

    +

    It should work with the tool's own remote cache setting, with nothing extra to install on developer machines. How to test: build twice with the local cache turned off and look for FROM-CACHE on the second run. FosterStack Cache: Gradle through its HttpBuildCache setting; Maven through Apache's Build Cache Extension, where the server side is built and its acceptance testing is still in progress.

    + +

    2. Who can write to it?

    +

    Anything that can write to the cache can change what later builds use. Ask for authentication, and ask what permission levels exist. FosterStack Cache: Basic Auth, with one password and one permission level today. Read-only clients and per-writer identity are not built yet.

    + +

    3. Can you verify what you download?

    +

    Look for signatures, a software bill of materials, build provenance and vulnerability statements, and check whether you need an account to see them. FosterStack Cache: signed images and checksums, an SBOM per binary archive, build provenance and a VEX file, all public. See how we build.

    + +

    4. Is it kept patched?

    +

    Ask how often published files are rescanned and how fast fixes ship. FosterStack Cache: a release is blocked on scanner findings unless a VEX statement explains them, and published images are rescanned daily. We aim to ship dependency fixes within 48 hours of disclosure. That is a target, not a promise.

    + +

    5. Does it call home?

    +

    A server in your build network should not need to reach out. FosterStack Cache: no telemetry, no update check, no licence check. It can run on a network with no route to the internet.

    + +

    6. How do you run and watch it?

    +

    You want size limits, metrics and a way to see it is healthy. FosterStack Cache: size-capped eviction, Prometheus metrics, a health check and a read-only status page. There is no admin web console, on purpose.

    + +

    7. What are the licence terms, and what if the project goes away?

    +

    You are looking at this because a free product ended. FosterStack Cache: MIT licensed, one static binary, nothing you need from us to keep running it. Paid tiers are described for the future, are not built, and security patches are never withheld from the free tier.

    + +

    8. Who already runs it?

    +

    FosterStack Cache: nobody in a real build pipeline yet except us. If that is a dealbreaker today, it is a fair answer. The README lists what has shipped and what has not.

    + +
    + Gradle and Maven setup + Questions and answers +
    +
    + + + From 4e8bdd9788906769df7bc0f55212c29a75f7c6d0 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:21:37 -0400 Subject: [PATCH 12/78] How we build: published images are the tested digests, promoted without a rebuild --- how-we-build/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/how-we-build/index.html b/how-we-build/index.html index 885d74e..04e120e 100644 --- a/how-we-build/index.html +++ b/how-we-build/index.html @@ -91,11 +91,11 @@

    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. Build. The binaries are built, with checksums and a software bill of materials for each archive.
    3. -
    4. Container images. Each image is the unmodified distroless base plus one binary.
    5. +
    6. Container images. Each image is assembled once, from the unmodified distroless base plus one binary.
    7. Reproducibility check. The image is assembled a second time on a separate machine. The results must be identical, or the release fails.
    8. 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.
    9. Authorization. One step checks that every earlier step produced signed proof for these exact files. Anything missing stops the release.
    10. -
    11. Publish. The approved images are published to the public registry and checked again the way a customer would check them.
    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.

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

    From 8c50ab6e3717c8bc1dbeaaad2a75bdeca6343a87 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:21:48 -0400 Subject: [PATCH 13/78] Page: long-term support for a build cache, what is supported today --- long-term-support-build-cache/index.html | 123 +++++++++++++++++++++++ sitemap.xml | 4 + 2 files changed, 127 insertions(+) create mode 100644 long-term-support-build-cache/index.html diff --git a/long-term-support-build-cache/index.html b/long-term-support-build-cache/index.html new file mode 100644 index 0000000..7629d80 --- /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, and nobody runs it in a real build pipeline yet except us.

    + +

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

    +
    + Questions and answers + FosterStack home +
    +
    + + + diff --git a/sitemap.xml b/sitemap.xml index 8c1e470..deb0511 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -16,4 +16,8 @@ weekly 1.0 + + https://fosterstack.com/long-term-support-build-cache/ + 2026-10-08 + From 814ca382166654ff3b1a4ebd2cc83d6cf290dd7f Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:22:04 -0400 Subject: [PATCH 14/78] LTS page: say only what the policy says --- long-term-support-build-cache/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/long-term-support-build-cache/index.html b/long-term-support-build-cache/index.html index 7629d80..eda7560 100644 --- a/long-term-support-build-cache/index.html +++ b/long-term-support-build-cache/index.html @@ -86,7 +86,7 @@

    What is supported today

    What that means for you

    Verify before you cross the gap

    @@ -108,7 +108,7 @@

    Install on the isolated side

    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
    -

    Put an HTTPS proxy in front of it before other machines use it. The offline install guide has the full detail, including what to do for later upgrades.

    +

    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.

    From 4d4950b1f6457ed26f1a74d3b726d9a49fe15028 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:25:24 -0400 Subject: [PATCH 25/78] CI page: a login, not the password --- jenkins-ci-build-cache/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jenkins-ci-build-cache/index.html b/jenkins-ci-build-cache/index.html index a8d6d1c..d3ccfc9 100644 --- a/jenkins-ci-build-cache/index.html +++ b/jenkins-ci-build-cache/index.html @@ -83,7 +83,7 @@

    Jenkins and CI build cache for Gradle and Maven

    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 the password.

    +

    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:

    From 59b8df84669cf16e78dfe459f331587e176841f5 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:25:25 -0400 Subject: [PATCH 26/78] Home + llms.txt: link the Maven setup page --- index.html | 1 + llms.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/index.html b/index.html index 4ef36cd..20d8b51 100644 --- a/index.html +++ b/index.html @@ -207,6 +207,7 @@

    Guides

  • Migrate from Build Cache Node: Gradle and Maven setup
  • Jenkins and CI build cache
  • Gradle remote build cache not working
  • +
  • Maven remote build cache setup
  • How big a server does a build cache need?
  • Install on an air-gapped network
  • What to check in a replacement build cache
  • diff --git a/llms.txt b/llms.txt index e1edaae..ea89eb5 100644 --- a/llms.txt +++ b/llms.txt @@ -27,6 +27,7 @@ verifiable against the CI run that produced them. - [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. - [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. From 5716716bb10f254cfb2cf2059fd978f0879f43b3 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:26:39 -0400 Subject: [PATCH 27/78] Page: security policy, plus /.well-known/security.txt --- .well-known/security.txt | 6 ++ bin/check-file-allowlist.sh | 1 + security/index.html | 137 ++++++++++++++++++++++++++++++++++++ sitemap.xml | 4 ++ 4 files changed, 148 insertions(+) create mode 100644 .well-known/security.txt create mode 100644 security/index.html 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/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/security/index.html b/security/index.html new file mode 100644 index 0000000..94e5054 --- /dev/null +++ b/security/index.html @@ -0,0 +1,137 @@ + + + + + +Security policy: how to report a vulnerability + + + + + + + + +
    + +
    Infrastructure worth keeping — and kept patched.
    +
    +
    +

    Security policy: how to report a vulnerability

    +

    Found a security problem in FosterStack Cache or on this website? Tell us privately. Here is how, what happens next, and what we sign and publish so you can check our work.

    + +

    Report a vulnerability

    + +

    Send the report, not a patch. You will be credited in the fix. Please do not post details publicly before a fix ships.

    + +

    What we aim for

    +

    We aim to ship fixes for dependency vulnerabilities within 48 hours of public disclosure. That is a target, not a promise you can hold us to. Security patches are never withheld from the free tier: the same fix ships to everyone on the same day.

    +

    The supported version is the latest minor release.

    + +

    What we sign and publish

    +

    Releases are built only in CI. Each one comes with files you can check without an account:

    + +

    The commands are under "Verifying a release" in RELEASING.md. See also how we build.

    +

    Every published image is scanned again every day, so a vulnerability disclosed against something we already shipped raises an issue.

    + +

    What the server does with your data

    +

    The server sends no telemetry and needs no connection to FosterStack: no usage reporting, no licence check, no update ping. It stores what your build tool sends it, which is cache keys and the files they point to. You can check that it makes no outbound connections, and the command is in the security policy on GitHub.

    +

    This website has no forms, no cookies, no analytics and no third-party requests.

    + +

    What we do not claim

    + + +

    Machine-readable contact: security.txt.

    +
    + Full security policy on GitHub + FosterStack home +
    +
    + + + diff --git a/sitemap.xml b/sitemap.xml index 8c1e470..a9f9611 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -16,4 +16,8 @@ weekly 1.0 + + https://fosterstack.com/security/ + 2026-10-08 + From 0d5389515cc75c36871e2471f8f59e7e86d50d16 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:28:26 -0400 Subject: [PATCH 28/78] Page: Gradle build cache with Docker Compose --- gradle-build-cache-docker-compose/index.html | 141 +++++++++++++++++++ sitemap.xml | 4 + 2 files changed, 145 insertions(+) create mode 100644 gradle-build-cache-docker-compose/index.html 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/sitemap.xml b/sitemap.xml index 8c1e470..8818c4f 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -16,4 +16,8 @@ weekly 1.0 + + https://fosterstack.com/gradle-build-cache-docker-compose/ + 2026-10-08 + From a9c84eb2abdc838053059db6b3e4dee6316f7074 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:28:50 -0400 Subject: [PATCH 29/78] Page: Gradle build cache on Kubernetes --- gradle-build-cache-kubernetes/index.html | 127 +++++++++++++++++++++++ sitemap.xml | 4 + 2 files changed, 131 insertions(+) create mode 100644 gradle-build-cache-kubernetes/index.html diff --git a/gradle-build-cache-kubernetes/index.html b/gradle-build-cache-kubernetes/index.html new file mode 100644 index 0000000..37c5b78 --- /dev/null +++ b/gradle-build-cache-kubernetes/index.html @@ -0,0 +1,127 @@ + + + + + +Gradle build cache on Kubernetes + + + + + + + + +
    + +
    Infrastructure worth keeping — and kept patched.
    +
    +
    +

    Gradle build cache on Kubernetes

    +

    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.

    + +

    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 any non-localhost http:// address. 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/sitemap.xml b/sitemap.xml index 8c1e470..ee811a2 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -16,4 +16,8 @@ weekly 1.0 + + https://fosterstack.com/gradle-build-cache-kubernetes/ + 2026-10-08 + From 9dc72a40bae8a11729b3fac6136ca0a1b4c0472a Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:29:13 -0400 Subject: [PATCH 30/78] Home + llms.txt: link compose, Kubernetes and security pages --- index.html | 3 +++ llms.txt | 3 +++ 2 files changed, 6 insertions(+) diff --git a/index.html b/index.html index 20d8b51..cb2d80a 100644 --- a/index.html +++ b/index.html @@ -208,6 +208,9 @@

    Guides

  • Jenkins and CI build cache
  • Gradle remote build cache not working
  • Maven remote build cache setup
  • +
  • Gradle build cache with Docker Compose
  • +
  • Gradle build cache on Kubernetes
  • +
  • Security policy: how to report a vulnerability
  • How big a server does a build cache need?
  • Install on an air-gapped network
  • What to check in a replacement build cache
  • diff --git a/llms.txt b/llms.txt index ea89eb5..851cd85 100644 --- a/llms.txt +++ b/llms.txt @@ -28,6 +28,9 @@ verifiable against the CI run that produced them. - [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. - [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. From a8e427ff4725033c8b8be786b9da7817329989df Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:29:56 -0400 Subject: [PATCH 31/78] Page: changelog, release notes v0.1.0 and v0.2.1 quoted --- changelog/index.html | 129 +++++++++++++++++++++++++++++++++++++++++++ sitemap.xml | 4 ++ 2 files changed, 133 insertions(+) create mode 100644 changelog/index.html diff --git a/changelog/index.html b/changelog/index.html new file mode 100644 index 0000000..e58d519 --- /dev/null +++ b/changelog/index.html @@ -0,0 +1,129 @@ + + + + + +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.

    +
    +

    Changelog

    + +

    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

    + +

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

    +
    + All releases on GitHub + FosterStack home +
    +
    + + + diff --git a/sitemap.xml b/sitemap.xml index 8c1e470..d997a14 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -16,4 +16,8 @@ weekly 1.0 + + https://fosterstack.com/changelog/ + 2026-10-08 + From 52008f5f77ee1a4476f4ba2ea1d4390828ec2ccd Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:30:08 -0400 Subject: [PATCH 32/78] Security page: drop the credit promise --- security/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/index.html b/security/index.html index 94e5054..def9a7b 100644 --- a/security/index.html +++ b/security/index.html @@ -87,7 +87,7 @@

    Report a vulnerability

  • For this website: private vulnerability report on GitHub.
  • If GitHub's form is not available to you, open an issue that asks for a private channel, with no details in it. We will follow up.
  • -

    Send the report, not a patch. You will be credited in the fix. Please do not post details publicly before a fix ships.

    +

    Send the report, not a patch. Please do not post details publicly before a fix ships.

    What we aim for

    We aim to ship fixes for dependency vulnerabilities within 48 hours of public disclosure. That is a target, not a promise you can hold us to. Security patches are never withheld from the free tier: the same fix ships to everyone on the same day.

    From 8bf61934692bcc95372972cf981e25682f477aa1 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:30:22 -0400 Subject: [PATCH 33/78] Page: verify a container image signature with cosign --- sitemap.xml | 4 + .../index.html | 124 ++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 verify-container-image-signature-cosign/index.html diff --git a/sitemap.xml b/sitemap.xml index 8c1e470..d22fcc3 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -16,4 +16,8 @@ weekly 1.0 + + https://fosterstack.com/verify-container-image-signature-cosign/ + 2026-10-08 + diff --git a/verify-container-image-signature-cosign/index.html b/verify-container-image-signature-cosign/index.html new file mode 100644 index 0000000..7304b99 --- /dev/null +++ b/verify-container-image-signature-cosign/index.html @@ -0,0 +1,124 @@ + + + + + +Verify a container image signature with cosign + + + + + + + + +
    + +
    Infrastructure worth keeping — and kept patched.
    +
    +
    +

    Verify a container image signature with cosign

    +

    How to check that a FosterStack Cache image was signed by our release workflow and built by GitHub's CI, using cosign and the gh tool. It takes under a minute.

    + +

    Find the current version

    +
    VER=$(curl -fsSL https://api.github.com/repos/fosterstack/cache/releases/latest \
    +        | sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p')
    + +

    1. Check the signature

    +

    This answers one question: did our release workflow sign these exact bytes? Pin the workflow and the release tag, not "any workflow in the repository".

    +
    cosign verify ghcr.io/fosterstack/cache:${VER} \
    +  --certificate-identity-regexp="^https://github.com/fosterstack/cache/.github/workflows/stage-promote.yml@refs/tags/v${VER}$" \
    +  --certificate-oidc-issuer='https://token.actions.githubusercontent.com'
    +

    There is no signing key to steal or rotate. The certificate is short-lived and issued at the moment the workflow ran, and the signature is recorded in a public transparency log. No account is needed for this step.

    + +

    2. Check where it was built

    +

    This answers a different question: what produced these bytes? It reads a signed statement about the build, naming the repository, workflow and run. It needs a GitHub token (GH_TOKEN or gh auth login), even though the repository is public.

    +
    gh attestation verify oci://ghcr.io/fosterstack/cache:${VER} \
    +  --repo fosterstack/cache \
    +  --signer-workflow fosterstack/cache/.github/workflows/stage-image.yml \
    +  --source-ref "refs/tags/v${VER}"
    +

    Success names the workflow run that produced the image you pulled, which shows it came from the public build pipeline and not a developer machine.

    + +

    What these checks do not tell you

    +

    A valid signature says who signed. A build statement says how it was built. Neither says the image was scanned or tested. Those are separate signed statements attached to the same image digest, and you can verify them the same way. The workflow names above belong to the current release steps and change between versions, so use the verification guide for older releases.

    + +

    Related: how releases are built and signed and installing on an air-gapped network.

    +
    + Full verification guide + FosterStack home +
    +
    + + + From 9fcbef4bea4ccc2062004166dc057a8f63c6b333 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:30:52 -0400 Subject: [PATCH 34/78] Changelog: one plain line on what changed since v0.1.0, quoted from RELEASING.md --- changelog/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog/index.html b/changelog/index.html index e58d519..c8f89ae 100644 --- a/changelog/index.html +++ b/changelog/index.html @@ -107,6 +107,7 @@

    v0.1.0

    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.

    From 37f68b27d30963b80de80480b729f4834cfff870 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:31:10 -0400 Subject: [PATCH 35/78] Page: is a remote build cache safe, who can write to it --- remote-build-cache-security/index.html | 131 +++++++++++++++++++++++++ sitemap.xml | 4 + 2 files changed, 135 insertions(+) create mode 100644 remote-build-cache-security/index.html diff --git a/remote-build-cache-security/index.html b/remote-build-cache-security/index.html new file mode 100644 index 0000000..7871b99 --- /dev/null +++ b/remote-build-cache-security/index.html @@ -0,0 +1,131 @@ + + + + + +Is a remote build cache safe? Who can write to it + + + + + + + + +
    + +
    Infrastructure worth keeping — and kept patched.
    +
    +
    +

    Is a remote build cache safe? Who can write to it

    +

    A build cache speeds up builds, and if it is down or empty, builds are only slower. The risk is different: a cache that serves the wrong files. That depends on who can write to it.

    + +

    Two kinds of failure

    + + +

    Treat it as part of your supply chain

    +

    Anyone who can write to the cache can influence your builds. So:

    + + +

    "Push disabled" is not a permission

    +

    Setting isPush = false in a Gradle config is the client choosing not to upload. It is not a rule on the server. A client that holds the read-write password can still write. The server-side rule is the read-only login.

    + +

    What is not built yet

    +

    There is no per-person identity. A read-write login is shared by whoever holds it, so you cannot tell which writer did what.

    + +

    A simple setup

    + +

    The Jenkins and CI page shows how to wire it: Jenkins and CI build cache.

    + +

    Related: what to check in a replacement build cache and security policy.

    + +
    + + + diff --git a/sitemap.xml b/sitemap.xml index 8c1e470..abcf179 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -16,4 +16,8 @@ weekly 1.0 + + https://fosterstack.com/remote-build-cache-security/ + 2026-10-08 + From 6424a3267ef4ff6754b381e3d1cba2c27e673bf7 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:32:34 -0400 Subject: [PATCH 36/78] Changelog: link the v0.1.0 commit list instead of quoting tool names; blockquote style --- changelog/index.html | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/changelog/index.html b/changelog/index.html index c8f89ae..9bc1baa 100644 --- a/changelog/index.html +++ b/changelog/index.html @@ -70,6 +70,7 @@ footer { border-top: 1px solid var(--line); margin-top: 4rem; padding: 1.5rem 1.25rem 3rem; } footer div { max-width: 44rem; margin: 0 auto; color: var(--muted); font-size: .82rem; } a { color: var(--accent); } + blockquote { border-left: 3px solid var(--line); padding: .2rem 0 .2rem 1rem; margin: .8rem 0; color: var(--muted); } @@ -92,18 +93,7 @@

    v0.2.0

    v0.1.0

    Published 2026-08-22.

    -

    Changelog

    - +

    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

    From 585eb99f00e32530f3c2fe5002828679fe9e2405 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:32:35 -0400 Subject: [PATCH 37/78] Cache security page: wording nits --- remote-build-cache-security/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/remote-build-cache-security/index.html b/remote-build-cache-security/index.html index 7871b99..3ac7b3d 100644 --- a/remote-build-cache-security/index.html +++ b/remote-build-cache-security/index.html @@ -83,7 +83,7 @@

    Is a remote build cache safe? Who can write to it

    Two kinds of failure

    @@ -92,7 +92,7 @@

    Treat it as part of your supply chain

    From bd477b68a6c3ebc36bf7dc22ea9eb0f7a6eb2c86 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:35:36 -0400 Subject: [PATCH 38/78] Owner cold-read edit: Maven stated without the in-progress hedge --- jenkins-ci-build-cache/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jenkins-ci-build-cache/index.html b/jenkins-ci-build-cache/index.html index d3ccfc9..25eecbe 100644 --- a/jenkins-ci-build-cache/index.html +++ b/jenkins-ci-build-cache/index.html @@ -115,7 +115,7 @@

    Give the runner the password

    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. The server side for Maven is built; its acceptance testing is still in progress. See the Gradle and Maven setup page.

    +

    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 From 5afd95a663b818889f732c37f869dc0ec3540e81 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:35:37 -0400 Subject: [PATCH 39/78] Owner cold-read edit: Maven stated without the in-progress hedge --- migrate-build-cache-node/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrate-build-cache-node/index.html b/migrate-build-cache-node/index.html index dfd3d53..28042fd 100644 --- a/migrate-build-cache-node/index.html +++ b/migrate-build-cache-node/index.html @@ -121,7 +121,7 @@

    Step 2: Gradle build cache setup

    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. The server part is built; its acceptance testing is still in progress.

    +

    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>
    
    From 194d7591b0f5fb9dcf4580c89fa868e5cb515d94 Mon Sep 17 00:00:00 2001
    From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com>
    Date: Thu, 8 Oct 2026 20:35:38 -0400
    Subject: [PATCH 40/78] Owner cold-read edit: Maven stated without the
     in-progress hedge
    
    ---
     how-we-build/index.html | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/how-we-build/index.html b/how-we-build/index.html
    index f9d1c93..648f153 100644
    --- a/how-we-build/index.html
    +++ b/how-we-build/index.html
    @@ -93,7 +93,7 @@ 

    The steps a release goes through

  • Build. The binaries are built, with checksums and a software bill of materials for each archive.
  • Container images. Each image is assembled once, from the unmodified distroless base plus one binary.
  • Reproducibility check. The image is assembled a second time on a separate machine. The results must be identical, or the release fails.
  • -
  • 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 acceptance tests run against the same images. Maven acceptance coverage is in progress.
  • +
  • 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 acceptance tests run against the same images.
  • Authorization. One step checks that every earlier step produced signed proof for these exact files. Anything missing stops the release.
  • 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.
  • From a3585966922d43f4b9febac57debf8b2e45f6c55 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:35:56 -0400 Subject: [PATCH 41/78] Owner cold-read edit: Maven stated without the in-progress hedge --- build-cache-node-faq/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build-cache-node-faq/index.html b/build-cache-node-faq/index.html index 653c89d..4efc758 100644 --- a/build-cache-node-faq/index.html +++ b/build-cache-node-faq/index.html @@ -9,7 +9,7 @@ - + + + +
    + +
    Infrastructure worth keeping — and kept patched.
    +
    +
    +

    Verify a FosterStack Cache release yourself

    +

    Check our signature, build provenance, SBOM and vulnerability statements with commands you run yourself. Every command below was run against release 0.2.1, and the output shown is what it printed. No account is needed except where we say so.

    + +

    What you need

    +

    cosign, the gh tool (for the build-provenance steps), and jq. Set the version and your platform once:

    +
    $ VER=0.2.1
    +PLATFORM=linux_arm64   # or linux_amd64, darwin_arm64, darwin_amd64
    + +

    1. Download the release files

    +
    $ gh release download v${VER} --repo fosterstack/cache \
    +  -p checksums.txt -p checksums.txt.bundle \
    +  -p "fscache_${VER}_${PLATFORM}.tar.gz" \
    +  -p "fscache_${VER}_${PLATFORM}.tar.gz.sbom.json" \
    +  -p fosterstack-cache.openvex.json -p release-manifest.json
    +

    Downloading from the releases page in a browser works the same.

    + +

    2. Is the checksums file ours?

    +

    This checks the signature on checksums.txt. It needs no GitHub account.

    +
    $ 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
    +Verified OK
    + +

    3. Does your download match it?

    +
    $ sha256sum -c <(grep "fscache_${VER}_${PLATFORM}.tar.gz" checksums.txt | grep -v sbom)
    +fscache_0.2.1_linux_arm64.tar.gz: OK
    +

    Steps 2 and 3 together are the important pair. The checksum alone only shows the archive matches a file you downloaded beside it. The signature is what ties that file to us.

    + +

    4. Is the container image signed by our release workflow?

    +

    Pin the workflow and the release tag, not "any workflow in the repository". No account is needed.

    +
    $ cosign verify ghcr.io/fosterstack/cache:${VER} \
    +  --certificate-identity-regexp="^https://github.com/fosterstack/cache/.github/workflows/stage-promote.yml@refs/tags/v${VER}$" \
    +  --certificate-oidc-issuer='https://token.actions.githubusercontent.com'
    +Verification for ghcr.io/fosterstack/cache:0.2.1 --
    +The following checks were performed on each of these signatures:
    +  - The cosign claims were validated
    +  - Existence of the claims in the transparency log was verified offline
    +  - The code-signing certificate was verified using trusted certificate authority certificates
    +
    +[{"critical":{"identity":{"docker-reference":"ghcr.io/fosterstack/cache:0.2.1"},"image":{"docker-manifest-digest":"sha256:8df991b5febdf5b4a177325b8af95b6bb6c321059e1715c06cb659c659f5a4f0"},"type":"https://fosterstack.com/attestations/release-manifest/v1"},"optional":{}},{"critical":{"identity":{"docker-reference":"ghcr.io/fosterstack/cache:0.2.1"},"image":{"docker-manifest-digest":"sha256:8df991b5febdf5b4a177325b8af95b6bb6c321059e1715c06cb659c659f5a4f0"},"type":"https://sigstore.dev/cosign/sign/v1"},"optional":{}}]
    +

    There is no signing key to steal. The certificate is short-lived and was issued when the workflow ran, and the signature is in a public transparency log. The same command against docker.io/fosterstack/cache:${VER} returns the same digest, sha256:8df991b5febdf5b4a177325b8af95b6bb6c321059e1715c06cb659c659f5a4f0.

    + +

    5. Where was the image built?

    +

    A signature says who signed. Build provenance says what produced the bytes. This step reads GitHub's attestation store, so it needs a token (GH_TOKEN or gh auth login), even though the repository is public.

    +
    $ gh attestation verify oci://ghcr.io/fosterstack/cache:${VER} \
    +  --repo fosterstack/cache \
    +  --signer-workflow fosterstack/cache/.github/workflows/stage-image.yml \
    +  --source-ref "refs/tags/v${VER}" \
    +  --format json | jq '.[0].verificationResult | {statement: .statement.predicateType, image: .statement.subject[0].digest, signer: .signature.certificate.subjectAlternativeName, run: .signature.certificate.runInvocationURI}'
    +{
    +  "statement": "https://slsa.dev/provenance/v1",
    +  "image": {
    +    "sha256": "8df991b5febdf5b4a177325b8af95b6bb6c321059e1715c06cb659c659f5a4f0"
    +  },
    +  "signer": "https://github.com/fosterstack/cache/.github/workflows/stage-image.yml@refs/tags/v0.2.1",
    +  "run": "https://github.com/fosterstack/cache/actions/runs/35554556056/attempts/1"
    +}
    +

    The command exits with status 0 on success. The image digest is the one from step 4, and the run link is the public build that produced it.

    + +

    6. Where was your download built?

    +
    $ gh attestation verify "fscache_${VER}_${PLATFORM}.tar.gz" --repo fosterstack/cache \
    +  --format json | jq -r '.[0].verificationResult.statement.subject[].name, .[0].verificationResult.signature.certificate.subjectAlternativeName'
    +fscache-fips_0.2.1_linux_amd64.tar.gz
    +fscache-fips_0.2.1_linux_amd64.tar.gz.sbom.json
    +fscache-fips_0.2.1_linux_arm64.tar.gz
    +fscache-fips_0.2.1_linux_arm64.tar.gz.sbom.json
    +fscache_0.2.1_darwin_amd64.tar.gz
    +fscache_0.2.1_darwin_amd64.tar.gz.sbom.json
    +fscache_0.2.1_darwin_arm64.tar.gz
    +fscache_0.2.1_darwin_arm64.tar.gz.sbom.json
    +fscache_0.2.1_linux_amd64.tar.gz
    +fscache_0.2.1_linux_amd64.tar.gz.sbom.json
    +fscache_0.2.1_linux_arm64.tar.gz
    +fscache_0.2.1_linux_arm64.tar.gz.sbom.json
    +checksums.txt
    +https://github.com/fosterstack/cache/.github/workflows/stage-build.yml@refs/tags/v0.2.1
    +

    One statement covers every archive, its SBOM and checksums.txt. Your archive is in the list.

    + +

    7. Did the whole release chain pass?

    +

    Each stage of the release signs its own statement about the exact image. One of them exists only if the final authorization stage checked all the others for this image: the source check, the build, the image assembly, the reproducibility check, each scanner's verdict, and the acceptance tests.

    +
    $ gh attestation verify oci://ghcr.io/fosterstack/cache:${VER} \
    +  --repo fosterstack/cache \
    +  --signer-workflow fosterstack/cache/.github/workflows/stage-authorize.yml \
    +  --predicate-type https://fosterstack.com/attestations/release-authorization/v1 \
    +  --format json | jq '.[0].verificationResult | {predicateType: .statement.predicateType, signer: .signature.certificate.subjectAlternativeName}'
    +{
    +  "predicateType": "https://fosterstack.com/attestations/release-authorization/v1",
    +  "signer": "https://github.com/fosterstack/cache/.github/workflows/stage-authorize.yml@refs/tags/v0.2.1"
    +}
    + +

    8. What is inside? The SBOM

    +

    Each binary archive has a software bill of materials in SPDX format.

    +
    $ jq '{spdxVersion, name, packages: (.packages | length)}' "fscache_${VER}_${PLATFORM}.tar.gz.sbom.json"
    +{
    +  "spdxVersion": "SPDX-2.3",
    +  "name": "fscache_0.2.1_linux_arm64.tar.gz",
    +  "packages": 13
    +}
    +
    $ jq -r '.packages[0:4][] | "\(.name) \(.versionInfo)"' "fscache_${VER}_${PLATFORM}.tar.gz.sbom.json"
    +github.com/beorn7/perks v1.0.1
    +github.com/cespare/xxhash/v2 v2.3.0
    +github.com/fosterstack/cache v0.2.1
    +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
    + +

    9. Which known vulnerabilities do not apply? The VEX file

    +

    The VEX (OpenVEX) file lists vulnerabilities that scanners may report and says why each does not affect this release.

    +
    $ jq -r '.statements[] | "\(.vulnerability.name) \(.status) \(.justification)"' fosterstack-cache.openvex.json
    +CVE-2024-51744 not_affected component_not_present
    +CVE-2025-60876 not_affected vulnerable_code_not_in_execute_path
    +CVE-2025-46394 not_affected vulnerable_code_not_in_execute_path
    +CVE-2024-58251 not_affected vulnerable_code_not_in_execute_path
    +

    You do not have to take our word for those. Run your own scanner against the image and give it this file.

    + +

    10. The release manifest

    +

    release-manifest.json lists the evidence for the whole release. It names three image variants (the standard one, debug and fips), each with its digest. The standard one is the digest you verified in steps 4 and 5, at both registries:

    +
    $ jq -r '.images[] | "\(.variant) \(.digest)"' release-manifest.json
    +production sha256:8df991b5febdf5b4a177325b8af95b6bb6c321059e1715c06cb659c659f5a4f0
    +debug sha256:e2e502f44d8bd9e6967cb160aabeb012e8520ebba0faa88e130143fe534f282e
    +fips sha256:e125fe3f6ed2c18c478453300ba2b787a42999edf66a39403877efb39865ab1b
    +
    $ jq -c '.images[0].refs' release-manifest.json
    +["ghcr.io/fosterstack/cache@sha256:8df991b5febdf5b4a177325b8af95b6bb6c321059e1715c06cb659c659f5a4f0","docker.io/fosterstack/cache@sha256:8df991b5febdf5b4a177325b8af95b6bb6c321059e1715c06cb659c659f5a4f0"]
    +
    $ jq '.verified_statements | length' release-manifest.json
    +43
    +

    That last number is the statements the authorization stage checked, each with its entry in the public transparency log.

    + +

    What these checks do and do not prove

    +
      +
    • A valid signature proves who signed these exact bytes.
    • +
    • Build provenance proves which repository, workflow and run produced them, which is a public CI run and not a developer machine.
    • +
    • The SBOM and VEX files are statements we publish. They are as trustworthy as the signature on the release that carries them.
    • +
    • None of this proves the software has no bugs or that no vulnerability exists. It proves the release is the one we built, in the way we describe.
    • +
    +

    The workflow names above belong to release 0.2.1. They are version-scoped, so for older releases use the verification guide.

    + +

    Related: verify a container image signature with cosign and how releases are built and signed.

    + +
    +
    +
    +

    FosterStack is not affiliated with, endorsed by, or sponsored by Gradle Inc. + "Gradle" and "Develocity" are trademarks of Gradle Inc., used here only to identify + compatibility. FosterStack contains no Gradle Inc. code; it is a clean-room implementation + 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

    +
    +
    + + From 1d73134a8e957b47d345b9b9835f1f9ca5b79983 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 21:00:22 -0400 Subject: [PATCH 61/78] Page: monitor a build cache with Prometheus and Grafana --- .../index.html | 162 ++++++++++++++++++ sitemap.xml | 4 + 2 files changed, 166 insertions(+) create mode 100644 monitor-build-cache-prometheus-grafana/index.html 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.

    + +
    +
    +
    +

    FosterStack is not affiliated with, endorsed by, or sponsored by Gradle Inc. + "Gradle" and "Develocity" are trademarks of Gradle Inc., used here only to identify + compatibility. FosterStack contains no Gradle Inc. code; it is a clean-room implementation + 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

    +
    +
    + + diff --git a/sitemap.xml b/sitemap.xml index bd1c604..534168e 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -20,4 +20,8 @@ weekly 1.0 + + https://fosterstack.com/monitor-build-cache-prometheus-grafana/ + 2026-10-08 + From 9a7b5b56647f70b23910c5bf6231318bf34483cd Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 21:01:23 -0400 Subject: [PATCH 62/78] Page: FIPS 140-3 build cache, run the FIPS image and confirm it --- fips-140-3-build-cache/index.html | 132 ++++++++++++++++++++++++++++++ sitemap.xml | 4 + 2 files changed, 136 insertions(+) create mode 100644 fips-140-3-build-cache/index.html 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

    +
      +
    • It shows the running program is using Go's validated module. The validation is of that cryptographic module.
    • +
    • Our own tests run the whole suite against the FIPS build with the module set to refuse any algorithm it does not approve. That proves the code paths the tests reach. It does not exercise the released FIPS image itself, which no test currently runs.
    • +
    • FosterStack Cache is not "FedRAMP compliant" or "CMMC compliant". Those attach to your service and your organisation, never to a component you deploy. The FIPS build is validated crypto for your compliance program, not a substitute for it.
    • +
    + +

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

    + +
    +
    +
    +

    FosterStack is not affiliated with, endorsed by, or sponsored by Gradle Inc. + "Gradle" and "Develocity" are trademarks of Gradle Inc., used here only to identify + compatibility. FosterStack contains no Gradle Inc. code; it is a clean-room implementation + 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

    +
    +
    + + diff --git a/sitemap.xml b/sitemap.xml index bd1c604..f6cdaa6 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -20,4 +20,8 @@ weekly 1.0 + + https://fosterstack.com/fips-140-3-build-cache/ + 2026-10-08 + From 054f55f5ad62d9efbe6149beb080a4b4f57c0d96 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 21:03:31 -0400 Subject: [PATCH 63/78] Regulated page: security block, owner's Maven line, links to the verify and FIPS pages --- regulated/index.html | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/regulated/index.html b/regulated/index.html index 6d21657..aba79d3 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,6 +108,33 @@

    What our piece does — and does not do

    signed, and checkable instead of a shrug.

    +

    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

    +
      +
    • Signed releases. Images and the checksums file are signed with Sigstore, with no signing key stored anywhere. How to verify.
    • +
    • Build provenance. Every image and binary archive carries a signed statement naming the CI run that built it.
    • +
    • A bill of materials. Every binary archive ships with an SBOM.
    • +
    • Published vulnerability statements. A VEX file with each release says which known vulnerabilities do and do not affect it.
    • +
    • Reproducible. Each image is assembled a second time on a separate machine, and the digests must match or the release fails.
    • +
    +

    Supply chain

    +
      +
    • Pinned build steps. Every GitHub Action our releases use is pinned to an exact commit, and a check in CI enforces it on every change. The container base image is pinned by digest.
    • +
    • Hash-checked installs. Python tooling in our CI is installed with hash checking, so a swapped package is refused.
    • +
    • A 7-day wait. Our CI holds new dependency versions for 7 days before adopting them, and Dependabot has the same cooling-off period.
    • +
    • A daily supply-chain check. Once a day, everything our build and CI pull in (actions, tools, packages) is checked against public advisory databases and malicious-package reports. Any hit opens an issue.
    • +
    • Scanned before release. Two independent scanners with different databases check every release candidate, and any finding blocks the release unless a published VEX statement explains it. RELEASING.md.
    • +
    • Rescanned every day. Every published image is scanned again daily, so a new vulnerability in something we shipped raises an issue.
    • +
    +

    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.
    • +
    +

    The Compliance tier

    The evidence underneath is public and free — SBOMs, SLSA provenance, @@ -133,6 +160,12 @@

    Verify everything for free first

    evidence doesn't hold up under your audit, the paid tier wouldn't 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. +

    From 66c0eab9d9349295e87be788af7260fb38f1af76 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 21:03:57 -0400 Subject: [PATCH 64/78] Kubernetes page: answer the Helm question in the title and a section --- gradle-build-cache-kubernetes/index.html | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/gradle-build-cache-kubernetes/index.html b/gradle-build-cache-kubernetes/index.html index 37c5b78..5d82aaf 100644 --- a/gradle-build-cache-kubernetes/index.html +++ b/gradle-build-cache-kubernetes/index.html @@ -3,8 +3,8 @@ -Gradle build cache on Kubernetes - +Gradle build cache on Kubernetes: plain manifests, no Helm chart yet + @@ -78,9 +78,12 @@
    Infrastructure worth keeping — and kept patched.
    -

    Gradle build cache on Kubernetes

    +

    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.

      From 88986762c7b58d6f9e0c7a592c0af7bc254f9046 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 21:03:58 -0400 Subject: [PATCH 65/78] Maven page: title matches the search phrase --- maven-remote-build-cache-setup/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/maven-remote-build-cache-setup/index.html b/maven-remote-build-cache-setup/index.html index d2ff9c2..be613fe 100644 --- a/maven-remote-build-cache-setup/index.html +++ b/maven-remote-build-cache-setup/index.html @@ -3,7 +3,7 @@ -Maven remote build cache setup with the Apache Build Cache Extension +Maven build cache extension: remote server setup @@ -78,7 +78,7 @@
      Infrastructure worth keeping — and kept patched.
      -

      Maven remote build cache setup with the Apache Build Cache Extension

      +

      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

      From 82ff93ccac5d52933d60039403197d1161fbef26 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 21:04:00 -0400 Subject: [PATCH 66/78] FAQ: can it store data in S3 (no) --- build-cache-node-faq/index.html | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build-cache-node-faq/index.html b/build-cache-node-faq/index.html index 34b2d3d..8693f86 100644 --- a/build-cache-node-faq/index.html +++ b/build-cache-node-faq/index.html @@ -9,7 +9,7 @@ - + + + +
      + +
      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.

      + +
      +
      +
      +

      FosterStack is not affiliated with, endorsed by, or sponsored by Gradle Inc. + "Gradle" and "Develocity" are trademarks of Gradle Inc., used here only to identify + compatibility. FosterStack contains no Gradle Inc. code; it is a clean-room implementation + 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

      +
      +
      + + diff --git a/sitemap.xml b/sitemap.xml index bd1c604..7006cba 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -20,4 +20,8 @@ weekly 1.0 + + https://fosterstack.com/gradle-build-cache-http-status-codes/ + 2026-10-08 + From 372120bcbad3ab224a17495a2d65f1c8d07a4f62 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 21:06:12 -0400 Subject: [PATCH 69/78] Page: how to clear or reset a self-hosted Gradle build cache --- reset-gradle-build-cache/index.html | 134 ++++++++++++++++++++++++++++ sitemap.xml | 4 + 2 files changed, 138 insertions(+) create mode 100644 reset-gradle-build-cache/index.html diff --git a/reset-gradle-build-cache/index.html b/reset-gradle-build-cache/index.html new file mode 100644 index 0000000..e58abd3 --- /dev/null +++ b/reset-gradle-build-cache/index.html @@ -0,0 +1,134 @@ + + + + + +How to clear or reset a self-hosted Gradle build cache + + + + + + + + +
      + +
      Infrastructure worth keeping — and kept patched.
      +
      +
      +

      How to clear or reset a self-hosted Gradle build cache

      +

      There is no purge button. You empty the cache by removing its data and restarting. This page shows how, for Docker, Docker Compose, Kubernetes and a plain binary, and what it looks like when it works.

      + +

      Why there is no purge button

      +

      The server has no purge endpoint and no admin web page, on purpose. Nobody can reconfigure or empty it while it runs, including us. A reset is a deliberate step in whatever manages your deployment, so it leaves a record there. The old Build Cache Node had a purge button; this does not.

      +

      It is also safe to do. A build cache holds results your builds can always rebuild. The first builds after a reset are slower while it refills, and that is all.

      + +

      Docker Compose

      +
      docker compose down -v && docker compose up -d
      +

      The -v removes the named data volume. The fresh start sets up its ownership correctly.

      + +

      Plain Docker

      +
      docker rm -f fscache
      +docker volume rm fscache-data
      +# then run your original docker run command again
      +

      Tested on the 0.2.1 image. Before the reset, /statusz showed one stored entry. After removing the container and the volume and starting again:

      +
      $ curl -s localhost:8080/statusz | grep -E '"store_entries"|"store_bytes"'
      +  "store_bytes": 0,
      +  "store_entries": 0,
      +$ curl -s localhost:8080/testkey -o /dev/null -w 'GET testkey %{http_code}\n'
      +GET testkey 404
      + +

      Kubernetes

      +

      Delete the volume claim. The cleanest way is to scale to zero, delete the claim, and scale back up:

      +
      kubectl scale deploy/fscache --replicas=0
      +kubectl delete pvc fscache-data
      +kubectl scale deploy/fscache --replicas=1
      +

      Keep it at one replica. See Gradle build cache on Kubernetes.

      + +

      A plain binary

      +

      Stop the process, delete the data directory, and start it again.

      + +

      Check it worked

      +

      The status page at /statusz shows the entry count and the stored size. Both should be zero straight after a reset. For a size cap that fills up on its own, see server sizing.

      + +

      Full detail: Docker deploy guide. Related: Gradle build cache with Docker Compose.

      + +
      +
      +
      +

      FosterStack is not affiliated with, endorsed by, or sponsored by Gradle Inc. + "Gradle" and "Develocity" are trademarks of Gradle Inc., used here only to identify + compatibility. FosterStack contains no Gradle Inc. code; it is a clean-room implementation + 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

      +
      +
      + + diff --git a/sitemap.xml b/sitemap.xml index bd1c604..b6a2f05 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -20,4 +20,8 @@ weekly 1.0 + + https://fosterstack.com/reset-gradle-build-cache/ + 2026-10-08 + From 93625445b50df41576c529d388ceb939ee1093e2 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 21:06:29 -0400 Subject: [PATCH 70/78] Home + llms.txt: link the newer guides --- index.html | 7 +++++++ llms.txt | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/index.html b/index.html index cb2d80a..020b4c5 100644 --- a/index.html +++ b/index.html @@ -216,6 +216,13 @@

      Guides

    • What to check in a replacement build cache
    • How FosterStack Cache is built and signed
    • Long-term support: what is supported today
    • +
    • Verify a release yourself: signature, provenance, SBOM, VEX
    • +
    • FIPS 140-3 build cache: run the FIPS image and confirm it
    • +
    • Monitor a build cache with Prometheus and Grafana
    • +
    • Gradle build cache HTTP status codes: 401, 403, 404, 413
    • +
    • How to clear or reset a self-hosted build cache
    • +
    • Is a remote build cache safe? Who can write to it
    • +
    • Changelog
    • Questions and answers
    diff --git a/llms.txt b/llms.txt index 851cd85..0306854 100644 --- a/llms.txt +++ b/llms.txt @@ -31,6 +31,13 @@ verifiable against the CI run that produced them. - [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. - [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. From 7e3ae2c085813961f020d69bbbb8ceda48318211 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 21:21:50 -0400 Subject: [PATCH 71/78] Plain-HTTP wording: tested on Gradle 9.8.0, localhost name refused, 127.0.0.1 allowed --- jenkins-ci-build-cache/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jenkins-ci-build-cache/index.html b/jenkins-ci-build-cache/index.html index 25eecbe..194265b 100644 --- a/jenkins-ci-build-cache/index.html +++ b/jenkins-ci-build-cache/index.html @@ -108,7 +108,7 @@

    Runners inside your Kubernetes cluster

    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 to anything but localhost unless you opt in, which is a useful warning, not a nuisance. Setup is in the install guide.

    +

    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.

    From e6d5c5a34eb5523cf79ec412d2b2d0806cc2c37d Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 21:21:51 -0400 Subject: [PATCH 72/78] Plain-HTTP wording: tested on Gradle 9.8.0, localhost name refused, 127.0.0.1 allowed --- gradle-remote-cache-not-working/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle-remote-cache-not-working/index.html b/gradle-remote-cache-not-working/index.html index 6144c77..ddff837 100644 --- a/gradle-remote-cache-not-working/index.html +++ b/gradle-remote-cache-not-working/index.html @@ -105,7 +105,7 @@

    3. Is the password reaching Gradle?

    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:// to anything except localhost unless you set isAllowInsecureProtocol = true. Use HTTPS in real deployments, because Gradle sends the password with every request.

    +

    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?

    From d0cd33b2b016689e41018d7bb304cc82d3c16171 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 21:22:26 -0400 Subject: [PATCH 73/78] Page: Gradle build cache tutorial, see FROM-CACHE (real run) --- gradle-build-cache-tutorial/index.html | 184 +++++++++++++++++++++++++ sitemap.xml | 4 + 2 files changed, 188 insertions(+) create mode 100644 gradle-build-cache-tutorial/index.html 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.

    + +
    +
    +
    +

    FosterStack is not affiliated with, endorsed by, or sponsored by Gradle Inc. + "Gradle" and "Develocity" are trademarks of Gradle Inc., used here only to identify + compatibility. FosterStack contains no Gradle Inc. code; it is a clean-room implementation + 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

    +
    +
    + + diff --git a/sitemap.xml b/sitemap.xml index bd1c604..2a8e4e2 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -20,4 +20,8 @@ weekly 1.0 + + https://fosterstack.com/gradle-build-cache-tutorial/ + 2026-10-08 + From f37189ec07d9df9c1c180c404db782e9b9e3d4b2 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 21:24:46 -0400 Subject: [PATCH 74/78] Maven page: working config (remote inside configuration, saveToRemote), real two-build run --- maven-remote-build-cache-setup/index.html | 53 ++++++++++++++++++----- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/maven-remote-build-cache-setup/index.html b/maven-remote-build-cache-setup/index.html index be613fe..c054a6e 100644 --- a/maven-remote-build-cache-setup/index.html +++ b/maven-remote-build-cache-setup/index.html @@ -95,20 +95,26 @@

    1. Add the 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

    -

    Create .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">
    +  

    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:

    +
      +
    • The remote element goes inside configuration. Beside it, the extension fails to start with "Unrecognised tag: 'remote'".
    • +
    • Add saveToRemote="true" on the machines that should upload. Without it the extension reads from the server but never writes to it, and the server stays empty.
    • +
    +
    <?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>
    -  <!-- FosterStack Cache — remote Maven build cache.
    -       https://github.com/fosterstack/cache -->
    -  <remote enabled="true" id="fosterstack-cache">
    -    <url>https://cache.example.com/</url>
    -  </remote>
     </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.

    +

    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:

    @@ -124,8 +130,31 @@

    3. Add the password, if the server has one

    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

    -
    mvn install
    -

    The first run fills the cache. A later run from a clean checkout, or after mvn clean install, should show cache hits in the log for modules that did not change. If not, check the id in step 3 matches, then read the extension's own settings.

    +

    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.

    From 615e4c2fc54f37a1341ec199c0478efbc6f56e35 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 21:24:48 -0400 Subject: [PATCH 75/78] Migration guide: working Maven config (remote inside configuration, saveToRemote) --- migrate-build-cache-node/index.html | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/migrate-build-cache-node/index.html b/migrate-build-cache-node/index.html index 997236c..b20235f 100644 --- a/migrate-build-cache-node/index.html +++ b/migrate-build-cache-node/index.html @@ -132,17 +132,19 @@

    Step 3: Maven build cache setup

    </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">
    +<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>
    -  <!-- FosterStack Cache — remote Maven build cache.
    -       https://github.com/fosterstack/cache -->
    -  <remote enabled="true" id="fosterstack-cache">
    -    <url>https://cache.example.com/</url>
    -  </remote>
     </cache>
    -

    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 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

    From 68197d002bdd3a27ecfcf06b447018e13d1cd837 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 21:28:58 -0400 Subject: [PATCH 76/78] Fold: private-report links point to /security/; every page linked from home, llms.txt and the sitemap --- build-cache-node-end-of-life/index.html | 2 +- index.html | 2 ++ llms.txt | 3 +++ regulated/index.html | 2 +- 4 files changed, 7 insertions(+), 2 deletions(-) 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/index.html b/index.html index f13d4cb..13f204e 100644 --- a/index.html +++ b/index.html @@ -243,6 +243,8 @@

    Guides

  • What to check in a replacement build cache
  • How FosterStack Cache is built and signed
  • Long-term support: what is supported today
  • +
  • Gradle build cache tutorial: see FROM-CACHE with a self-hosted cache
  • +
  • Verify a container image signature with cosign
  • Verify a release yourself: signature, provenance, SBOM, VEX
  • FIPS 140-3 build cache: run the FIPS image and confirm it
  • Monitor a build cache with Prometheus and Grafana
  • diff --git a/llms.txt b/llms.txt index 904bec1..bdf66fd 100644 --- a/llms.txt +++ b/llms.txt @@ -38,6 +38,9 @@ verifiable against the CI run that produced them. - [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. diff --git a/regulated/index.html b/regulated/index.html index d7ec432..1e90f42 100644 --- a/regulated/index.html +++ b/regulated/index.html @@ -132,7 +132,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.
  • What is free, and what is not available yet

    From 6f5ad6c2810bccd4e02290f7f7b48ed74867195f Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 21:30:40 -0400 Subject: [PATCH 77/78] Fold: cut the 'nobody is running it' sentence on the home page; v0.2 label; Kubernetes page plain-HTTP wording --- gradle-build-cache-kubernetes/index.html | 2 +- index.html | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle-build-cache-kubernetes/index.html b/gradle-build-cache-kubernetes/index.html index 5d82aaf..9fe2702 100644 --- a/gradle-build-cache-kubernetes/index.html +++ b/gradle-build-cache-kubernetes/index.html @@ -105,7 +105,7 @@

    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 any non-localhost http:// address. Here it is reasonable because the traffic never leaves the cluster network. See Jenkins and CI build cache.

    +

    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.

    diff --git a/index.html b/index.html index 13f204e..9b8e3d2 100644 --- a/index.html +++ b/index.html @@ -109,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.

    From 2029db11654cae023880a736c179303900c620b8 Mon Sep 17 00:00:00 2001 From: FosterStack Admin <317177128+fosterstack-admin@users.noreply.github.com> Date: Thu, 8 Oct 2026 21:32:45 -0400 Subject: [PATCH 78/78] Reset page: Kubernetes needs the claim re-created (tested on a throwaway cluster) --- reset-gradle-build-cache/index.html | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/reset-gradle-build-cache/index.html b/reset-gradle-build-cache/index.html index e58abd3..ae93f91 100644 --- a/reset-gradle-build-cache/index.html +++ b/reset-gradle-build-cache/index.html @@ -101,10 +101,12 @@

    Plain Docker

    GET testkey 404

    Kubernetes

    -

    Delete the volume claim. The cleanest way is to scale to zero, delete the claim, and scale back up:

    -
    kubectl scale deploy/fscache --replicas=0
    -kubectl delete pvc fscache-data
    -kubectl scale deploy/fscache --replicas=1
    +

    Delete the volume claim, then create it again. The Deployment cannot start without its claim, so the new claim is a required step. Tested on a throwaway cluster:

    +
    kubectl -n fscache-ns scale deploy/fscache --replicas=0
    +kubectl -n fscache-ns delete pvc fscache-data
    +kubectl -n fscache-ns apply -f fscache-pvc.yaml   # your claim manifest, the same one you first applied
    +kubectl -n fscache-ns scale deploy/fscache --replicas=1
    +

    Without the third line, the pod stays Pending with the event persistentvolumeclaim "fscache-data" not found. With it, the pod starts on an empty volume: /statusz showed "store_entries": 0 and a key stored before the reset returned 404.

    Keep it at one replica. See Gradle build cache on Kubernetes.

    A plain binary