Repository navigation
Releases
0.1.0-rc.2
Release Notes
Names settle: config fields, record values, metrics, and CLI flags now use the glossary's terms, and the Claude route has one form. Many changes are breaking (marked below); a 0.1.0-rc.1 config needs the renames listed under "Breaking, config". wye init writes sample policy tests, and every command run with no arguments says what to run next.
- License: MIT, instead of WTFPL.
- Breaking:
wye init --api-keyis now--provider-key, and--teamis now--shared. The flags name the two choices a setup makes.--sharedwith--provider-keyis no longer an error, since a shared gateway always holds the provider key. - Examples, one directory per use:
examples/developer/(wasexamples/subscription.toml),examples/team/(developers through SSO, CI through GitHub Actions tokens, two keys with failover), andexamples/app/(a gateway token per service, aliases with a canary, a second provider for failover, OpenAI), the last two with policy tests that the test suite runs.examples/policy-tests/is gone; its config trusted a public test key. - The sample price tables (README,
docs/policy.md,examples/wye.toml) priced cache reads on Opus 5.5 and Sonnet 5.5 at 0.1 times input; Anthropic charges 0.05 times. A table copied from them counts cache reads at twice their cost: setcache_read = "0.20"for Opus 5.5 and"0.10"for Sonnet 5.5. examples/wye.tomlsets the[server]and Claude route options live at their defaults instead of in comments, so every option is visible.- Docs: the README says what Wye does, who it is for, how it compares with what teams expect of a gateway, and how fast it is; the guide gains how-tos for several upstreams, costs, model aliases and upgrades, and an OpenTelemetry backend.
wye-bench --agentgatewayruns the benchmark through agentgateway; results indocs/benchmarks.md.- Fix: under an output-token ceiling, a body with a second JSON value after the object (
{...}{"max_output_tokens":10}) is refused. It passed on the OpenAI routes, and a provider whose parser reads only the first value would have seen no ceiling. - Fix: a model name longer than 128 bytes from the provider is no longer kept in records and metrics (Messages and Chat Completions streams).
- Fix: with OTLP on, a usage log whose reader stalls keeps as many records as without OTLP, and shutdown exports the final metrics even while the usage log is blocked.
wye reportkeeps each count on the line of its name when it wraps.- A body sent in chunks so small that it alone passes
max_total_held_bytesgets413instead of the retryable529(503on the OpenAI APIs), which no retry could clear. - The refusal of an output-token value over the ceiling says
exceeds the output-token ceiling of N; it saidthis gateway token's limiteven when the ceiling came from[auth] max_output_tokensalone.wye explainshows(ceiling N). - Breaking, config: keys renamed so each names what it holds, with no aliases; a config with an old name fails at startup with an unknown-field error.
api_key_env→provider_key_envandapi_key_file→provider_key_file(in every upstream);[[auth.issuers]]→[[auth.identity_providers]](and itsgrants);[[auth.keys]]→[[auth.signing_keys]];[auth] max_token_ttl_secs→max_gateway_token_ttl_secs;[server] max_held_request_bytes→max_total_held_bytes;[[pricing.models]]→[[pricing.prices]](and itstiers). The route optionfailover_max_held_bytesis now[server] max_total_kept_bytes: it always bounded kept bodies across the whole process, so it is set once there, with the same default (256 MiB). - Breaking, config:
[upstream]is removed, with no alias; the Claude route has one form,[[routes.claude.upstreams]]entries with optional[routes.claude]fields, and a config with[upstream]fails at startup with an unknown-field error. Move its fields into one[[routes.claude.upstreams]]entry.idis optional when a route has one upstream (its records then carry noupstream, as before), andforward_caller_credential = truemoves there: allowed on the Claude route's only upstream, still loopback only and not with[auth]. - Config errors name the field as the config writes it:
routes.claude.upstreams "a": provider_key_envfor a route's upstream,routes.claude.upstreams[0]: base_urlfor a route's only upstream written without an id, androutes.openai.selectfor a route's own options; a file that fails to load at startup is named the same way. - Breaking, records: what Wye emits is renamed, with no aliases. Usage records: the
outcomegateway_error, which covered Wye's refusals, upstreams that gave no response, and Wye's own faults, splits intorefused(Wye's refusal),upstream_error(Wye's502or504), andgateway_error(Wye's own fault only);failed→truncated;headers_ms→response_headers_ms;context_compacted→after_compaction; theusage_statusnot_observed→none; eachfailoversentry'sstatusorerror→reason(429,529, orconnect);prompt→prompt_hashes; theusage_log_droppedevent'scount→records, as inotlp_dropped. New:upstream_errorsays why an upstream gave no usable response (connect,timeout, orreset), andwye reportcounts it. OTLP logs and spans:gen_ai.system→gen_ai.provider.name, now the provider of the upstream that served the request (aws.bedrockfor Bedrock);wye.requested_model→gen_ai.request.model;wye.headers_ms→wye.response_headers_ms;wye.context_compacted→wye.after_compaction;wye.prompt→wye.prompt_hashes; newwye.upstream_error; tool spanstool <name>→execute_tool <name>, withgen_ai.operation.name; arefusedrequest's span leaves its status unset instead ofERROR. Metrics:wye.telemetry.lost→wye.records.dropped; inwye.failovers,wye.upstream→wye.failover.upstream, unit{request}→{failover};wye.upgrade.issuesunit{request}→{issue}. Client errors: an upstream's header timeout (504) istimeout_error(wasapi_error), and a client body stalled during the hold (408) isinvalid_request_error(wastimeout_error).wye reportstill reads older logs. wye service restartrewrites the service definition when thiswyeor the config'sdrain_delay_secsandshutdown_grace_secscall for a new one, and has the service manager load it; a restart used to keep the old stop timeout.- Breaking, CLI: names say what they hold, with no aliases. The signing key
~/.wye/issuer.keyis~/.wye/signing.key(mv ~/.wye/issuer.key ~/.wye/signing.keykeeps yours);wye token issue --keyis--signing-key;wye explain --pathis--endpoint, and a policy test'spathisendpoint. Output:wye explain's stepstoken:→gateway token:(withkey id),route:→selection:, andprovider key: the caller's own→credential: the caller's own (passthrough), and a path it does not serve readsnot an endpoint Wye serves;wye report's(no hint)class →(unclassified), and its time columns readmodelandtoolsundertime;wye service'sLogandErrors→Usage logandMessages;wye policy-diffnames aprovider key(not the operator's key), agateway token, andWye tokens;wye doctorchecks a config's secrets, files, and upstreams. wye initalso writespolicy-tests/next to the config: sample requests and the decisions the config it wrote makes (with--shared, a request with a gateway token that forwards and one without, refused with401), sowye policy-testpasses right afterwye init. It never writes over a file. Run with no argument, each command does what its name says or fails with one line that says what to run next:wye explainnames a sample request,wye policy-testwithout cases says how to add them,wye token issueandwye policy-diffshow an example,wye claude,wye codex, andwye probesay to runwye initwhen nothing answers and there is no config,wye token issuewithout a signing key says how to make one, andwye token keygenrefuses to replace the keywye token issuesigns with.wye service statusshows the message log too.- Links that the CLI prints or writes (the config
wye initwrites, its deployment step,wye service's advice on secrets) point at this version's release tag instead ofmain, so they describe the config keys and commands of thewyethat printed them.
Install wye 0.1.0-rc.2
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/askrid/wye/releases/download/v0.1.0-rc.2/wye-installer.sh | shDownload wye 0.1.0-rc.2
| File | Platform | Checksum |
|---|---|---|
| wye-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| wye-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| wye-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| wye-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
Verifying GitHub Artifact Attestations
The artifacts in this release have attestations gene...
0.1.0-rc.1
Release Notes
A prerelease, to prove the release pipeline. Wye is a working prototype: tested
and benchmarked on one machine, used with Claude Code through a claude.ai
subscription. Expect the config format to change before 1.0. wye service has
not run under systemd on a real Linux machine.
What is in it:
- Anthropic Messages (
/v1/messages,count_tokens), OpenAI Responses, and
OpenAI Chat Completions, forwarded byte for byte, streams relayed event by
event. - Routes with several accounts. Each Claude Code session stays on one account
(weighted rendezvous hashing on the session id). Failover on429,529,
or a connection refused or cut before the request body was delivered, and
never after the first response byte. - Providers for Claude: Anthropic, Claude Platform on AWS, and Amazon Bedrock,
with API keys. - Gateway tokens: Wye's own Ed25519 tokens, or identity-provider tokens
(RS256, ES256, EdDSA) with keys pinned in config. Model permissions and
output-token ceilings, which a token can narrow but never widen. - Model aliases with per-subject rollout, and upgrade checks that report
which requests a target model would reject. - Usage records per request (tokens, cost, cache hits and misses, session,
subagent, request class, tool calls and time), OTLP logs, traces, and
metrics, and optional cache-miss diagnostics. - Cost from a price table in config, in exact integer arithmetic.
- Commands:
init,serve,validate,doctor,explain,
policy-test,policy-diff,report,probe,claude,codex, and
token, each with--help. A user's files live in~/.wye, and a
command reads~/.wye/wye.tomlunless told otherwise (-c,
$WYE_CONFIG), never a config it merely finds in the working directory.
wye claudeturns on Claude Code's gateway hints (request class, prompt id,
tool times), andwye claude --bgstarts background sessions that go through
the gateway too. wye service install,status,restart, anduninstall: the gateway
as a LaunchAgent (macOS) or systemd user unit (Linux), shown before it is
written and checked before it starts.- Release archives for Linux and macOS (x86_64, arm64) with checksums and
build provenance attestations.
Not yet checked against the real services:
- Bedrock and Claude Platform on AWS were built from the providers'
documentation and tested against mocks. Bedrock's API key header in
particular is unconfirmed (x-api-keyis sent). - Codex support was built from Codex's source and tests, not from a real
session.
Known limits:
- No Windows build, no published container image, no Vertex AI, no AWS SigV4.
- Provider keys are read at startup; short-lived keys need a restart to
refresh. - No cumulative budgets or rate limits, by design: those stay with the
provider. - Model permissions read the request's top-level
model; a model named
inside the body (an OpenAIimage_generationtool's) is not checked. - Findings from review rounds that are still open are listed in
docs/roadmap.md, "Open findings".
Install wye 0.1.0-rc.1
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/askrid/wye/releases/download/v0.1.0-rc.1/wye-installer.sh | shDownload wye 0.1.0-rc.1
| File | Platform | Checksum |
|---|---|---|
| wye-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| wye-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| wye-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| wye-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
Verifying GitHub Artifact Attestations
The artifacts in this release have attestations generated with GitHub Artifact Attestations. These can be verified by using the GitHub CLI:
gh attestation verify <file-path of downloaded artifact> --repo askrid/wyeYou can also download the attestation from GitHub and verify against that directly:
gh attestation verify <file-path of downloaded artifact> --bundle <file-path of downloaded attestation>