Local GitHub API cache proxy — reduce latency and save rate limits.
local-hub sits between your GitHub CLI (or any GitHub API client) and the GitHub API, transparently caching responses to deliver:
- Near-zero latency for repeated requests (served from local cache)
- Rate limit savings via ETag-based conditional requests (304 responses don't count against limits)
- Offline access to previously fetched data
gh CLI ──(Unix socket)──→ local-hub ──(HTTPS)──→ GitHub API
fetch ──(HTTP :8787)───→ (cache)
- Dual listen — Unix socket for
ghCLI + TCP port forfetch/octokit - ETag + TTL caching — conditional requests for freshness, TTL for speed
- Token-hash isolation — each token gets its own cache namespace (no privilege leaks)
- All GET requests cached — issues, PRs, repos, orgs, projects, and more
- Write passthrough — POST/PUT/PATCH/DELETE go straight to GitHub with related cache invalidation
- Tiny footprint — ~3-5MB memory, <2ms cold start (Rust native binary)
- Zero config — sensible defaults, just start and go
brew install pleaseai/tap/local-hubnpm install -g local-hubcargo install --git https://github.com/pleaseai/local-hubDownload platform-specific binaries from Releases.
# Start the proxy
local-hub start
# Configure gh CLI to use it
gh config set http_unix_socket ~/.local-hub/local-hub.sock
# Use gh as normal — requests are now cached
gh issue list
# Show cache statistics
local-hub status
# Flush cache entries
local-hub flush
# For fetch/octokit clients, use HTTP endpoint
# baseUrl: http://localhost:8787- GET request arrives → compute cache key from
SHA256(token)[:16] + method + URL + query - Cache hit + not expired → return cached response instantly
- Cache hit + expired → send conditional request with
If-None-Match: <etag> - 304 Not Modified → refresh TTL, return cached response (no rate limit cost)
- Cache miss or 200 → store response + ETag + TTL, return to client
- TTL-based — configurable per-endpoint pattern (default: 5 minutes)
- Write-triggered — mutations automatically invalidate related cache entries
- Manual —
local-hub flush [pattern]to clear specific entries
Tokens are never stored. Only a truncated SHA-256 hash is used as a cache namespace prefix, ensuring:
- Each token's cached data is isolated
- Token rotation automatically creates a fresh namespace
- No risk of serving cached data across different permission levels
Requires mise for toolchain management.
mise install # Install rust + lefthook
mise run dev # Build local-hub + start web dev server
mise run hub # Build and start local-hub only
mise run web # Start web dev server only
mise run build # Build local-hub (release)
mise run stop # Stop local-hub# Custom port
local-hub start --port 9000
# Custom cache directory
local-hub start --cache-dir ~/.my-cache
# Custom default TTL (seconds)
local-hub start --ttl 600
# Show cache stats
local-hub statuscrates/
└── server/
└── src/
├── main.rs # CLI entry point (start, status, flush)
├── server.rs # axum HTTP proxy (TCP + Unix socket)
├── proxy.rs # Request forwarding to GitHub API
├── cache.rs # redb cache layer (get, set, invalidate)
├── key.rs # Cache key generation (token hash + URL normalization)
├── ttl.rs # Per-endpoint TTL rules
├── error.rs # Typed error definitions
└── lib.rs # Library root (re-exports)
- Language: Rust — minimal memory, instant startup
- HTTP: axum + hyper — async HTTP server
- Cache: redb — embedded key-value store (single file, ACID)
- Tooling: mise — version management
- Phase 1: Local proxy with TTL + ETag caching
- Phase 2: Webhook-based cache invalidation (via relay-worker)
- Phase 3: Team shared cache (L2 layer)
- Phase 4: Web client dashboard (via better-hub) — in progress (
apps/web)
MIT