Context
We need to add authentication and authorization to cache-next, but the data plane cannot afford a hot-path round trip to the main server on every request.
The existing tuist/cache service is useful as a reference, but it is not the right target model:
- It uses standard bearer auth at the edge.
- It verifies some JWTs locally.
- For opaque tokens, it falls back to the main server (
/api/projects) and caches the result. - It depends on a bespoke
projectsclaim shape in JWTs. - It shares the Guardian signing secret with the cache service.
That works as a compatibility bridge, but it is still a mixed model and not a clean long-term boundary.
What we want
- No main-server round trip in the cache hot path.
- Standard-ish authn/authz model.
- Authorization that works for more than just projects.
- Tenant isolation from cluster placement.
- Support for cache namespaces that are not project-bound, e.g.
misetool caches.
Important observation
In a cluster-per-tenant model, tenant/account isolation is implicit from the cluster itself.
That means we do not need to authorize account_handle in every request.
However, we still need to authorize access to the requested cache namespace unless we intentionally want any tenant token to access any cache entry in that tenant.
Proposed direction
1. Use a control-plane/data-plane split
- The main server remains the control plane.
- Cache nodes are the data plane.
- The main server issues short-lived cache access tokens.
- Cache nodes verify them locally on every request.
2. Do not send opaque account/project tokens directly to cache nodes
Opaque long-lived tokens should be exchanged once for a short-lived cache JWT.
That gives us:
- no hot-path introspection
- no DB lookup on cache nodes
- no repeated authorization callback to the main server
3. Use asymmetric signing for cache access tokens
Cache nodes should verify with public keys / JWKS, not with the same symmetric signing secret used by the main server.
This avoids distributing the token-signing secret to every cache deployment.
4. Generalize authz from project to namespace
project should not be the universal authorization primitive.
We need a cache namespace model such as:
project:<id>domain:mise-tools- possibly more later
5. Token claims should authorize namespace access locally
Use standard JWT claims plus a small private claim for cache permissions.
Example shape:
{
"iss": "...",
"aud": "cache",
"sub": "...",
"exp": 123,
"iat": 123,
"nbf": 123,
"jti": "...",
"scp": ["cache:read", "cache:write", "cache:clean"],
"cache_access": {
"all_projects": false,
"project_ids": ["p1", "p2"],
"all_domains": false,
"domains": ["mise-tools", "swiftpm-binaries"]
}
}The cache node then only needs to check:
- token signature / expiry / audience
- required action scope for the endpoint
- whether the requested namespace is allowed by the claims
API implications
The cache API should stop assuming every request is project-scoped.
Instead, requests should carry or resolve a namespace such as:
namespace_type=project,namespace_id=<project-id>namespace_type=domain,namespace_id=mise-tools
We should also stop treating account_handle as an authorization input in the cache data plane.
Storage implications
Storage identity should include namespace, not just project.
Suggested structure:
tenant + namespace_type + namespace_id + artifact_kind + artifact_key
Examples:
tenant-a / project / p1 / xcode / hash-123tenant-a / domain / mise-tools / tool / python-3.13.2
Implementation sketch
- Add a cache auth plug in
cache-next. - Verify bearer JWTs locally.
- Map endpoints to required scopes:
- read paths ->
cache:read - write/multipart paths ->
cache:write - clean paths ->
cache:cleanorcache:write
- read paths ->
- Resolve the target namespace from the request.
- Store a normalized auth context in
conn.assigns. - Bind multipart uploads to the authorized namespace at creation time.
Open questions
- Should namespace IDs be stable internal IDs only, or can some domain namespaces remain string-based?
- Do we want
cache:cleanas a separate scope fromcache:write? - Should the CLI exchange all auth methods into a cache-specific token up front, or should only CI/account tokens be exchanged?
- Do we want one cache audience for all cache nodes, or audience per tenant / environment?
Acceptance criteria
- No cache-node call to the main server in the request hot path.
- No dependency on the old bespoke
projectsJWT claim. - No cache-node dependency on the main server’s symmetric signing secret.
- Authorization works for both project-scoped and domain-scoped cache entries.
- The resulting pattern is standard bearer + JWT verification, with only minimal private claims for namespace restrictions.
Activity
0No activity yet.