A lightweight Go service backed by Postgres that proxies the GitHub REST and GraphQL APIs. It provides:
- Pooled “donated” tokens with automatic rotation by API category (core/search/code_search/graphql)
- DB-backed caching of GET/HEAD responses with TTL and size limits
- Per‑key rate limiting
- A simple admin UI to create/disable API keys and view usage
Ports: the server listens on :8080.
Admin UI: http://localhost:8080/admin (HTTP Basic Auth, creds from env).
API docs: http://localhost:8080/docs.
Prereqs: Docker + Docker Compose.
- Create a
.envfile in the project root. At minimum, set your GitHub OAuth app values if you want the “donate token” flow:
# --- required for token-donation login (/auth/github) ---
GITHUB_OAUTH_CLIENT_ID=your_client_id
GITHUB_OAUTH_CLIENT_SECRET=your_client_secret
# --- admin login (change in production) ---
ADMIN_USER=admin
ADMIN_PASS=admin
# --- optional tuning (see full list below) ---
MAX_CACHE_TIME=300
MAX_CACHE_SIZE_MB=100
DB_MAX_CONNS=20
MAX_PROXY_BODY_BYTES=1048576
# For local dev the compose file pins BASE_URL to http://localhost:8080Tip: Create your GitHub OAuth App with Homepage URL:
http://localhost:8080Authorization callback URL:http://localhost:8080/auth/github/callbackScope used:read:user(read‑only).
- Start the stack (hot‑reload dev server + Postgres):
docker compose up --build
# in another terminal, view logs:
docker compose logs -f app- Open the app:
- Home:
http://localhost:8080/(donate a token with GitHub) - Admin:
http://localhost:8080/admin(defaultadmin/admin) - API Docs:
http://localhost:8080/docs
-
Create an API key in /admin. You’ll see the key once—copy it now.
-
Make a request through the proxy:
# REST example (public repo)
curl -H "X-API-Key: YOUR_KEY" \
"http://localhost:8080/gh/repos/zachlatta/sshtron"
# GraphQL example
curl -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \
-d '{"query":"{ viewer { login } }"}' \
http://localhost:8080/gh/graphqlYou’ll see helpful response headers like:
X-Gh-Proxy-Cache: hit|missX-Gh-Proxy-Category: core|search|code_search|graphqlX-Gh-Proxy-Client: <your key identifier>X-Gh-Proxy-Donor: <github username>(when a donated token was used)
Postgres in dev is exposed on localhost:5433. The app in Docker connects to
db:5432internally.
- Start Postgres via Compose (for convenience):
docker compose up -d db-
Create
.env(same as above). The defaultDATABASE_URLpoints to the dev DB on localhost:5433. -
Build & run:
go build -o ./bin/server ./cmd/server
./bin/serverMigrations run automatically at startup.
gh-proxy reads environment variables and also loads a .env file from the working directory.
| Variable | Required | Default | What it does |
|---|---|---|---|
DATABASE_URL |
Prod: yes (Dev: optional) | Dev default: postgres://ghproxy:ghproxy@localhost:5433/ghproxy?sslmode=disable (Compose app uses postgres://ghproxy:ghproxy@db:5432/ghproxy?sslmode=disable) |
Postgres connection string. Migrations run automatically. |
BASE_URL |
Yes | http://localhost:8080 |
Public base URL of this service. Must match the external scheme+host (used for OAuth callback + WebSocket origin checks). |
ADMIN_USER |
Yes | admin |
HTTP Basic username for /admin. |
ADMIN_PASS |
Yes | admin |
HTTP Basic password for /admin. |
GITHUB_OAUTH_CLIENT_ID |
Needed for token donation | — | GitHub OAuth App client ID used by /auth/github. |
GITHUB_OAUTH_CLIENT_SECRET |
Needed for token donation | — | GitHub OAuth App client secret. |
MAX_CACHE_TIME |
No | 300 |
Cache TTL in seconds for cached responses (0 = unlimited; stored without expiry). GET/HEAD 200s only; respects public caching. |
MAX_CACHE_SIZE_MB |
No | 100 |
Approximate max size (in MB) of the cached_responses table. Oldest rows are trimmed periodically. |
DB_MAX_CONNS |
No | 20 |
Max connections in the Postgres pool. |
MAX_PROXY_BODY_BYTES |
No | 1048576 |
Max allowed request body to /gh/* in bytes (returns 413 if exceeded). |
If
GITHUB_OAUTH_CLIENT_ID/SECRETaren’t set, the server still runs, but token donation (the “Donate Token” button) will be disabled.
- Homepage:
/— explains the project and lets users donate a GitHub token. - API Docs:
/docs— copy‑paste examples for REST/GraphQL. - Admin:
/admin— create/disable API keys, view usage, recent activity. - REST proxy:
/gh/{path}— proxies tohttps://api.github.com/{path} - GraphQL proxy:
/gh/graphql— proxies tohttps://api.github.com/graphql
All API requests require X-API-Key: <your key>.
- Token rotation: Donated tokens are stored (read‑only scope). The proxy rotates tokens and tracks category‑specific GitHub rate limits. Revoked/unauthorized tokens are marked and skipped automatically.
- Caching: GET/HEAD successful responses are cached in Postgres with a TTL and size cap. Periodic jobs trim old cache rows and keep only recent request logs.
- Rate limiting: Each API key has a per‑second limit (default 10 rps) configured when the key is created.
Build and run as a single container (provide your own Postgres):
# Build a minimal image
docker build -t gh-proxy:latest -f Dockerfile .
# Run (example)
docker run --rm -p 8080:8080 --env-file .env \
-e DATABASE_URL="postgres://user:pass@host:5432/ghproxy?sslmode=disable" \
gh-proxy:latestIn production, set
BASE_URLto your public HTTPS URL (e.g.,https://proxy.example.org) so OAuth and the admin WebSocket work correctly. ChangeADMIN_USER/ADMIN_PASS.
- OAuth login fails / admin live stats don’t update: Ensure
BASE_URLexactly matches the public origin (scheme + hostname + port). - “missing X-API-Key” (401): Include your API key header on
/gh/*requests. - 429 Too Many Requests: Your key hit its per‑second rate limit; lower concurrency or request fewer times per second.
- 413 Request Entity Too Large: Increase
MAX_PROXY_BODY_BYTESif you need to send larger GraphQL payloads.
- Hot‑reload is provided in the dev container via
air. - Server logs include request lines, admin actions, OAuth events, cache activity, and errors.
- See
DEVELOPMENT.mdfor logging commands and tips.