Source: https://mayphus.org/entries/opencode-responses-gateway/ Title: Building a Responses gateway with explicit limits Metadata: {"date":"2026-10-07","kind":"article","language":"en","locale":"en","route":"/entries/opencode-responses-gateway/","slug":"entries/opencode-responses-gateway","tags":["ai","gateway","software"],"type":"article"} # Building a Responses gateway with explicit limits In June–August 2026, I built a small local gateway so clients speaking the Responses protocol could use selected model-provider endpoints. Version v0.3.2 was released on August 4. The [public repository](https://github.com/mayphus/opencode-responses-gateway) is now archived, so this is a record of the engineering choices, not a promise of current compatibility or support. The gateway had two deliberately different modes. One translated Responses requests into Chat Completions for a model that needed that compatibility layer. The other forwarded a provider's native Responses requests and responses. Forwarding a field is not the same as establishing that the upstream provider offers every hosted feature behind it. The [compatibility matrix](https://github.com/mayphus/opencode-responses-gateway/blob/main/docs/responses-compatibility.md) makes that boundary explicit. ## More than renaming JSON fields Streaming needed event behavior that clients could interpret, including failures after SSE headers had already gone out. Tool calls needed round trips that kept the call, output, and subsequent model input connected. The translation path also had to account for reasoning replay and state handling. Early project notes recorded fixes for malformed upstream JSON, streaming failure handling, missing-key error ordering, and shutdown behavior. The gateway rejected image input in translation mode rather than silently dropping it or adding a separate vision service. Native mode forwarded image and PDF fields, but the gateway did not itself provide hosted web search, file search, code execution, computer use, or image generation. Its Responses transport was HTTP and SSE, not WebSocket. These were compatibility boundaries, not hidden features waiting to be enabled. ## Keeping a local service bounded The documented service listened on loopback, required a generated local bearer token, and stored provider credentials through the operating system's credential store. An August 4 [project security review](https://github.com/mayphus/opencode-responses-gateway/blob/main/docs/security-review.md) recorded fixes for unauthenticated usage, stale PID reuse, dependency and build pinning, and atomic configuration writes. A PID record was paired with a random instance identity checked against the service health response before stopping it. These are the project's documented design and remediation, not an independent security audit. Processes running as the same OS user were not isolated from one another. The project packaged native single-executable archives with checksums. In an [October 2 CI run](https://github.com/mayphus/opencode-responses-gateway/actions/runs/36968346216), test, type-check, build, and smoke jobs passed on Linux, macOS, and Windows for both x64 and arm64; the dependency-audit job also passed. The WinGet live-install job in that run was skipped. Those job results establish the tested build paths, not live availability of upstream hosted tools or every provider behavior. The executables were not Authenticode-signed or Developer-ID-notarized. The earlier gateway's source and history were [preserved in the consolidated repository](https://github.com/mayphus/opencode-responses-gateway/blob/main/docs/consolidation.md). That source move did not migrate anyone's running service, configuration, or stored data. The lasting lesson is to make translation boundaries and unsupported behavior visible: a compatibility endpoint is useful when callers can see exactly what it does and where the upstream provider still owns the answer.