From d6ed4b04f9e5b68b7df7aef94e517df796ec120e Mon Sep 17 00:00:00 2001 From: Ray Walker Date: Sun, 1 Nov 2026 06:08:29 +0000 Subject: [PATCH] docs(saas-api): name the X-CacheKit-* response headers CORS exposes --- spec/saas-api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/saas-api.md b/spec/saas-api.md index 2890b64..b717d66 100644 --- a/spec/saas-api.md +++ b/spec/saas-api.md @@ -64,7 +64,7 @@ The write-space split applies to mutations only (`PUT`, `DELETE`, lock, TTL refr > [!WARNING] > **The split covers `ns:`- and `nsapi:`-prefixed cache keys only.** A cache key with neither prefix maps to the tenant's `default` namespace and belongs to a **shared write space**: any key class whose namespace grants include `default` **or are unrestricted** may create, overwrite, delete, or lock it. The split is an intra-tenant guard for namespaced keys, not a general write-isolation guarantee between key classes. -**CORS preflight exception.** `OPTIONS` on any path is answered `204 No Content` before authentication; no `Authorization` or `X-CacheKit-L1-Status` header is required or inspected. Every other `/v1/cache/*` request is authenticated before any other check, and the authentication outcome — `401`, or `503` for a key-store fault while checking the key — precedes every other error. Browser callers are outside this specification's scope, and **no key class is intended for delivery to browser code** — an API key in client-side JavaScript is a tenant-wide credential exposed to every visitor. The deployed CORS policy returns `Access-Control-*` headers only for a closed allowlist of first-party origins; for every other origin the preflight carries none and the browser blocks the request. Even from an allowlisted origin the policy allows `Authorization` but no `X-CacheKit-*` request header and exposes no `X-CacheKit-*` response header, so `ck_sdk_` keys cannot be used cross-origin and freshness headers are unreadable there. This specification governs `/v1/cache/*` only. +**CORS preflight exception.** `OPTIONS` on any path is answered `204 No Content` before authentication; no `Authorization` or `X-CacheKit-L1-Status` header is required or inspected. Every other `/v1/cache/*` request is authenticated before any other check, and the authentication outcome — `401`, or `503` for a key-store fault while checking the key — precedes every other error. Browser callers are outside this specification's scope, and **no key class is intended for delivery to browser code** — an API key in client-side JavaScript is a tenant-wide credential exposed to every visitor. The deployed CORS policy returns `Access-Control-*` headers only for a closed allowlist of first-party origins; for every other origin the preflight carries none and the browser blocks the request. Even from an allowlisted origin the policy allows `Authorization` but no `X-CacheKit-*` request header, and of the `X-CacheKit-*` response headers it exposes only `X-CacheKit-Limiter` and `X-CacheKit-Deny-Reason` — not `X-CacheKit-Freshness` or `X-CacheKit-Fresh-For` — so `ck_sdk_` keys cannot be used cross-origin and freshness headers are unreadable there. This specification governs `/v1/cache/*` only. **HTTP intermediary caching is prohibited.** Servers MUSTAPI-2 emit `Cache-Control: no-store` and `Vary: Authorization` on every response. The [cache key](cache-key-format.md) carries no tenant component — tenancy rides only in the `Authorization` header — so two tenants using the same namespace, function, and arguments produce byte-identical request paths, and a shared HTTP cache applying heuristic freshness (RFC 9111 §4.2.2) to an unmarked response could serve one tenant's bytes to another. `no-store` forbids storing the response at all; `Vary: Authorization` is the independent second control — a cache that wrongly stores despite `no-store` (or RFC 9111 §3.5's rule for authenticated requests) but honors `Vary` still cannot match tenant A's copy to tenant B's request. Any CacheKit-operated serving tier that caches responses (edge, colo) MUSTAPI-3 partition its internal cache by tenant, never by URL alone; such tiers are part of the server, not HTTP intermediaries, and these headers govern what they emit, not what they may store.