Skip to content

Releases: vispar-tech/openrot

openrot v0.4.2

Choose a tag to compare

@github-actions github-actions released this 19 Oct 22:16

What's New

Fixed: release binaries crashed on import

The v0.4.0 and v0.4.1 binaries failed at startup with FileNotFoundError: opencode_tools.json. The PyInstaller spec shipped no data files, and the new gateway module reads openrot/core/data/opencode_tools.json and title_system.txt via Path(__file__).parent at import time — package-data declarations in pyproject.toml don't reach the PyInstaller bundle. The spec now bundles openrot/core/data next to the module.

Fixed: self-update never installed anything

(from v0.4.1, binaries were broken so it needs this release) The early-exit condition in the self-update command was inverted: when an update was available, the CLI printed the message and exited — the download and install step was unreachable. Only the "not updated" paths exit now:

  • already up to date → green message, exit;
  • check failure (network/API error) → red message, exit;
  • update available → confirmation prompt (or --yes), then download and install.

Affected installs must update once manually (e.g. via install.sh), after which self-update works as intended.

openrot v0.4.1

Choose a tag to compare

@github-actions github-actions released this 19 Oct 22:04

What's New

Fixed: self-update never installed anything

The early-exit condition in the self-update command was inverted: when an update was available, the CLI printed the message and exited — the download and install step was unreachable. Only the "not updated" paths exit now:

  • already up to date → green message, exit;
  • check failure (network/API error) → red message, exit;
  • update available → confirmation prompt (or --yes), then download and install.

Applies to every release since the condition was introduced — affected installs must update once manually (e.g. via install.sh), after which self-update works as intended.

openrot v0.4.0

Choose a tag to compare

@github-actions github-actions released this 19 Oct 21:45

What's New

Free-tier gateway normalization

Every upstream request is now reshaped into the exact form the free gateway accepts — the same rules as the official opencode client:

  • The four session headers (X-Opencode-Session, X-Opencode-Session-Id, X-Session-Affinity, X-Session-Id) are set to one canonical ses_ id. A client-provided canonical session is kept for prompt-cache affinity; any other value is deterministically re-encoded into a canonical one.
  • Recognized tool names are replaced with their genuine opencode definitions (shipped as package data), and missing core tools (shell, edit, glob, grep, read) are appended — the gateway rejects stubs that merely reuse a recognized name. Unknown client tools pass through untouched.
  • opencode's title-generation system prompt is swapped for a reworded equivalent. The gateway fingerprints the original by its task/rules/examples markers together and answers 403 FreeTierError, so without the swap the first request of every new chat fails. The replacement is idempotent and leaves unrelated system prompts untouched.
  • Chat bodies are forced to streaming with stream_options.include_usage; missing Authorization falls back to Bearer public, missing X-Opencode-Project/X-Opencode-Client are added, and a non-opencode User-Agent is replaced.
  • The legacy X-Opencode-Request header is stripped instead of injected, and the default upstream moved to https://opencode.ai/inference.

Direct mode (no WARP)

New bridge_direct config flag (OPENROT_BRIDGE_DIRECT=1) turns the bridge into a plain pass-through: WARP is not started, requests go upstream from the host's own address, and every request/response is still logged. Useful when you only want to observe opencode's traffic — IP rotation and 429 self-rotation are unavailable in this mode (a usage-limit 429 is treated like any other rate limit: wait out Retry-After and retry).

Full error visibility

Non-200 response bodies are now logged verbatim (quota, validation, ...), and unexpected handler errors produce a JSON 502 to the client plus a full traceback in the log instead of a silent hang. OPENROT_DEBUG_REQ=1 additionally dumps raw request bodies to debug/ for offline diffing.

Docs

README (EN/RU) updated for the gateway contract, direct mode, and the new env override OPENROT_BRIDGE_DIRECT.

openrot v0.3.0

Choose a tag to compare

@github-actions github-actions released this 08 Oct 14:21

What's New

WARP-only: sing-box and the node chain are gone

openrot no longer bundles or manages sing-box. The entire node chain — VLESS and free-proxy providers, urltest, health checks, and rotation between tiers — is removed. The project is WARP-only: it always tries to maintain a Cloudflare WARP connection and routes opencode traffic through it.

This is a breaking release:

  • start cascade is gone; start bridge is the only start command.
  • Removed commands: profile add/list/remove/set, list, test, update.
  • The warp_enabled flag is gone — WARP is always on. warp off now disconnects once; the health loop reconnects within health_interval (default 30s) while the bridge runs.
  • Removed config: strategy, urltest_url, health_timeout, fail_threshold, profiles, singbox_bin, current_node_id, update_interval, TOP_LIMIT, and the old port (7890).

Direct WARP SOCKS5

The bridge connects straight to WARP's SOCKS5 proxy (socks5://{warp_host}:{warp_port}) instead of proxying through the cascade, so upstream 429s stay visible and rotation is a single WARP IP rotate.

Session-aware bridge logs

Bridge request logs now carry the session id — ses= from X-Opencode-Session — next to req=, so a request can be traced back to its opencode session.

Bridge-only containers and installers

docker-entrypoint.sh, the Dockerfile, Makefile, install.sh, and install-docker.sh no longer reference sing-box or the cascade mode. The container runs the bridge in the foreground on port 7891.

Docs rewritten

README (EN/RU), CONTRIBUTING, and the bug template were rewritten for the WARP-only reality: commands, config reference, env overrides, Docker usage, and troubleshooting.

openrot v0.2.11

Choose a tag to compare

@github-actions github-actions released this 18 Sep 12:06

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

openrot v0.2.10

Choose a tag to compare

@github-actions github-actions released this 04 Sep 08:49

What's New

Wait for in-progress rotation instead of skipping

When the bridge hits a retryable upstream status (e.g. 429) it rotates the cascade before retrying. Previously, if two requests hit the limit at the same time, the second one would find a rotation already in progress and skip its own rotation — then retry against the still-rate-limited node and fail with an error.

Now rotation uses a competition flag:

  • The first caller performs the rotation (as before).
  • Concurrent callers wait for that rotation to finish, then retry against the freshly-rotated node — no error is returned, no second rotation is started.

So a burst of parallel 429s is handled by a single rotation, and every request gets a retry on a healthy node.

Rotation timing context in logs

The bridge now logs whether it actually rotated or waited on an in-progress rotation, with the elapsed time:

INFO [warp] rotation took 0.4s
INFO [warp] waited 0.3s for in-progress rotation

Bug Fixes

  • Fix UnboundLocalError in the bridge retry path when rotate() bails out (no alive node) before assigning the rotation result.

Full Changelog: v0.2.9...v0.2.10

openrot v0.2.9

Choose a tag to compare

@vispar-tech vispar-tech released this 03 Sep 17:36

What's New

Unified logging — single log file

All daemon output now goes into one rotating log file: openrot.log. The old multi-file setup (openrot-events.log + raw daemon stdout) is gone, so there's one place to look for everything.

2026-09-03 20:13:37,612 INFO [bridge] POST /v1/chat/completions big-pickle input=120.3k output=3.2k time=6.2s
  • console.print is used only for interactive foreground output (e.g. the Ctrl-C to stop. tip)
  • The daemon process no longer captures stdout to a file — everything is routed through the events logger with timestamps
  • RotatingFileHandler handles rotation automatically (1MB × 3 backups)

Richer bridge request logs

Request log lines now include more context:

[bridge] POST /v1/chat/completions big-pickle input=120.3k output=3.2k time=6.2s
  • input=... — prompt characters (was prompt=)
  • output=... — response characters from Content-Length
  • time=... — request elapsed time

No duplicate request lines

Previously each request was logged twice (once by the handler, once by the HTTP server's built-in logger). That duplicate is removed.

openrot logs simplifies to one file

The logs command now tails the single openrot.log instead of juggling multiple sources.

Bug Fixes

  • Ctrl-C to stop. no longer appears in the log file in daemon mode (interactive only)
  • Bridge console output now appears correctly when the cascade is already running (console echo is set up up-front)

Full Changelog: v0.2.8...v0.2.9

Full Changelog: v0.2.8...v0.2.9

openrot v0.2.8

Choose a tag to compare

@github-actions github-actions released this 03 Sep 15:28

What's New

Thread-safe WARP rotation

Rotation is now serialized with a lock. Concurrent calls are handled safely — when a rotation is already in progress, the caller returns immediately instead of blocking (or worse, racing). A warning is logged when a rotation is skipped.

INFO rotate skipped: another rotation already in progress

Rotation elapsed time

The bridge now logs how long a rotation/retry takes, so you can see the cost of a rate-limit recovery:

INFO [warp] rotation took 0.4s

Bug Fixes

  • Fix BrokenPipeError in bridge headers — the entire response write path (headers + body) is wrapped in a try/except for BrokenPipeError, ConnectionResetError, and OSError. A client disconnecting mid-response no longer crashes the handler thread.
  • Make bridge logging consistent — all bridge messages now go through the events logger instead of mixing console.print and events.

Full Changelog: v0.2.7...v0.2.8

openrot v0.2.7

Choose a tag to compare

@github-actions github-actions released this 03 Sep 08:29

What's New

Feature: enhanced bridge event logging

Bridge event logs now display additional request metadata: model name, prompt character count, and response time. This makes it easier to monitor request patterns and performance without additional tooling.

Example log line: bridge POST /v1/chat/completions 200 model=gpt-4o prompt=1.2k 23ms

Full Changelog: v0.2.6...v0.2.7

openrot v0.2.6

Choose a tag to compare

@github-actions github-actions released this 03 Sep 08:29

What's New

Feature: stop daemons before self-update and restart after

When running openrot self-update, the cascade daemon, bridge daemon, and sing-box proxy are now gracefully stopped before downloading and installing the update. After the update completes, the cascade daemon is restarted automatically (which respawns the proxy if it was running). This prevents "Text file busy" errors on Linux when replacing a running binary.

Full Changelog: v0.2.5...v0.2.6