The vx cache server (@vzn/vx-cache-server), 2026-10
A server anyone hosts that keeps vx’s artifacts, so a fresh CI runner or a new branch starts warm. It is optional: a workspace that declares no plugin for it never talks to it, and Turbo-wire, Nx-wire and REAPI remotes stay. Each item below is marked shipped or planned, and this page changes in the same PR as the item it describes.
- Shipped: one fetch handler (
src/server.ts) over one seam,Blobs, so every backend shares every route: Bun with an S3-compatible bucket (Bun’s S3 client), the same server as a Docker image, and Workers with R2. One conformance suite (tests/conformance.ts) runs against each; the Worker runs in workerd (miniflare), the image in Docker, and the bucket is an in-memory S3 that Bun’s own client talks to. - Shipped: no database (2026-10-25). An artifact is one object named
<issuer>/<repository>/<ref>/<hash>, each a readable, percent-encoded folder (2026-11-03, replacing 10-25’s hashed parts: pruning one branch is deleting its prefix; no tier); its size is the bucket’s metadata, and a put replaces a name whole, so racing uploads keep one whole artifact and nodes share nothing but the bucket. Expiry is one bucket lifecycle rule by age; the server sweeps nothing, and the server deletes nothing itself. - Shipped: every read names exact scopes (2026-10-25: the whole
point of scope is exact keys) on Turbo’s routes (2026-10-25: “use
turbo”):
GET,HEADandPUT /v1/artifacts/<hash>, a download’s scope inx-artifact-scope, andPOST /v1/artifactswith{ hashes, scopes }(M: “checks can ask many scopes”), up tomaxCheckHasheslookups, answering each hash’s{ size, scope }, the first scope asked that holds it, or null. One exact head per lookup: a list of a scope reads every name in it, and a scope may hold billions (M). A Worker checks 6 by default, one round of its waiting calls, and the client sends chunks at once. - Shipped: limits an operator sets (
MAX_CHECK_HASHES,MAX_UPLOAD_BYTES), named on every check and at/v1/capabilities; the server never lowers them to a plan’s (2026-10-25: never optimize for the free plan). - Shipped: Turbo’s shape where it fits (2026-10-25: keep to the
standard, differ only where it blocks us):
{ code, message }errors, hex hashes, our own/v1. Batches and exact scoped keys are ours: Turbo has neither. - Shipped: task timings left for
@vzn/vx-history-server(2026-10-25): this server keeps artifacts only.
Encryption (planned, no format change)
Section titled “Encryption (planned, no format change)”- The server compares hashes and never reads them or the bytes (PROTOCOL.md § Opaque strings). End-to-end encryption is then the client’s alone: a workspace key the server never sees, an HMAC of each hash (equality still works, so lookups do), and an authenticated cipher over each artifact’s bytes.
- Refs stay plain: the server matches them against CI tokens, which name them.
Access
Section titled “Access”- Shipped: CI OIDC tokens only (
@vzn/vx-devtools, shared with vx’s other servers), admitted byALLOW(issuer:owner/repo:workflow, the relay’s string): a token writes its own branch’s scope, apull_request_targetrun nothing; another repository or workflow gets nothing.
- Shipped:
vxCache()(@vzn/vx-cache), the client plugin: a remote for core’s cache seam that asks for the run’s refs’ exact keys, its branch then main’s. - Planned: a storage cap, by a daily list of the bucket.