ANY /{namespace}/{id}{rest}Resolves the id to its worker (placing it if needed) and streams the request there. The response is the worker's, streamed back unchanged except for the headers listed below. See Routing for how a worker is chosen.
Authentication
Per the namespace's data-plane rule. Open when the server has no namespaces rules. A request carrying valid instance attestation skips the rule.
Path parameters
| Name | Type | Description |
|---|---|---|
namespace | string | A namespace a worker registered, without the leading /. |
id | string | Instance id: 1–256 characters of [A-Za-z0-9._~-]. |
rest | string | Optional. Path after the id, forwarded verbatim. |
The query string is forwarded verbatim.
Request headers
| Header | Handling |
|---|---|
Authorization (or the configured cookie/header) | Checked against the data-plane rule, then forwarded. |
Host | Hostname-aware mode: selects the hostname. Never forwarded; the worker sees its own host. |
Pinboard-* | Read for instance attestation, then stripped. Never forwarded. |
X-Forwarded-For | Replaced with the peer address Pinboard observed. |
Hop-by-hop (Connection, Keep-Alive, TE, Trailer, Transfer-Encoding, Upgrade, Proxy-*, and any named in Connection) | Stripped. |
| Everything else | Forwarded. |
Pinboard adds:
| Header | Value |
|---|---|
Pinboard-Attestation | HS256 JWS for the target worker; see Authentication. |
Pinboard-Epoch | Placement epoch, decimal string. |
Request body
Any. Streamed to the worker without buffering or a size cap. Never replayed.
Example
curl -N https://pinboard.example.com/threads/t_8f2a/stream \
-H "Authorization: Bearer $USER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"attach"}'Response
The worker's status, headers and body, streamed. Pinboard:
- strips hop-by-hop headers and every
Pinboard-*header; - strips
Pinned-Refusal(an internal signal; see below); - sets
X-Accel-Buffering: no; - when the request had an
Originand the response has noAccess-Control-Allow-Origin, setsAccess-Control-Allow-Origin: {Origin}and appendsVary: origin. This applies to Pinboard's own error responses and to worker responses alike.
Redirects from the worker are passed to the client, never followed.
WebSockets
Send the upgrade to the same path. Resolution, auth and attestation are identical. On success the client gets the worker's 101 with Pinboard-* and Pinned-Refusal headers removed, and Pinboard pipes the two sockets. TCP keepalive (30 s) is the tunnel's dead-peer detection.
When the upgrade fails before the tunnel opens, the client gets a bare status line and closes:
HTTP/1.1 503 Service Unavailable
retry-after: 1
connection: close- No body and no
errorstring; only the status and, for503,Retry-After. - A
500destroys the socket without a status line. - A worker that answers the upgrade with a non-
101status has its status line relayed. - A WebSocket upgrade to
/healthz,/readyzor/registry/*is treated as an instance request (namespace/registry,/healthz, …).
In-band refusal
A worker that cannot take a new activation answers 503 with Pinned-Refusal: lease-unhealthy. Pinboard does not relay it:
- Releases the placement and counts a
nackagainst the worker. - If the request has no body (or is an upgrade), re-resolves once, excluding that worker, and forwards there.
- Otherwise, or when the retry also refuses or no other worker exists:
503 {"error":"worker refused activation, retry"}.
Errors
| Status | Body | When |
|---|---|---|
400 | id must be non-empty / id must be at most 256 characters / id must contain only [A-Za-z0-9._~-] (text) | Invalid id |
400 | {"error":"missing Host header"} | Hostname-aware, no Host |
401 | {"error":"invalid credential"} | A provider rejected the credential |
401 | {"error":"authentication required"} | Rule requires a credential |
403 | {"error":"namespace not allowed"} | No data-plane rule matches |
403 | {"error":"insufficient scope"} | Missing a required scope |
403 | attestation errors | See Call from an instance |
404 | {"error":"unknown namespace"} | No id segment in the path, or no worker registered the namespace |
502 | bad gateway (text) | Worker unreachable or failed before headers |
502 | empty | Client aborted before the forward started |
503 | {"error":"…"} + Retry-After | A refusal; see Errors |
504 | upstream timeout (text) | No response headers within forwards.timeoutMs (30 s) |
508 | {"error":"subrequest depth exceeded"} | Instance call depth over 50 |
500 | {"error":"internal error"} | Unhandled error |
Check order: id, attestation, depth, data-plane rule, hostname binding, caller scope, resolve, inflight cap, forward.
Limits
| Limit | Default | Option |
|---|---|---|
| Wait for response headers | 30 s | forwards.timeoutMs |
| Forwards awaiting headers, per worker | 1024 | forwards.maxInflight |
| Body idle on a streamed response (Node entry) | 60 s | none |
| TCP connect to a worker (Node entry) | 5 s | none |
A streaming response body is never cut by the header timeout.