Cache-server auth scoping: split publish from read - #88
Merged
Conversation
…m 1) Optional --publish-token (HASHLOOM_CACHE_PUBLISH_TOKEN): when set, POSTs require it and GETs accept either token — publish implies read — so CI writes greens and a laptop with the read token only consumes them. A valid read token on a publish route is 403 read_only; an unknown token stays 401 unauthorized. All checks remain constant-time compare_digest. Back-compat: with only --token, that token grants both roles, unchanged. Client side, shared config takes optional "publish": false so read-only clients skip publish requests entirely (a rejected publish was already a swallowed no-op). shared.py and all contracts untouched — the role lives in the transport, per Store's backend-agnostic invariant. Per-project/ team tokens remain deferred (docs/hosted-store.md).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First item of the v0.5 hosted-store theme (ROADMAP: "auth scoping — split publish from read: CI can write greens, a laptop can only consume them. Today one bearer token does both.").
Server —
python -m hashloom.cache_server:--publish-token(orHASHLOOM_CACHE_PUBLISH_TOKEN). When set: publishes (POST /verification,POST /blob) require it; reads (GET) accept either token — publish implies read, so CI holds one token.read_only(scope problem); an unknown token gets 401unauthorized(identity problem) — diagnosable apart.hmac.compare_digest, with both tokens always compared (no short-circuit string equality on the 401/403 split).--token, that token grants both roles — byte-identical to today, pinned by test. No existing deployment or config changes.Client —
.hashloom/config.json:sharedtakes an optional"publish": false: a read-only client skips publish requests entirely instead of collecting one rejected POST per verdict/blob during index/verify. Explicit opt-in, so existing single-token CI configs keep publishing. A rejected publish was already a swallowed no-op — the client-side degradation semantics are unchanged.Untouched by design:
shared.py(LayeredStore is role-agnostic) and every contract — the role lives in the constructor/transport where the token already was, soStore's backend-agnostic invariant holds without edits. The three hosted-store files remain deliberately uncontracted while the theme reshapes them.Tests (6 new in
test_remote_store.py): read token 200-on-GET / 403-on-POST; publish token both verbs (implies read); unknown token 401 both verbs; single-token back-compat pin; end-to-end CI-publishes-laptop-consumes (laptop getscached-passwith zero test runs, and its own local green never lands on the server);publish: falsesuppression (nothing lands — verdicts or blobs) + config validation.Live smoke against the real server process: all six token/verb combinations returned the designed codes (404/403/404/200/401/401).
Full suite 239 passed, coverage 90.48% (gate 85), benchmark ≥5x.
Per-project/team tokens remain deferred (
docs/hosted-store.mditem 2 says so); next theme items are concurrent writers and cross-graph invalidation.🤖 Generated with Claude Code