307 Temporary Redirect to a sibling site that serves it. The client repeats the request there.
Cross-site redirect is off by default. With no site ID configured, the router behaves exactly as without the feature.
Hivenet Router does not discover sibling sites or build the sibling map. An external map writer must push the map to every router through
POST /admin/sibling-map, and push it again after a router restarts.How a request is handled
Every inference endpoint can redirect:POST /v1/chat/completions, POST /v1/messages and POST /v1/messages/count_tokens by the map’s chat placements, POST /v1/embeddings by its embedding placements and POST /v1/rerank by its rerank placements. After the API key is authenticated, the model alias is resolved and the model is checked against the key’s allowlist:
- If any healthy agent on this site serves the model, the request is served here. A redirect never replaces a working local agent.
- If the request has already been redirected twice, it is served here as before.
- Otherwise, if the sibling map lists another site inside the request’s scope that serves the model with the endpoint’s kind, the router answers
307to it. Nothing is admitted, queued or charged to the key’s quota on this site. - Otherwise, the request is handled here. When the map places the model on this site with
scale_from_zero: true, it is held until the model wakes (see Hold at zero); otherwise it normally ends inmodel_not_found.
403 model_forbidden here. A redirect takes precedence over a policy’s fallback_provider: when a sibling serves the model, the request goes to the sibling, not to OpenAI or Anthropic.
Choosing the site
Every router computes the same choice from the map, in this order:- Candidates. The sites with a placement of the model and the endpoint’s kind, inside the request’s scope, except this site and the site where the request first arrived.
- Home set. The two candidates that rank highest by a hash of the caller’s API-key ID, the model and the site ID. One key spreads one model over at most two sites, so its rate limits stay meaningful.
- Session order. The home set is ordered by a hash of the key ID and the optional
X-Session-IDrequest header. The same key and session always reach the same site, so its prompt cache is reused. When a site leaves the map, only the sessions that were on it move. - Wake here first. While the map’s hints are fresh, when every home site is cold and the map also places the model on this site, for the endpoint’s kind, with
scale_from_zero: true, the request is not redirected: it is held here while the model wakes. A cold sibling is no faster, and the request stays where it landed. - Hints. While the map’s hints are fresh, a target whose hint says
load: full, or that is cold and cannot scale from zero, is skipped, and a warm target is preferred over a cold one. When every target would be skipped, the first one is used anyway. Afterhints_expire_at, hints are ignored and the session order alone decides.
X-Session-ID with a stable value per conversation to spread one key’s conversations over its two home sites. Without it, every request of a key for one model goes to the same site.
Scope
The scope bounds where a request may be redirected. A client asks for one with theX-Hivenet-Scope request header:
Without the header, the scope is this site’s country: by default a request is only redirected within the country it landed in, and leaving it takes an explicit
EU or all. Any other header value is refused with 400 request_invalid; it is never widened. When this site is missing from the map, a request without the header is not redirected.
The scope travels in the redirect token, and the target serves the request only if it lies inside it. A second redirect keeps the scope of the first.
The redirect token
Clients remove theAuthorization header when a redirect changes host. The 307 therefore carries a signed token in the rt query parameter, and the target authenticates the repeated request with it:
- the token is well formed, its signature is valid, and its request ID (and client request ID, when present) is a UUID;
- it has not expired, with 5 seconds of grace for clock drift between routers;
- it was not issued more than 5 seconds in the future. Such a token means this router’s clock is behind, and it would otherwise stay valid longer than its lifetime;
- it names this site and this request path;
- this site is inside the token’s scope;
- the key it names is still enabled and unexpired, and any
Authorizationheader the client sent belongs to that same key; - the request body is byte-identical to the one the token was issued for. This is checked before any quota is spent;
- the token has not been used before: a token works once.
Authorization header that authenticates, the token is ignored and the request is served on that key as a new request. Otherwise a refused token gets 401 unauthorized, like any other invalid credential.
An accepted request is served on the token’s key. It keeps the first router’s request ID, returned in X-Request-ID, so one request can be followed across both sites’ logs, and the client’s own ID, if it sent one, returned in X-Client-Request-ID. It is routed on the token’s model, the concrete model the first router resolved, so an alias such as model: "auto" is not resolved again. The key’s quota is charged only here.
Loop guard
The hop count travels inside the token. A request that has already been redirected twice is never redirected again: it is served where it is or fails as usual. A second redirect never goes back to the site where the request first arrived. A stale or inconsistent map therefore costs at most one extra redirect, never a loop.Requirements
- Every router runs with the
--site-idof its site, as the map names it. - Every site shares the same redirect signing secrets, each at least 32 bytes long.
- API keys are dynamic keys pushed through
/admin/api-keys, or the auth mode isnone. The router refuses to start with cross-site redirect and staticauth.yamlkeys, because static keys have no key ID that a sibling could resolve. - Every site URL in the map is an absolute
https://origin under one of--redirect-allowed-domains. Clients follow a redirect from HTTPS to HTTP, and some keep sending the API key after a host change, so the router rejectshttp://URLs and any host it does not own. - Clients follow
307redirects. Most HTTP clients and the OpenAI and Anthropic SDKs do so by default. Plainhttpxdoes not: setfollow_redirects=True. A client that does not follow receives the307with amodel_redirectederror body.
Configuration
When both
--redirect-secret-file and HIVENET_ROUTER_REDIRECT_SECRETS are set, the file wins. The router refuses to start when any of these settings is invalid, or when secrets are set without a site ID.
To rotate the secret without refusing tokens in flight, add the new secret as a second line on every site, then move it to the first line on every site, then remove the old one once the token lifetime has passed.
A token that expires before the client follows is refused with 401, and counted as reason="expired" in hivenet_redirect_rejections_total.
Pushing the sibling map
Replace the whole map with an admin request:
Push the same map to every site. A router ignores its own site when it picks a target. Any invalid field rejects the whole push with
400, and the current map is kept. The map is held in memory only: a restarted router has an empty map and redirects nothing until the next push.
Read the current map with GET /admin/sibling-map. Both endpoints return 501 when cross-site redirect is disabled.
Metrics
A rising
reason="expired" count means clients take longer than the token lifetime to follow the redirect. reason="clock" means this router’s clock is behind the others. reason="replay" means a token was presented twice. Compare hivenet_sibling_map_version across routers to find one that missed a push.
Limitations
- A redirect happens only when no healthy agent on this site serves the model. Requests are not redirected when local agents are busy.
- A token works once on each router, not once per site: each router remembers used token IDs in its own memory.
- A client that repeats an already served request at the same redirect URL without its API key is refused with
401, because that token was used. Repeating the request at the original URL gets a new token, and a client that also sends its API key is served on it as a new request. - API keys have no home country or maximum scope yet: the default scope is the country of the site the request landed in, and any request may ask for
EUorall. - The map push is protected by the admin key only. Treat that key as a map-writer secret: the map decides where clients and their API keys are sent.
- Only the
rtquery parameter is redacted from the access log; other query parameters are logged as before.

