v0.7.0 — Sync to vgi-rpc 0.17: gzip codec, error_kind, sticky sessions
Wire-protocol catch-up with the Python reference at vgi-rpc 0.17.1.
Conformance: 893 passed, 0 skipped across all transports.
What's new
HTTP content-encoding negotiation
gzip is now a co-equal codec to zstd on both request and response paths. Servers emit VGI-Supported-Encodings: zstd, gzip on every response so capability-aware clients can pick a codec from the intersection; unknown Content-Encoding returns HTTP 415. Required for Python clients running without the zstandard package installed.
Sticky sessions (HTTP-only, opt-in)
Full implementation of the cross-language sticky-sessions spec.
HttpServer.EnableSticky(defaultTTL)turns it on; off by default.CallContext.OpenSession(state, ttl)/CloseSession()/Session()/SessionID().- AEAD-sealed
VGI-Sessiontoken via the existing XChaCha20-Poly1305 envelope. - Per-worker registry with TTL reaper goroutine; per-session mutex serializes same-session calls.
- Idempotent
DELETE {prefix}/__session__teardown endpoint. - Echo headers (
VGI-Echo-<name>) for client-driven routing (e.g. Fly.io'sfly-force-instance-id). - Typed
SessionLostErrorandServerDrainingErrorwithvgi_rpc.error_kindmetadata. DrainHandleoperator API for graceful shutdown.- Middleware runs on
/initAND/exchangeso producer continuations and exchange round-trips re-resolve the session on every HTTP turn.
Typed errors with metadata
MethodNotImplementedError sentinel + vgi_rpc.error_kind metadata key on EXCEPTION batches so cross-language clients can match on the kind without substring-searching the message. SessionLostError and ServerDrainingError use the same mechanism.
Implementation field on CallContext / DispatchInfo
Optional caller-supplied reference to the service implementation, set via Server.SetImplementation. Mirrors Python's CallContext.implementation so framework callbacks reach the impl without holding their own captured reference.
Wire-compat fix (potentially breaking)
AAD fix for state and session tokens. Python emits prefix + b\"\x01\" + domain + b\"\x00\" + principal for authenticated tokens; the Go port was emitting prefix + b\"\x01\" + principal (no domain, no separator). This was wire-incompatible with Python for any authenticated principal and enabled cross-domain replay. The anonymous predicate is also aligned with Python's not auth.authenticated.
Migration: any state tokens or session tokens minted under the old Go AAD format are unreadable after upgrade. For deployments that mint long-lived tokens, plan a graceful expiry window before deploying. New tokens are wire-compatible with Python.
Other
- Panic recovery in
handleUnaryso a handler crash afterctx.OpenSession()doesn't leak the registry entry. - Conformance suite now exercises sticky on every code path: unary, producer-stream continuation, exchange-stream round-trip, and a unary↔stream shared-state round-trip.
- Race detector clean; lint clean.
🤖 Generated with Claude Code