Skip to content

openrot v0.2.11

Choose a tag to compare

@github-actions github-actions released this 18 Sep 12:06
· 9 commits to main since this release

What's New

Reliable bridge rate-limit handling

The bridge now tells real rate limits apart from free-tier quota exhaustion and treats each differently:

  • A FreeUsageLimitError (the free tier's "no more requests" signal) rotates the cascade and retries the same request.
  • Any other retryable status (plain 429, server load, ...) is honored with Retry-After (or a 5s fallback) before the retry — no rotation is wasted on a limit that a backoff can clear.

Retries stay configurable via bridge_retry_statuses / bridge_retry_attempts (and their OPENROT_BRIDGE_* env overrides).

Upstream pacing and concurrency control

Requests leave the bridge through a single shared httpx client instead of one per request, so connections are reused and the daemon stops leaking sockets.

  • bridge_max_concurrent (default 3) — how many parallel upstream requests may be in flight; the rest queue on a global semaphore.
  • bridge_min_interval (default 0.5s) — a global minimum gap between request starts, so a burst of client calls is smoothed out before it reaches the upstream.

Both are settable in config or via OPENROT_BRIDGE_MAX_CONCURRENT / OPENROT_BRIDGE_MIN_INTERVAL.

Free-tier compatibility

opencode's free tier accepts a request only when it looks like it comes from a real opencode client. The bridge now makes bare curl/script calls just work:

  • X-Opencode-Session (and X-Opencode-Request) are injected when missing, matching the format the gateway validates.
  • A request body with fewer than two tools gets stub tools (bash, read) injected, since the gateway rejects tool-less bodies — requests from real opencode clients (30+ tools) pass through untouched.
  • A non-opencode User-Agent (e.g. curl/8.0) is replaced with an opencode one.
  • The unused x-opencode-client marker is dropped rather than forwarded.

Client-supplied session/request ids and an existing opencode User-Agent are always respected, never overwritten. Disable injection with bridge_inject_session: false (OPENROT_BRIDGE_INJECT_SESSION=0).

Verbatim streaming responses

Responses are re-framed exactly as the client expects: content is streamed through chunked encoding when content-length is absent, and a server that ignores Accept-Encoding: identity is decoded transparently instead of forwarding mismatched headers. The request-summary log now reflects the real output size even for streamed responses.

Daemon cascade keeps the bridge healthy

The bridge daemon now runs the cascade in the background instead of sitting passive next to a foreground loop:

  • node health and WARP health loops run as daemon threads, so a dead node or a dropped WARP connection triggers a fallback instead of leaving the bridge serving 502s forever.
  • the node refresh scheduler starts too (when update_interval > 0).

Smarter WARP rotation

WARP rotate verifies the IP actually changed and stops only when it did:

  • disconnects, waits for the session to fully tear down, reconnects, and compares the new egress IP.
  • if Cloudflare re-assigns the same address, it retries (up to OPENROT_WARP_ROTATE_ATTEMPTS, default 5), waiting OPENROT_WARP_ROTATE_DELAY seconds between attempts.
  • returns success only after a real IP change (or connection, after exhausting retries).

Port guards

openrot bridge serve and cascade startup now refuse to run when the port is already in use, failing fast with a clear message instead of silently colliding with another instance.

Request debugging

OPENROT_DEBUG_REQ=1 logs every in/out request to the bridge — method, headers (secrets masked), and a summary of the body (model, stream, tools, message sizes) — making upstream differences trivial to diagnose.

Internal

  • openrot probe is gone: probing is fully covered by verify.
  • openrot.providers.free renamed to openrot.providers.proxy.
  • Shared httpx client factory (openrot.core.http), egress-IP helper, and atomic PID-file handling (openrot.core.pid) now used consistently across proxy, daemon, and bridge.

Full Changelog: v0.2.10...v0.2.11