Skip to content

feat(client): add Go SDK for hypercache-server clusters#124

Merged
hyp3rd merged 6 commits into
mainfrom
feat/dist-mem-cache
May 12, 2026
Merged

feat(client): add Go SDK for hypercache-server clusters#124
hyp3rd merged 6 commits into
mainfrom
feat/dist-mem-cache

Conversation

@hyp3rd
Copy link
Copy Markdown
Owner

@hyp3rd hyp3rd commented May 11, 2026

Introduce pkg/client — a typed Go client for hypercache-server that closes
three operational gaps surfaced by the OIDC-client example:

  • Multi-endpoint HA: New() accepts a seed slice; each request picks an
    endpoint at random and falls back to the next on transport failure, 5xx,
    or 503 draining. 4xx answers are deterministic and do not trigger failover.

  • Optional topology refresh: WithTopologyRefresh(interval) runs a background
    loop that pulls /cluster/members and updates the in-memory endpoint view,
    so nodes added or removed after deploy become reachable without redeploying
    consumers. Seeds remain as a permanent fallback when the live view empties.

  • Four auth modes in one API: WithBearerAuth, WithBasicAuth,
    WithOIDCClientCredentials (auto-refreshing OAuth2 client-credentials flow),
    and WithHTTPClient (bring your own mTLS-configured client). Mutually
    exclusive; last applied wins.

Typed error surface: ErrNotFound, ErrUnauthorized, ErrForbidden, ErrDraining,
ErrBadRequest, ErrInternal, ErrAllEndpointsFailed, ErrNoEndpoints compose with
errors.Is. *StatusError carries the canonical {code, error, details} envelope
for callers that need finer discrimination via errors.As.

Commands: Set, Get (raw bytes), GetItem (full envelope with version/owners),
Delete, Identity (/v1/me canary), Endpoints, RefreshTopology, Close.

Promote golang.org/x/oauth2 from indirect to direct dependency. Update
cspell.config.yaml with new terms (errchkjson, httptest, mathrand, misrouted,
unparseable). Full test coverage in pkg/client/client_test.go covering happy-
path round-trip, every auth mode, 5xx failover, 4xx no-failover regression
guard, exhaustive-failure wrapping, all sentinel errors.Is mappings, topology
refresh, and constructor input validation

hyp3rd added 2 commits May 11, 2026 19:14
Introduce username/password authentication via `Authorization: Basic`
as a first-class credential class alongside static bearer tokens, mTLS
certs, and OIDC JWTs. The resolve chain is now: bearer → Basic → mTLS
→ OIDC.

Changes:

- pkg/httpauth/policy.go: new `BasicIdentity` struct (bcrypt-hashed
  passwords); `resolveBasic` resolver with fail-closed TLS posture
  (`AllowBasicWithoutTLS` defaults to false); `Capabilities()` on
  `Identity` derives stable `cache.<scope>` strings from scopes.

- pkg/httpauth/loader.go: new `users:` YAML block in `fileSchema`;
  `AllowBasicWithoutTLS` config flag; boot-time `Validate()` rejects
  malformed bcrypt hashes and empty usernames loudly.

- cmd/hypercache-server/main.go + openapi.yaml: `meResponse` and
  `IdentityResponse` gain a required `capabilities` field populated
  from `Identity.Capabilities()`; OpenAPI description updated to
  reflect all four auth modes.

- __examples/distributed-oidc-client/: new runnable OIDC
  client-credentials demo (PUT/GET/DELETE/batch surface) with full
  README covering env-var setup, scope mapping patterns across
  Keycloak/Auth0/Okta, and production caveats.

- docs/rfcs/0003-client-sdk-and-redis-style-affordances.md: new RFC
  proposing a Go client SDK with multi-endpoint HA, username/password
  auth, and structured typed errors; design options enumerated with
  recommended path.

- Tests: Basic auth happy path, wrong-password/user, malformed header,
  and plaintext-refused-by-default cases in `policy_test.go`; YAML
  round-trip and boot-guard tests in `loader_test.go`; `me_test.go`
  updated for the new `capabilities` field.

- go.mod: promote `golang.org/x/crypto` from indirect to direct; bump
  ewrap v1.5.0 → v1.5.1, sectools v1.2.5 → v1.2.6.
Introduce pkg/client — a typed Go client for hypercache-server that closes
three operational gaps surfaced by the OIDC-client example:

- Multi-endpoint HA: New() accepts a seed slice; each request picks an
  endpoint at random and falls back to the next on transport failure, 5xx,
  or 503 draining. 4xx answers are deterministic and do not trigger failover.

- Optional topology refresh: WithTopologyRefresh(interval) runs a background
  loop that pulls /cluster/members and updates the in-memory endpoint view,
  so nodes added or removed after deploy become reachable without redeploying
  consumers. Seeds remain as a permanent fallback when the live view empties.

- Four auth modes in one API: WithBearerAuth, WithBasicAuth,
  WithOIDCClientCredentials (auto-refreshing OAuth2 client-credentials flow),
  and WithHTTPClient (bring your own mTLS-configured client). Mutually
  exclusive; last applied wins.

Typed error surface: ErrNotFound, ErrUnauthorized, ErrForbidden, ErrDraining,
ErrBadRequest, ErrInternal, ErrAllEndpointsFailed, ErrNoEndpoints compose with
errors.Is. *StatusError carries the canonical {code, error, details} envelope
for callers that need finer discrimination via errors.As.

Commands: Set, Get (raw bytes), GetItem (full envelope with version/owners),
Delete, Identity (/v1/me canary), Endpoints, RefreshTopology, Close.

Promote golang.org/x/oauth2 from indirect to direct dependency. Update
cspell.config.yaml with new terms (errchkjson, httptest, mathrand, misrouted,
unparseable). Full test coverage in pkg/client/client_test.go covering happy-
path round-trip, every auth mode, 5xx failover, 4xx no-failover regression
guard, exhaustive-failure wrapping, all sentinel errors.Is mappings, topology
refresh, and constructor input validation.
@hyp3rd hyp3rd changed the title Feat/dist mem cache feat(client): add Go SDK for hypercache-server clusters May 11, 2026
hyp3rd and others added 4 commits May 11, 2026 23:37
Split the distributed-oidc-client example into two variants:

- distributed-oidc-client/ — rewritten as the recommended ~150-line SDK
  consumer using pkg/client with OIDC client-credentials, multi-endpoint
  failover, and topology refresh (down from ~480 lines)
- distributed-oidc-client-raw/ — the original hand-rolled net/http version
  retained as a wire-protocol reference and for non-Go consumers

Add docs/client-sdk.md: comprehensive SDK reference covering all four auth
modes (bearer, Basic, OIDC client credentials, mTLS via WithHTTPClient),
failover policy, topology refresh semantics with the 1s floor and seed
fallback, the full sentinel + *StatusError recipe set, command surface, and
production caveats (connection pooling, retry policy, OTel propagation,
OIDC refresh visibility).

Update __examples/README.md to list both variants with the SDK path flagged
as recommended, and register the new page in mkdocs.yml Reference navigation.
Introduce three new methods on *Client that execute their respective
operations in a single HTTP round-trip against the existing
POST /v1/cache/batch/{put,get,delete} wire endpoints.

Key design points:
- Per-item granularity: the outer error fires only on transport/auth/
  HTTP-level failures; individual item failures surface via the result's
  Err field (*StatusError), preserving errors.Is/errors.As compatibility
  with the single-key sentinel set.
- BatchGet treats missing keys as Found=false, not an error.
- Empty input is a client-side no-op (returns empty slice, nil error)
  without dispatching an HTTP request.
- Results mirror input order (index i of result = outcome for input i).

Supporting changes:
- Extract contentTypeJSON const to eliminate drift between call sites.
- Add test_consts_test.go with shared JSON wire-key constants.
- Eight new test cases in batch_test.go covering happy paths, per-item
  failures, mixed found/missing, empty-input no-ops, and HTTP-level
  failure wrapping ErrAllEndpointsFailed.
- Extend docs/client-sdk.md with a dedicated Batch operations section
  and update the commands reference table with the three new methods.
- Add a BatchSet demo step to the distributed-oidc-client example.
- Fix markdown anchor links in docs/oncall.md (double-dash → single-dash).
- Bump softprops/action-gh-release v2 → v3 in release.yml.
Switch rebalance integration tests from using node.Set (quorum write)
to node.DebugInject (test bypass) when seeding keys before triggering
topology changes. This eliminates a ~1-in-50 flake under -shuffle in
CI caused by fan-out transport calls missing deadlines on brand-new
two-node clusters.

Add populateKeysOnAll helper to inject keys across multiple nodes
simultaneously, and migrate leave, replica-diff, and throttle tests
to use the new helper instead of inline key-seeding loops.
@hyp3rd hyp3rd merged commit 24b3584 into main May 12, 2026
15 of 16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant