Gateway
Gateway protocol auth
How a client proves who it is: the handshake auth paths, device identity and pairing signatures, and TLS pinning.
Auth
- Shared-secret gateway auth uses
connect.params.auth.tokenorconnect.params.auth.password, depending on the configuredgateway.auth.mode("none" | "token" | "password" | "trusted-proxy"). - Identity-bearing modes such as Tailscale Serve (
gateway.auth.allowTailscale: true) or non-loopbackgateway.auth.mode: "trusted-proxy"satisfy the connect auth check from request headers instead ofconnect.params.auth.*. - Private-ingress
gateway.auth.mode: "none"skips shared-secret connect auth entirely; do not expose that mode on public/untrusted ingress. - After pairing, the gateway issues a device token scoped to the connection
role + approved grant, returned in
hello-ok.auth.deviceToken. Clients should persist it withhello-ok.auth.scopesafter a successful connect when the token is new or different from the stored token. hello-ok.auth.scopesis the current socket's live authority and matches the scopes enforced by RPC dispatch.- When
hello-ok.auth.deviceTokenexactly matches the token already stored for the same gateway, device, client, and role, preserve that record's stored scopes instead of replacing them with a narrower live scope set. A newly issued or rotated token useshello-ok.auth.scopes; its approved grant matches that connection when it is issued. - Reconnecting with that stored device token should also reuse the stored approved scope set for that token. This preserves read/probe/status access already granted and avoids silently collapsing reconnects to a narrower implicit admin-only scope.
- Client-side connect auth assembly (
selectConnectAuthinpackages/gateway-client/src/client.ts):auth.passwordis orthogonal and always forwarded when set.auth.tokenis populated in priority order: explicit shared token first, then an explicitdeviceToken, then a stored per-device token (keyed bydeviceId+role).auth.bootstrapTokenis sent only when none of the above resolvedauth.token. A shared token or any resolved device token suppresses it.- Auto-promotion of a stored device token on the one-shot
AUTH_TOKEN_MISMATCHretry is gated to trusted endpoints only: loopback, orwss://with a pinnedtlsFingerprint. Publicwss://without pinning does not qualify.
- Built-in setup-code bootstrap returns the primary node
hello-ok.auth.deviceTokenplus a bounded operator token inhello-ok.auth.deviceTokensfor trusted mobile handoff. The operator token includesoperator.talk.secretsfor native Talk configuration reads, but excludes pairing-mutation scopes andoperator.admin. hello-ok.auth.deviceTokenscontains only additional bootstrap-handoff tokens. Do not use it as metadata for the primarydeviceTokenreconnect record.- While a non-baseline setup-code bootstrap waits for approval,
PAIRING_REQUIREDdetails includerecommendedNextStep: "wait_then_retry",retryable: true, andpauseReconnect: false. Keep reconnecting with the same bootstrap token until the request is approved or the token becomes invalid. - Persist
hello-ok.auth.deviceTokensonly when the connect used bootstrap auth on a trusted transport such aswss://or loopback/local pairing. - If a client supplies an explicit
deviceTokenor explicitscopes, that caller-requested scope set remains authoritative for the live connection and is reported inhello-ok.auth.scopes; cached token-grant scopes are only reused when the client is reusing the stored per-device token. - Device tokens can be rotated/revoked via
device.token.rotateanddevice.token.revoke(requiresoperator.pairing). Rotating or revoking a node or other non-operator role also requiresoperator.admin. device.token.rotatereturns rotation metadata. It echoes the replacement bearer token only for same-device calls already authenticated with that device token, so token-only clients can persist their replacement before reconnecting. Shared/admin rotations do not echo the bearer token.- Token issuance, rotation, and revocation stay bounded to the approved role set recorded in that device's pairing entry; token mutation cannot expand or target a device role that pairing approval never granted.
- For paired-device token sessions, device management is self-scoped unless
the caller also has
operator.admin: non-admin callers can manage only the operator token for their own device entry. Node and other non-operator token management is admin-only, even for the caller's own device. device.token.rotateanddevice.token.revokealso check the target operator token scope set against the caller's current session scopes. Non-admin callers cannot rotate or revoke a broader operator token than they already hold.- Auth failures include
error.details.codeplus recovery hints:error.details.canRetryWithDeviceToken(boolean)error.details.recommendedNextStep: one ofretry_with_device_token,update_auth_configuration,update_auth_credentials,wait_then_retry,review_auth_configuration(packages/gateway-protocol/src/connect-error-details.ts).
- Client behavior for
AUTH_TOKEN_MISMATCH:- Trusted clients may attempt one bounded retry with a cached per-device token.
- If that retry fails, stop automatic reconnect loops and surface operator action guidance.
AUTH_SCOPE_MISMATCHmeans the device token was recognized but does not cover the requested role/scopes. Do not present this as a bad token; prompt the operator to re-pair or approve the narrower/broader scope contract.
Device identity and pairing
- Nodes should include a stable device identity (
device.id) derived from a keypair fingerprint. - Gateways issue tokens per device + role.
- Pairing approvals are required for new device IDs unless local auto-approval is enabled.
- Pairing auto-approval is centered on direct local loopback connects.
- OpenClaw also has a narrow backend/container-local self-connect path for trusted shared-secret helper flows.
- Same-host tailnet or LAN connects are still treated as remote for pairing and require approval.
- WS clients normally include
deviceidentity duringconnect(operator + node). The only device-less operator exceptions are explicit trust paths:- successful
gateway.auth.mode: "trusted-proxy"operator Control UI auth. - direct-loopback
gateway-clientbackend RPCs on the reserved internal helper path.
- successful
- Omitting device identity has scope consequences. When a device-less
operator connection is allowed through an explicit trust path, OpenClaw
still clears self-declared scopes to an empty set unless that path has a
named scope-preservation exception. Scope-gated methods then fail with
missing scope. - The reserved direct-loopback
gateway-clientbackend helper path preserves scopes only for internal local control-plane RPCs; custom backend IDs do not receive this exception. - All connections must sign the server-provided
connect.challengenonce.
Device auth migration diagnostics
For legacy clients that still use pre-challenge signing behavior, connect
returns DEVICE_AUTH_* detail codes under error.details.code with a stable
error.details.reason.
Common migration failures:
| Message | details.code | details.reason | Meaning |
|---|---|---|---|
device nonce required |
DEVICE_AUTH_NONCE_REQUIRED |
device-nonce-missing |
Client omitted device.nonce (or sent blank). |
device nonce mismatch |
DEVICE_AUTH_NONCE_MISMATCH |
device-nonce-mismatch |
Client signed with a stale/wrong nonce. |
device signature invalid |
DEVICE_AUTH_SIGNATURE_INVALID |
device-signature |
Signature payload does not match v2 payload. |
device signature expired |
DEVICE_AUTH_SIGNATURE_EXPIRED |
device-signature-stale |
Signed timestamp is outside allowed skew. |
device identity mismatch |
DEVICE_AUTH_DEVICE_ID_MISMATCH |
device-id-mismatch |
device.id does not match public key fingerprint. |
device public key invalid |
DEVICE_AUTH_PUBLIC_KEY_INVALID |
device-public-key |
Public key format/canonicalization failed. |
Migration target:
- Always wait for
connect.challenge. - Use
connect.challenge.payload.tsasconnect.params.device.signedAt. - Sign the v2 payload that includes the server nonce.
- Send the same nonce in
connect.params.device.nonce. - Preferred signature payload is
v3(buildDeviceAuthPayloadV3inpackages/gateway-client/src/device-auth.ts), which bindsplatformanddeviceFamilyin addition to device/client/role/scopes/token/nonce fields. - Legacy
v2signatures remain accepted for compatibility, but paired-device metadata pinning still controls command policy on reconnect.
TLS and pinning
- TLS is supported for WS connections (
gateway.tlsconfig). - Clients may optionally pin the gateway cert fingerprint via
gateway.remote.tlsFingerprintor CLI--tls-fingerprint.
Was this useful?