360 Secure SkillHub
360 安全技能中心

proxq

proxq 是基于 Redis/asynq 的 Go 异步 HTTP 代理队列:把任意方法/路径/请求体的 HTTP 请求转发到配置的上游,提交后立即返回 202 和 job ID;通过 GET /__jobs/{id} 轮询 queued/running/completed/failed,GET /__jobs/{id}/content 获取上游原始响应,DELETE 取消。支持路径前缀路由多上游、超时/重试、响应缓存,WebSocket/分块/大请求自动直连绕过。适合慢/不稳后端异步化、webhook 重试、短超时反代后排队重任务。无内置认证,需自行加认证层并仅信任上游。

psyb0t
psyb0t
热度 61.61
最近更新 2026-10-10 23:27:05
分类:开发依赖:第三方 API / 代码执行应用场景:API 集成 / 工作流 / 自动化脚本 / DevOps

安装技能

复制下面的话术发给你的 Agent,它会通过 secure-skills 检索并安装该技能。

执行命令安装此 skill: npx secure-skills-cli add proxq
Metadata
name
proxq
description
Go, Redis-backed async HTTP proxy queue (built on asynq). POST any HTTP request (any method, any path, any body) to a configured upstream, get a job ID back instantly (202), a worker forwards it later, you poll GET /__jobs/{id} for status (queued/running/completed/failed) and GET /__jobs/{id}/content for the replayed upstream response (status/headers/body). DELETE /__jobs/{id} cancels. Path-prefix routing to multiple upstreams, per-upstream timeout/retries/pathFilter, optional response caching (memory or Redis LRU), automatic direct-proxy bypass for WebSocket/chunked/large-body requests. No built-in auth. Use when the user wants to turn a slow/unreliable backend into a fire-and-forget async API, decouple a client from upstream latency, relay webhooks with retries, or queue heavy uploads/processing jobs behind short-timeout reverse proxies.
homepage
https://github.com/psyb0t/docker-proxq
user-invocable
true
metadata
{"openclaw":{"emoji":"🦡","primaryEnv":"PROXQ_URL","requires":{"bins":["curl","docker"]}}}
permissions
{"network":"outbound HTTP to the configured PROXQ_URL (submit/poll/cancel job calls) — AND proxq itself makes arbitrary outbound HTTP requests to whatever upstream/URL you submit through it, on your behalf. That's an SSRF surface: only submit requests you intend proxq's configured upstreams to receive.","shell":"curl + docker/docker-compose invocations shown in setup.md and this file (container lifecycle, request examples) — no other host access"}

proxq

The honey badger of HTTP proxies. POST a request, get a job ID back instantly, come back later for the goods. "I'll get back to you" as a service — every HTTP request becomes an async job in a Redis-backed queue (via asynq).

For installation, configuration, and container setup, see references/setup.md.

Security & safety

  • This is an SSRF surface by design. proxq's whole job is to make outbound HTTP requests to an upstream on your behalf — that's not a bug, it's the feature. Anyone who can submit a job through proxq gets proxq's network position to reach whatever upstreams[].url is configured (and, via directProxyMode/prefix stripping, whatever path/query you tack onto it). Never point an upstream at internal/admin services you wouldn't otherwise expose, and never let untrusted callers choose the upstream prefix or URL.
  • No built-in authentication or authorization. proxq ships with zero auth — no API key, no bearer token, no allowlist. Anyone who can reach PROXQ_URL can submit jobs, poll any job ID, and cancel any job ID (job IDs are UUIDv4 but there is no ownership check). Front it with a reverse proxy doing auth (basic auth, mTLS, an API gateway) or bind it to loopback/an internal network only — do not expose a bare proxq instance to the open internet.
  • Trusted upstreams only. Configure upstreams[].url to point only at backends you control or explicitly trust. proxq forwards the full original request (method, headers, body) plus X-Forwarded-For/X-Real-IP/X-Forwarded-Proto — treat the upstream config the same way you'd treat a reverse-proxy target list.
  • Consumer-only. This skill talks to an instance you (or your operator) already run and trust. It never provisions, hardens, or reconfigures the server — that's covered in setup.md as an explicit operator step.
  • Cancelling a job — DELETE /__jobs/{id} best-effort stops an in-flight job and deletes its record (no undo). Per the no-auth point above there's no ownership check, so only cancel a job you submitted or one the user explicitly named — don't guess IDs or bulk-cancel.

When To Use

  • Put an async facade in front of a slow backend so callers get an instant response instead of hanging on a long-running request.
  • Decouple a client from an upstream that occasionally times out — proxq queues the request, retries transport failures automatically, and the client polls at its own pace.
  • Sit behind a CDN/reverse-proxy with a short request timeout while your real backend takes minutes.
  • Relay webhooks without blocking the sender, with configurable retry/backoff.
  • Queue large uploads or long-running processing jobs (video, exports, reports) so the client doesn't hold a connection open.
  • Mix sync and async traffic on one gateway: fast paths (auth, health) bypass the queue via pathFilter, slow paths get queued.
  • Cache idempotent (or even non-idempotent-but-repeatable) responses so duplicate requests don't re-hit the upstream.

When NOT To Use

  • True real-time / streaming responses — the whole model is submit-then-poll; there's no push/webhook-back-to-caller notification built in.
  • WebSocket or chunked-transfer traffic that needs the queue semantics — those bypass the queue automatically and just get reverse-proxied straight through (see directProxyMode in setup.md), so don't expect a job ID for them.
  • As a public-facing endpoint without your own auth layer in front — proxq has none.
  • Pointing an upstream at anything you don't fully trust — proxq will happily forward arbitrary methods/bodies/headers to it.

Usage

Point at a running instance:

export PROXQ_URL=http://localhost:8080

All job-management endpoints live under jobsPath (default /__jobs). Every response proxq itself generates (not proxied from upstream) carries X-Proxq-Source: proxq — that's how you distinguish a proxq-origin response (job not ready, no upstream match, proxq error) from a real upstream response replayed verbatim.

Submit a job

Any request that doesn't hit a job endpoint gets routed by longest-prefix match to a configured upstream and queued (unless it qualifies for direct-proxy bypass — see setup.md).

curl -s -X POST "$PROXQ_URL/api/heavy-computation" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $UPSTREAM_TOKEN" \
  -d '{"data": "lots of it"}'
# 202 Accepted, X-Proxq-Source: proxq
# {"jobId": "550e8400-e29b-41d4-a716-446655440000"}

If no upstream prefix matches the request path: 502 Bad Gateway, X-Proxq-Source: proxq.

Optional per-request override header: X-Proxq-Timeout: <go-duration> (e.g. X-Proxq-Timeout: 30s) overrides the upstream's configured timeout for that one request. Invalid value → 400 Bad Request.

Poll job status

curl -s "$PROXQ_URL/__jobs/550e8400-e29b-41d4-a716-446655440000"
{"id": "550e8400-...", "status": "completed", "completedAt": "2025-01-01T00:00:00Z"}

Failed job includes error:

{"id": "550e8400-...", "status": "failed", "error": "forward request: dial tcp: connection refused"}

status is one of queued (pending/scheduled/aggregating), running (active, or waiting on a retry), completed (done — response stored, even if upstream returned 4xx/5xx), failed (transport broke and retries are exhausted). Unknown job ID → 404 Not Found, X-Proxq-Source: proxq, body {"code":"NOT_FOUND","message":"Not found"}.

Fetch job content (the payoff)

Replays the upstream response exactly — status code, headers, body — as if you'd called upstream directly.

curl -si "$PROXQ_URL/__jobs/550e8400-e29b-41d4-a716-446655440000/content"
HTTP/1.1 200 OK
Content-Type: application/json
X-Custom-Header: from-upstream

{"result": "done"}

If upstream returned a 404, you get 404 back too — but without X-Proxq-Source (it's a real upstream response). If the job isn't done yet, or doesn't exist: 404 Not Found with X-Proxq-Source: proxq. That header is the whole disambiguation trick — present means "proxq talking", absent means "upstream talking".

Cancel a job

curl -s -X DELETE "$PROXQ_URL/__jobs/550e8400-e29b-41d4-a716-446655440000"
# {"status": "cancelled"}

Best-effort: attempts to stop in-flight processing, then deletes the task record. Unknown job ID → 404 Not Found, X-Proxq-Source: proxq. (See Security & safety — no ownership check, so only cancel jobs you own.)

Poll loop example

JOB_ID=$(curl -s -X POST "$PROXQ_URL/api/report" -d '{}' | jq -r .jobId)

while :; do
  STATUS=$(curl -s "$PROXQ_URL/__jobs/$JOB_ID" | jq -r .status)
  case "$STATUS" in
    completed) curl -s "$PROXQ_URL/__jobs/$JOB_ID/content" | jq; break ;;
    failed)    echo "job failed" >&2; break ;;
    *)         sleep 2 ;;
  esac
done

For upstream routing rules (prefix matching/stripping), direct-proxy bypass conditions, caching behavior, and the full config reference (env vars, docker run/compose), see references/setup.md.