Repository navigation
CAS: let browsers cache downloads (Authorization header, ETag, 304) #3549
Copy link
Copy link
Open
Labels
Description
Problem
A browser cannot keep a copy of a CAS download. Each open of the same artifact downloads it again in full, and the CAS copies the object from the storage backend each time. For large artifacts, such as AI coding session transcripts, this cost is high.
There are two causes:
- The download URL holds a new token in the query (
?t=<token>) on each call. The browser cache uses the URL as its key, so it never finds a match. - The download response has no
ETagand noCache-Controlheader. The browser cannot check its copy with the CAS.
Proposal
Change the CAS download endpoint (/download/<digest>):
- Accept the token in an
Authorization: Bearerheader. Today the endpoint reads the token only from thetquery parameter. It must accept the token from the header or from the query. Clients that use the query token keep working. - Send cache headers on the download response:
ETag: "<digest>", for example"sha256:abc...". Put the value in quotes. Without the quotes, browsers can ignore the ETag. The digest identifies the bytes, so the ETag never goes stale.Cache-Control: private, no-cache. The browser keeps the copy, but it checks with the CAS before each use. Shared proxies do not store it.
- Answer a conditional request with 304. When the request has
If-None-Matchwith the digest, the CAS does these steps:- It checks the token.
- It checks that the object exists in the storage backend, with a metadata call. It does not copy the object.
- It answers
304 Not Modifiedwith no content. The 304 also has theETagandCache-Controlheaders.
The CORS preflight already allows Authorization and sends Access-Control-Max-Age: 86400, so no CORS change is necessary.
sequenceDiagram
participant B as Browser
participant CAS
participant S as Storage backend
alt First open
B->>CAS: GET /download/digest (Authorization)
CAS->>S: Copy object, check digest
CAS-->>B: 200, ETag, Cache-Control private no-cache
else Next open
B->>CAS: GET /download/digest (Authorization, If-None-Match)
CAS->>CAS: Check token
CAS->>S: Check that the object exists
CAS-->>B: 304 Not Modified
end
Acceptance criteria
- A request with a valid token in
Authorization: Bearerand notquery parameter downloads the artifact. - A request with the
tquery parameter works as before. - The 200 response has
ETag: "<digest>"andCache-Control: private, no-cache. - A request with a valid token and a matching
If-None-Matchgets a 304 with no content. The CAS does not copy the object from the storage backend. - A request with a matching
If-None-Matchand a bad or missing token gets a 401. - A request with a matching
If-None-Matchfor an object that is not in the storage backend does not get a 304.
Reactions are currently unavailable
Activity
jiparis commentedon Oct 7, 2026
Spec: #3552