Relay handshake errors
For self-hosters. JWT validation fails. Noise handshake rejected. Workspaces refuse to connect.
This page is for self-hosters of the relay. If you're using the hosted relay at relay.getviewport.com, see Daemon won't connect.
First: read the relay logs
docker logs viewport-relay --tail 200
# or
curl -H "Authorization: Bearer $RELAY_ADMIN_TOKEN" https://relay.your-co.com/logsThe relay logs handshake failures with a clear reason code.
Common errors
JWT validation failed: JWKS unreachable
The relay can't reach the platform's JWKS endpoint to verify daemon tokens.
Check:
docker exec viewport-relay curl -I "$RELAY_JWKS_URL"If that fails: network policy, DNS, or the JWKS URL is wrong. Confirm:
docker exec viewport-relay env | grep JWKSShould match your platform's well-known endpoint, e.g., https://api.getviewport.com/api/.well-known/jwks.json for hosted control plane, or your self-hosted URL.
JWT validation failed: signature mismatch
The daemon's token was signed by a key the relay's JWKS doesn't have.
Causes:
- The platform rotated its signing key but the relay cached an old JWKS. Fix: restart the relay. Or wait 1 hour for the cache to expire.
- The daemon is connecting to the wrong relay (testing instance, etc.). Fix: check
daemon.relay.endpointin~/.viewport/config.json.
JWT validation failed: token expired
The relay credential has aged out. Daemon should auto-rotate, but if it's been offline a long time the token may be past its hard expiry.
Fix: on the daemon, vpd restart. If that doesn't work, vpd unpair && vpd pair.
Noise handshake failed: invalid psk
The workspace is configured with policy_mode: crypto_pairing (Noise IKpsk2), but the daemon is connecting with plain Noise IK.
Fix:
- If you want PSK mode: ensure the daemon was paired after the policy mode was set. Re-pair if needed.
- If you don't want PSK mode: change the workspace policy in the web app: Settings → Security → Policy mode → Standard.
Workspace not found
The relay validated the JWT but the workspace it points at doesn't exist on this platform.
Causes:
- The daemon is paired to a different platform than the relay is talking to.
- The workspace was deleted.
Fix: confirm daemon.api.endpoint (control plane URL) matches RELAY_VALIDATE_URL (relay-to-platform URL).
RELAY_INTERNAL_KEY mismatch
The relay's RELAY_INTERNAL_KEY doesn't match what the platform expects on the validate endpoint.
Fix: rotate. Set the same value in both relay container env and platform config. Restart both.
frame too large
A single frame exceeded RELAY_FRAME_MAX_BYTES (default 1 MiB). The daemon should chunk large transcripts, but if you're hitting this with normal agent activity, raise the limit:
docker run -d -e RELAY_FRAME_MAX_BYTES=4194304 … # 4 MiBrate limit exceeded
The IP you're connecting from exceeded RELAY_RATE_LIMIT_PER_IP. Defaults are conservative.
Fix: raise the limit, or disable for trusted IPs by setting RELAY_RATE_LIMIT_PER_IP=0. See Reference: env vars.