run-shunt
Build, launch, and drive shunt — the Claude Code LLM gateway (a Rust/axum Anthropic-Messages proxy). Use to run, start,…
Launch and drive a local reference Claude apps gateway (claude gateway + Dex + Postgres) to probe, capture, or re-verify the login / managed-settings / OTLP-telemetry wire protocol that shunt's gateway superset (login → managed/settings → telemetry, epic #87) must implement. Use
$ npx -y skills add pleaseai/shunt --skill run-claude-gateway-ref --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/run-claude-gateway-refContext preview
The summary Claude sees to decide when to auto-load this skill.
Launch and drive a local reference Claude apps gateway (claude gateway + Dex + Postgres) to probe, capture, or re-verify the login / managed-settings / OTLP-telemetry wire protocol that shunt's gateway superset (login → managed/settings → telemetry, epic #87) must implement. Use
name: run-claude-gateway-ref description: Launch and drive a local reference Claude apps gateway (claude gateway + Dex + Postgres) to probe, capture, or re-verify the login / managed-settings / OTLP-telemetry wire protocol that shunt's gateway superset (login → managed/settings → telemetry, epic #87) must implement. Use to run the reference gateway, walk the device flow non-interactively, capture wire fixtures, or observe the telemetry relay.
Spins up the **real** `claude gateway` (built into the `claude` binary) with a local Dex IdP and throwaway Postgres, then drives the full device-flow login **non-interactively with curl** — no browser needed. Use it to (re)capture the wire contract shunt mirrors: exact token JSON, `/managed/settings` body, env push, OTLP relay behavior.
Everything is driven by `.claude/skills/run-claude-gateway-ref/driver.sh` (paths relative to repo root). Backend note: curl is sufficient — the `/device` browser page is scriptable once you send same-origin CSRF headers.
S=.claude/skills/run-claude-gateway-ref/driver.sh
$S up # pg + dex containers, gateway on :8790, waits healthy
$S sink-start # optional: local OTLP sink so the telemetry relay is observable
$S login # full RFC 8628 device flow via curl → capture/token.json
$S probe # captures protocol.md, managed_settings.json (+304), models.json,
# refresh grant, POST /v1/metrics relay, /user/bootstrap
$S status # health of all three processes
$S down # kill gateway + sink, remove containersArtifacts land in `/tmp/shunt-gw-probe/capture/` (override root with `GWREF_WORK`). Gateway log: `/tmp/shunt-gw-probe/gateway.log` (audit events are JSON lines — `evt`: `device.authorize`, `device.verify`, `session.mint`, `session.refresh`, `managed.serve`, `auth.denied`). Sink log: `/tmp/shunt-gw-probe/sink.log`.
Login identity: `dev@example.com` / `password` (Dex static user, no groups).
Captured live; these fill the gaps `docs/gateway-protocol.md` marks unspecified:
`{"access_token":"<HS256 JWT>","refresh_token":"<opaque>","token_type":"Bearer","expires_in":3600}`. JWT claims: `sub` (IdP subject), `email`, `name`, `aud`=oidc client_id, `iss`=public_url. Refresh **rotates** the refresh token. Gateway refresh = refresh against the IdP (3500ms budget) + re-mint; IdP failure → `401 {"error":"invalid_grant"}`.
`ETag` = checksum, `If-None-Match` → 304. With `telemetry.forward_to` set it pushes **six** env vars (docs say five): `CLAUDE_CODE_ENABLE_TELEMETRY=1`, `OTEL_{METRICS,LOGS,TRACES}_EXPORTER=otlp`, `OTEL_EXPORTER_OTLP_ENDPOINT=<public_url>`, `OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf`, merged with policy `env`.
`token_endpoint_auth_methods_supported: ["none"]`, `scopes_supported`.
attributes stamped by the gateway; `POST /v1/{metrics,logs,traces}` returns 200 even when the destination is down. Boot log says `signals enabled: metrics` for a bare `forward_to` entry.
Anthropic shape (`type`, `has_more`, `first_id`, `last_id`).
policy carries a `desktop:` block.
`Sec-Fetch-Site: same-origin` it returns 200 with "request came from another site and was blocked" instead of the 302 to the IdP. The audit log shows `device.verify result=csrf_rejected`.
`/oauth/callback`** or the device grant is never approved.
storage) → gateway `session.refresh` fails with "timed out after 3500ms" and the client gets `invalid_grant`. Pin `v2.44.0`.
`CLAUDE_GATEWAY_ALLOW_LOOPBACK=1` (driver sets it). `http://` public_url is accepted on loopback.
the next `python3 json.load` fails with "Extra data".
fail. Postgres is required at boot (gateway runs 6 migrations).
is unpinned/master; `docker rm -f shunt-gw-dex` and rerun `up` (pulls v2.44.0).
(driver sends them; check you didn't proxy/strip them).
`/tmp/shunt-gw-probe/gateway.log` first.
Offline credential import: shunt import opencodex --dry-run previews compatible API keys and Cursor/Command Code access tokens. Exports create new private snapshots without changing existing settings or copying refresh tokens. See credential import.