Skip to content

Delete cached artifacts through the API and the UI - #491

Merged
andrew merged 2 commits into
git-pkgs:mainfrom
gilesw:feat/delete-cached-artifact
Oct 20, 2026
Merged

andrew merged 2 commits into
git-pkgs:mainfrom
gilesw:feat/delete-cached-artifact

Conversation

@gilesw

@gilesw gilesw commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

There is no way to remove a single cached artifact. Once a file is cached it stays until max_size or retention evicts it. That is a problem when an upstream republishes a version, or when a Maven non-unique SNAPSHOT or a file from the wrong upstream gets cached. Maven files are cached by path and never checked against the upstream again. The only documented fix is to wipe the whole cache.

This adds an opt-in cache_delete option (PROXY_CACHE_DELETE), off by default.

DELETE /api/cache/{ecosystem}/{name}/{version} removes the cached files of a version, or only the file named by ?filename=. The next request fetches them from the upstream again.

  • Records are cleared and stored objects queued for deletion, the same way as a replaced artifact (DB.DiscardArtifact). Downloads in progress can finish.
  • Pushed archives are not touched. A whole-version delete skips them, and naming one returns 409.
  • The response lists the files that were deleted. A version with nothing to delete returns 404.

The version page in the UI shows a Delete button for each cached file and a "Delete all cached files" button. Each needs a second click to confirm. The whole-version delete matters for Maven: since #484 the .sha1 and .asc files are cached separately, so deleting only the jar would leave its old checksum cached.

Neither route has authentication of its own, in the same way the UI has none. The UI sends its deletes to /ui/api/cache, so the ingress rules that guard /ui also guard its deletes. The docs say to restrict /api/cache and /ui before turning the option on.

Docs: README (API table, Cache Management), docs/configuration.md (Cache Delete API), config.example.yaml. Swagger is regenerated.

Tested with:

  • go vet ./..., go test ./... and golangci-lint v2.13.1.
  • New tests for deleting one file, deleting a whole version while keeping pushed files, the 409/404/400 errors, and that the buttons only appear when the option is on.
  • Running the proxy against Maven Central: I cached gson 2.10.1, deleted the jar through /ui/api/cache, the next request fetched it again, and a whole-version delete then removed the jar, .sha1 and .pom.

@simonchrz simonchrz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me: opt-in, pushed archives are left alone, deletes go through DiscardArtifact, and the two-click confirm in the UI is a nice touch. Since there are no CORS headers, a cross-site page can't trigger the DELETE either.

Non-blocking suggestion: /api/cache could reuse requireBearerToken, like /api/hosted, so scripts need a token. Without one, anyone who can reach the proxy can delete cached artifacts, and during an upstream outage those packages become unavailable. It might also be worth stating that more directly in the docs: with cache_delete on and no ingress rules, everyone on the network can delete through the UI too.

@andrew andrew left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move the delete-button JavaScript out of the inline script in internal/server/templates/pages/version_show.html into a file under internal/server/static, and load it with a script src tag. The packages-list.js integration provides an existing pattern. Keep the two-click confirmation and error handling.

@gilesw

gilesw commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

Moved the delete-button script into internal/server/static/cache-delete.js, loaded with <script src="/ui/static/cache-delete.js" defer> like packages-list.js. It still only loads when cache deletion is enabled, and the two-click confirmation and error handling are unchanged. Tests now check the page loads the script only when enabled and that the file is served as JavaScript. (8973893)

cache_delete (PROXY_CACHE_DELETE) registers
DELETE /api/cache/{ecosystem}/{name}/{version}, which removes a
version's cached files, or only ?filename=, so the next request fetches
them from the upstream again. Records are cleared and the stored objects
queued for deletion like a replaced artifact. Pushed archives are left
alone: a whole-version delete skips them and naming one answers 409.

With it enabled, the version page shows a Delete button for each cached
file and one for the whole version. The UI calls /ui/api/cache so the
same ingress rules that guard /ui guard its deletes. Neither route has
authentication of its own, so the option is off by default.
Move the version page's delete-button JavaScript into
static/cache-delete.js and load it with a script tag, as
packages-list.js does.
@andrew
andrew force-pushed the feat/delete-cached-artifact branch from 8973893 to 7760d0e Compare October 20, 2026 10:29

@andrew andrew left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The delete-button JavaScript is now served from a static file, preserving confirmation and error handling. The rebase preserves the configuration changes, and the earlier review request is addressed. No blocking findings.

@andrew
andrew merged commit 8bebc56 into git-pkgs:main Oct 20, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants