# proxq Instructions for coding agents (Claude Code, Codex and others) and people changing this repository. Users of the image want the [documentation site](https://psyb0t.github.io/docker-proxq/) instead. ## Agent setup `CLAUDE.md` is a committed symlink to `AGENTS.md`, and `.claude` is a committed symlink to `.agents`, so Claude Code reads the same instructions, rules and skills as Codex. Claude's Edit and Write tools refuse to write through a symlink, so edit `AGENTS.md` and `.agents/rules/*.md` directly. ## What it is A Docker image, `psyb0t/proxq`, holding one Go binary that turns HTTP requests into jobs in a Redis queue. A client sends any request, gets `202 Accepted` with a job ID, and later reads the job status and the stored upstream response through a small jobs API (`/__jobs` by default). WebSocket upgrades, chunked bodies, large bodies and paths picked by a per-upstream filter skip the queue and go straight to the upstream. Responses can be cached in memory or in Redis. The module also ships a public Go package, `pkg/clients/openai`, that wraps the openai-go SDK so its calls go through proxq. ## Architecture - `cmd/main.go`: resolves the config path (`--config`, then `PROXQ_CONFIG`, then `config.yaml`) and calls `app.Run`. The blank import of `slogging/slogconf` configures logging from `LOG_LEVEL`, `LOG_FORMAT` and `LOG_ADD_SOURCE`. - `internal/config`: YAML parsing, defaults, validation and sentinel errors. `Parse` applies defaults, validates, compiles the path filter regexes, then sorts upstreams longest prefix first. - `internal/app`: wiring. One process runs both the asynq worker server and the HTTP server (aichteeteapee `serbewr`) on the same Redis, and builds the cache (common-go `cache`). - `internal/proxy/handler.go`: the catch-all route. Resolves the upstream, decides between direct proxy (WebSocket, chunked, size threshold, path filter) and enqueueing, and writes the `202` with the job ID. - `internal/proxy/worker.go`: the asynq task handler. Forwards the stored request with aichteeteapee `prawxxey.ForwardRequest` (cache, hop-by-hop header stripping, `X-Forwarded-*`) and writes the response as the task result. - `internal/proxy/jobs_handler.go`: `GET {jobsPath}/{id}`, `GET {jobsPath}/{id}/content`, `DELETE {jobsPath}/{id}` over an asynq `Inspector`. - `internal/proxy/job.go`: task type name, status mapping from asynq states, the task envelope and the retry delay function. - `pkg/types`: public header names (`X-Proxq-Source`, `X-Proxq-Timeout`) shared by the server and the client. - `pkg/clients/openai`: an `http.RoundTripper` that enqueues, polls and fetches content, returned inside a real `*openai.Client`. Data flow: client -> serbewr router (request ID, logger, recovery) -> `Handler.ServeHTTP` -> asynq task in Redis -> `Worker.ProcessTask` -> upstream -> task result in Redis -> `JobsHandler.Content` replays it. ## Stack - Language: Go, `go 1.26.9` in `go.mod`, dependencies vendored in `vendor/`. - Libraries: asynq (queue), go-redis, aichteeteapee (`serbewr` server, `prawxxey` forwarding), common-go (`cache`, `slogging`, `errors`), ctxerrors, yaml.v3, openai-go v3 (client package only). - Image: `Dockerfile` builds a static binary on `golang:1.26.10-alpine` and runs it on `alpine:3.24` as the non-root user `proxq`, both pinned by digest. - Tests: testify, testcontainers-go (Redis), `-race`. - Lint: golangci-lint v2 run through `go tool` (pinned in the `tool` block of `go.mod`), config in `.golangci.yml`. ## Layout | Path | Holds | | --- | --- | | `cmd/` | `main.go`, config path resolution | | `internal/app/` | process wiring: asynq client, inspector and server, cache, HTTP routes | | `internal/config/` | config struct, defaults, validation, sentinel errors | | `internal/proxy/` | request handler, worker, jobs API, job types | | `internal/testinfra/` | Redis testcontainer helpers for integration tests | | `pkg/types/` | public header constants | | `pkg/clients/openai/` | the OpenAI SDK client that goes through proxq | | `tests/` | separate Go module with end-to-end tests that build the image (not run by `make test` or CI) | | `config.yaml.example` | example config with every field | | `docs/` | the documentation site, see below | | `.agents/` | the agent skill (`skills/proxq/`), the Claude and Codex plugin manifests, and these rules | | `.github/workflows/pipeline.yml` | CI: lint, tests, govulncheck, image build, tag promotion, release, ClawHub | `docs/` is the documentation site, published by GitHub Pages from `main` at https://psyb0t.github.io/docker-proxq/ (Jekyll with the just-the-docs theme). ## Commands Docker, Make and Bash are enough. Every Go command runs in a pinned `golang` container, with build caches in `.testing/go-cache`. ```bash make help # every target make lint # go fix -diff and golangci-lint make lint-fix # apply go fix and golangci-lint fixes make test # go test -race ./..., integration tests start Redis through testcontainers make test-coverage # tests with the 90% coverage gate, writes coverage-percent.txt make sec # govulncheck make dep # go mod tidy and go mod vendor make build # docker build -t psyb0t/proxq:latest . make docs # build the documentation site into .testing/docs-site ``` `make test` and `make test-coverage` mount the Docker socket into the Go container and use host networking, because the integration tests start Redis with testcontainers. ## Rules Rules for every change: `.agents/rules/`. Read them before editing.