# Read Auth for Private Networks — COMPLETE Shipped 2026-03-14. Per-path read token enforcement for private networks. ## Design **Model**: If any token in the store has `"read"` in its operations AND its path pattern matches the request path, that path requires read auth. If no such token exists, the path is public. No new config — the existing tokens TOML file drives everything. **Performance**: Pre-compute `readPaths []string` at load time by collecting path patterns from tokens with `"read"` in operations. `RequiresReadAuth` calls `matchesAnyPath(ts.readPaths, reqPath)` — no iteration over tokens at request time. **Exempt paths**: `/health` and `/.well-known/agent-manifest.md` bypass read auth so agents can discover capabilities before authenticating. **Content-addressed fetch**: Hash lookups resolve to a real path first, then read auth checks that path — knowing a hash doesn't bypass access control. ## What Shipped ### `server/internal/auth/auth.go` - `readPaths []string` field on `TokenStore`, pre-computed at load time - `collectReadPaths()` — extracts path patterns from tokens with `"read"` op - `RequiresReadAuth(path)` — checks if any read token covers the path - Directory path normalization — checks both `/private` and `/private/` against patterns so trailing-slash omission can't bypass `/**` globs ### `server/internal/handler/handler.go` - `authorizeRead(w, req)` helper — checks token store, exempts well-known manifest path, calls `RequiresReadAuth`, then `Authorize` if needed - Integrated into `handleFetch`, `handleList`, `handleVersions` - `handleList` and `handleVersions` signatures changed from `(w, path string)` to `(w, req protocol.Request)` to carry auth metadata - Hash-based fetch checks read auth on the resolved path after hash lookup - Versioned path auth — `/doc.md/v2` checks auth on the base path `/doc.md` ### Tests - `TestRequiresReadAuth` — 9 table-driven cases covering read tokens, publish-only tokens, no tokens, glob patterns, directory paths with/without trailing slash - `TestReadAuth` — 15 handler tests covering FETCH/LIST/VERSIONS with and without tokens, wrong tokens, public paths, well-known manifest, no token store, hash-based fetch, versioned paths ### Edge Cases Caught During Review - **Versioned path bypass**: `/doc.md/v2` wouldn't match an exact-path token for `/doc.md`. Fixed by checking auth on the base path before dispatching to `handleFetchVersion`. - **Directory path bypass**: `LIST /private` without trailing slash wouldn't match `/private/**`. Fixed by having `RequiresReadAuth` check both the path as-is and with a trailing slash appended. ## Backwards Compatibility Fully backwards compatible: - No read tokens configured = all reads public (same as before) - No new config options needed - No protocol changes - Existing write auth unchanged