34 KiB
| read_when | summary | title | x-i18n | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
Protocollo WebSocket del Gateway: handshake, frame, versionamento | Protocollo Gateway |
|
Protocollo Gateway (WebSocket)
Il protocollo WS del Gateway è il singolo control plane + trasporto node per OpenClaw. Tutti i client (CLI, UI web, app macOS, node iOS/Android, node headless) si connettono tramite WebSocket e dichiarano il proprio role + scope al momento dell'handshake.
Trasporto
- WebSocket, frame di testo con payload JSON.
- Il primo frame deve essere una richiesta
connect.
Handshake (connect)
Gateway → Client (challenge pre-connessione):
{
"type": "event",
"event": "connect.challenge",
"payload": { "nonce": "…", "ts": 1737264000000 }
}
Client → Gateway:
{
"type": "req",
"id": "…",
"method": "connect",
"params": {
"minProtocol": 3,
"maxProtocol": 3,
"client": {
"id": "cli",
"version": "1.2.3",
"platform": "macos",
"mode": "operator"
},
"role": "operator",
"scopes": ["operator.read", "operator.write"],
"caps": [],
"commands": [],
"permissions": {},
"auth": { "token": "…" },
"locale": "en-US",
"userAgent": "openclaw-cli/1.2.3",
"device": {
"id": "device_fingerprint",
"publicKey": "…",
"signature": "…",
"signedAt": 1737264000000,
"nonce": "…"
}
}
}
Gateway → Client:
{
"type": "res",
"id": "…",
"ok": true,
"payload": {
"type": "hello-ok",
"protocol": 3,
"server": { "version": "…", "connId": "…" },
"features": { "methods": ["…"], "events": ["…"] },
"snapshot": { "…": "…" },
"policy": {
"maxPayload": 26214400,
"maxBufferedBytes": 52428800,
"tickIntervalMs": 15000
}
}
}
server, features, snapshot e policy sono tutti obbligatori nello schema
(src/gateway/protocol/schema/frames.ts). canvasHostUrl è facoltativo. auth
riporta il role/gli scope negoziati quando disponibili e include deviceToken
quando il gateway ne emette uno.
Quando non viene emesso alcun token dispositivo, hello-ok.auth può comunque riportare i
permessi negoziati:
{
"auth": {
"role": "operator",
"scopes": ["operator.read", "operator.write"]
}
}
Quando viene emesso un token dispositivo, hello-ok include anche:
{
"auth": {
"deviceToken": "…",
"role": "operator",
"scopes": ["operator.read", "operator.write"]
}
}
Durante il trusted bootstrap handoff, hello-ok.auth può anche includere ulteriori
voci di role limitate in deviceTokens:
{
"auth": {
"deviceToken": "…",
"role": "node",
"scopes": [],
"deviceTokens": [
{
"deviceToken": "…",
"role": "operator",
"scopes": ["operator.approvals", "operator.read", "operator.talk.secrets", "operator.write"]
}
]
}
}
Per il flusso bootstrap integrato node/operator, il token node primario resta
scopes: [] e qualsiasi token operator trasferito resta limitato alla allowlist
bootstrap dell'operator (operator.approvals, operator.read,
operator.talk.secrets, operator.write). I controlli degli scope bootstrap restano
con prefisso del role: le voci operator soddisfano solo richieste operator, e i role
non-operator richiedono comunque scope sotto il prefisso del proprio role.
Esempio node
{
"type": "req",
"id": "…",
"method": "connect",
"params": {
"minProtocol": 3,
"maxProtocol": 3,
"client": {
"id": "ios-node",
"version": "1.2.3",
"platform": "ios",
"mode": "node"
},
"role": "node",
"scopes": [],
"caps": ["camera", "canvas", "screen", "location", "voice"],
"commands": ["camera.snap", "canvas.navigate", "screen.record", "location.get"],
"permissions": { "camera.capture": true, "screen.record": false },
"auth": { "token": "…" },
"locale": "en-US",
"userAgent": "openclaw-ios/1.2.3",
"device": {
"id": "device_fingerprint",
"publicKey": "…",
"signature": "…",
"signedAt": 1737264000000,
"nonce": "…"
}
}
}
Framing
- Request:
{type:"req", id, method, params} - Response:
{type:"res", id, ok, payload|error} - Event:
{type:"event", event, payload, seq?, stateVersion?}
I metodi con effetti collaterali richiedono chiavi di idempotenza (vedi schema).
Role + scope
Role
operator= client control plane (CLI/UI/automazione).node= host di capacità (camera/screen/canvas/system.run).
Scope (operator)
Scope comuni:
operator.readoperator.writeoperator.adminoperator.approvalsoperator.pairingoperator.talk.secrets
talk.config con includeSecrets: true richiede operator.talk.secrets
(o operator.admin).
I metodi RPC Gateway registrati dai Plugin possono richiedere il proprio scope operator, ma
i prefissi admin core riservati (config.*, exec.approvals.*, wizard.*,
update.*) vengono sempre risolti in operator.admin.
Lo scope del metodo è solo il primo controllo. Alcuni comandi slash raggiunti tramite
chat.send applicano controlli a livello di comando ancora più rigidi. Ad esempio, le scritture
persistenti /config set e /config unset richiedono operator.admin.
node.pair.approve ha anche un controllo di scope aggiuntivo al momento dell'approvazione,
oltre allo scope base del metodo:
- richieste senza comando:
operator.pairing - richieste con comandi node non-exec:
operator.pairing+operator.write - richieste che includono
system.run,system.run.prepareosystem.which:operator.pairing+operator.admin
Caps/commands/permissions (node)
I node dichiarano le capability claim al momento della connessione:
caps: categorie di capability di alto livello.commands: allowlist dei comandi per invoke.permissions: toggle granulari (ad es.screen.record,camera.capture).
Il Gateway li tratta come claim e applica allowlist lato server.
Presence
system-presencerestituisce voci indicizzate per identità dispositivo.- Le voci di presence includono
deviceId,rolesescopescosì le UI possono mostrare una singola riga per dispositivo anche quando si connette sia come operator sia come node.
Scope degli eventi broadcast
Gli eventi broadcast WebSocket inviati dal server sono limitati dagli scope in modo che le sessioni con scope pairing o solo-node non ricevano passivamente contenuti di sessione.
- I frame chat, agent e tool-result (inclusi gli eventi
agentin streaming e i risultati delle chiamate tool) richiedono almenooperator.read. Le sessioni senzaoperator.readsaltano completamente questi frame. - I broadcast
plugin.*definiti dai Plugin sono limitati aoperator.writeooperator.admin, a seconda di come il Plugin li ha registrati. - Gli eventi di stato e trasporto (
heartbeat,presence,tick, ciclo di vita connect/disconnect, ecc.) restano senza restrizioni così lo stato di salute del trasporto resta osservabile da ogni sessione autenticata. - Le famiglie di eventi broadcast sconosciute sono limitate dagli scope per impostazione predefinita (fail-closed), a meno che un handler registrato non le allenti esplicitamente.
Ogni connessione client mantiene il proprio numero di sequenza per-client, così i broadcast preservano l'ordinamento monotono su quel socket anche quando client diversi vedono sottoinsiemi diversi dello stream di eventi filtrati per scope.
Famiglie comuni di metodi RPC
Questa pagina non è un dump generato completo, ma la superficie WS pubblica è più ampia dei soli esempi di handshake/autenticazione qui sopra. Queste sono le principali famiglie di metodi che il Gateway espone oggi.
hello-ok.features.methods è un elenco di discovery conservativo costruito da
src/gateway/server-methods-list.ts più le esportazioni dei metodi dei Plugin/canali caricati.
Trattalo come feature discovery, non come un dump generato di ogni helper invocabile
implementato in src/gateway/server-methods/*.ts.
Sistema e identità
healthrestituisce lo snapshot di integrità del gateway memorizzato in cache o sondato di recente.statusrestituisce il riepilogo del gateway in stile/status; i campi sensibili sono inclusi solo per client operator con scope admin.gateway.identity.getrestituisce l'identità dispositivo del gateway usata dai flussi relay e pairing.system-presencerestituisce lo snapshot corrente di presence per i dispositivi operator/node connessi.system-eventaggiunge un evento di sistema e può aggiornare/trasmettere il contesto di presence.last-heartbeatrestituisce l'ultimo evento Heartbeat persistito.set-heartbeatsattiva o disattiva l'elaborazione di Heartbeat sul gateway.
Modelli e utilizzo
models.listrestituisce il catalogo dei modelli consentiti a runtime.usage.statusrestituisce riepiloghi di finestre di utilizzo/quota residua del provider.usage.costrestituisce riepiloghi aggregati del costo per un intervallo di date.doctor.memory.statusrestituisce lo stato di prontezza di vector-memory / embedding per il workspace dell'agente predefinito attivo.sessions.usagerestituisce riepiloghi di utilizzo per sessione.sessions.usage.timeseriesrestituisce serie temporali di utilizzo per una sessione.sessions.usage.logsrestituisce voci di log di utilizzo per una sessione.
Canali e helper di login
channels.statusrestituisce riepiloghi di stato dei canali/plugin integrati + inclusi.channels.logoutesegue il logout di un canale/account specifico dove il canale supporta il logout.web.login.startavvia un flusso di login QR/web per l'attuale provider del canale web compatibile con QR.web.login.waitattende il completamento di quel flusso di login QR/web e avvia il canale in caso di successo.push.testinvia una notifica push APNs di test a un node iOS registrato.voicewake.getrestituisce i trigger wake-word memorizzati.voicewake.setaggiorna i trigger wake-word e ne trasmette la modifica.
Messaggistica e log
sendè l'RPC di consegna diretta in uscita per invii mirati a canale/account/thread al di fuori del runner di chat.logs.tailrestituisce il tail del file di log configurato del gateway con controlli per cursor/limit e max-byte.
Talk e TTS
talk.configrestituisce il payload effettivo di configurazione Talk;includeSecretsrichiedeoperator.talk.secrets(ooperator.admin).talk.modeimposta/trasmette lo stato corrente della modalità Talk per i client WebChat/Control UI.talk.speaksintetizza voce tramite il provider di parlato Talk attivo.tts.statusrestituisce lo stato di abilitazione TTS, il provider attivo, i provider di fallback e lo stato di configurazione del provider.tts.providersrestituisce l'inventario visibile dei provider TTS.tts.enableetts.disableattivano/disattivano lo stato delle preferenze TTS.tts.setProvideraggiorna il provider TTS preferito.tts.convertesegue una conversione text-to-speech one-shot.
Secrets, configurazione, aggiornamento e wizard
secrets.reloadrisolve nuovamente i SecretRef attivi e sostituisce lo stato runtime dei secret solo in caso di successo completo.secrets.resolverisolve le assegnazioni di secret di destinazione del comando per un insieme specifico di comando/destinazione.config.getrestituisce lo snapshot e l'hash della configurazione corrente.config.setscrive un payload di configurazione validato.config.patchunisce un aggiornamento parziale della configurazione.config.applyvalida + sostituisce l'intero payload di configurazione.config.schemarestituisce il payload dello schema di configurazione live usato da Control UI e dagli strumenti CLI: schema,uiHints, versione e metadati di generazione, inclusi i metadati di schema di Plugin + canali quando il runtime può caricarli. Lo schema include metadati dei campititle/descriptionderivati dalle stesse etichette e dallo stesso testo di aiuto usati dalla UI, inclusi rami annidati di oggetto, wildcard, item di array e composizionianyOf/oneOf/allOfquando esiste documentazione di campo corrispondente.config.schema.lookuprestituisce un payload di lookup con ambito percorso per un percorso di configurazione: percorso normalizzato, nodo di schema superficiale, hint corrispondente +hintPathe riepiloghi dei figli immediati per drill-down UI/CLI.- I nodi schema di lookup mantengono la documentazione rivolta all'utente e i comuni campi di validazione:
title,description,type,enum,const,format,pattern, limiti numerici/stringa/array/oggetto e flag booleani comeadditionalProperties,deprecated,readOnly,writeOnly. - I riepiloghi dei figli espongono
key,pathnormalizzato,type,required,hasChildren, piùhint/hintPathcorrispondenti.
- I nodi schema di lookup mantengono la documentazione rivolta all'utente e i comuni campi di validazione:
update.runesegue il flusso di aggiornamento del gateway e pianifica un riavvio solo quando l'aggiornamento stesso è riuscito.wizard.start,wizard.next,wizard.statusewizard.cancelespongono la procedura guidata di onboarding tramite WS RPC.
Famiglie principali esistenti
Helper di agente e workspace
agents.listrestituisce le voci agente configurate.agents.create,agents.updateeagents.deletegestiscono i record degli agenti e il wiring del workspace.agents.files.list,agents.files.geteagents.files.setgestiscono i file del workspace bootstrap esposti per un agente.agent.identity.getrestituisce l'identità assistant effettiva per un agente o una sessione.agent.waitattende che un'esecuzione termini e restituisce lo snapshot terminale quando disponibile.
Controllo delle sessioni
sessions.listrestituisce l'indice corrente delle sessioni.sessions.subscribeesessions.unsubscribeattivano/disattivano le sottoscrizioni agli eventi di modifica delle sessioni per il client WS corrente.sessions.messages.subscribeesessions.messages.unsubscribeattivano/disattivano le sottoscrizioni agli eventi transcript/messaggio per una sessione.sessions.previewrestituisce anteprime bounded del transcript per specifiche chiavi di sessione.sessions.resolverisolve o canonicalizza una destinazione sessione.sessions.createcrea una nuova voce di sessione.sessions.sendinvia un messaggio in una sessione esistente.sessions.steerè la variante interrupt-and-steer per una sessione attiva.sessions.abortinterrompe il lavoro attivo per una sessione.sessions.patchaggiorna i metadati/override della sessione.sessions.reset,sessions.deleteesessions.compacteseguono la manutenzione della sessione.sessions.getrestituisce l'intera riga di sessione memorizzata.- l'esecuzione della chat usa ancora
chat.history,chat.send,chat.abortechat.inject. chat.historyè normalizzato per la visualizzazione per i client UI: i tag di direttiva inline vengono rimossi dal testo visibile, i payload XML di tool-call in testo semplice (inclusi<tool_call>...</tool_call>,<function_call>...</function_call>,<tool_calls>...</tool_calls>,<function_calls>...</function_calls>e blocchi tool-call troncati) e i token di controllo del modello ASCII/full-width trapelati vengono rimossi, le righe assistant composte solo da silent-token come esattoNO_REPLY/no_replyvengono omesse e le righe troppo grandi possono essere sostituite con placeholder.
Pairing dei dispositivi e token dispositivo
device.pair.listrestituisce i dispositivi associati in sospeso e approvati.device.pair.approve,device.pair.rejectedevice.pair.removegestiscono i record di pairing del dispositivo.device.token.rotateruota un token di dispositivo associato entro i limiti del role e dello scope approvati.device.token.revokerevoca un token dispositivo associato.
Pairing node, invoke e lavoro in sospeso
node.pair.request,node.pair.list,node.pair.approve,node.pair.rejectenode.pair.verifycoprono il pairing dei node e la verifica bootstrap.node.listenode.describerestituiscono lo stato dei node noti/connessi.node.renameaggiorna un'etichetta di node associato.node.invokeinoltra un comando a un node connesso.node.invoke.resultrestituisce il risultato di una richiesta invoke.node.eventtrasporta nel gateway gli eventi originati dal node.node.canvas.capability.refreshaggiorna i token di capability canvas con scope limitato.node.pending.pullenode.pending.acksono le API di coda per i node connessi.node.pending.enqueueenode.pending.draingestiscono il lavoro pending persistente per node offline/disconnessi.
Famiglie di approvazione
exec.approval.request,exec.approval.get,exec.approval.listeexec.approval.resolvecoprono richieste di approvazione exec one-shot più ricerca/replay delle approvazioni pending.exec.approval.waitDecisionattende una singola approvazione exec pending e restituisce la decisione finale (onullin caso di timeout).exec.approvals.geteexec.approvals.setgestiscono gli snapshot della policy di approvazione exec del gateway.exec.approvals.node.geteexec.approvals.node.setgestiscono la policy di approvazione exec locale del node tramite comandi relay del node.plugin.approval.request,plugin.approval.list,plugin.approval.waitDecisioneplugin.approval.resolvecoprono i flussi di approvazione definiti dai Plugin.
Altre famiglie principali
- automazione:
wakepianifica un'iniezione di testo wake immediata o al prossimo Heartbeatcron.list,cron.status,cron.add,cron.update,cron.remove,cron.run,cron.runs
- skills/tool:
commands.list,skills.*,tools.catalog,tools.effective
Famiglie comuni di eventi
chat: aggiornamenti della chat UI comechat.injecte altri eventi di chat solo-transcript.session.messageesession.tool: aggiornamenti transcript/stream di eventi per una sessione sottoscritta.sessions.changed: l'indice delle sessioni o i metadati sono cambiati.presence: aggiornamenti dello snapshot di presence del sistema.tick: evento periodico di keepalive / liveness.health: aggiornamento dello snapshot di integrità del gateway.heartbeat: aggiornamento dello stream di eventi Heartbeat.cron: evento di modifica di esecuzione/job Cron.shutdown: notifica di arresto del gateway.node.pair.requested/node.pair.resolved: ciclo di vita del pairing node.node.invoke.request: broadcast della richiesta invoke del node.device.pair.requested/device.pair.resolved: ciclo di vita del dispositivo associato.voicewake.changed: la configurazione dei trigger wake-word è cambiata.exec.approval.requested/exec.approval.resolved: ciclo di vita dell'approvazione exec.plugin.approval.requested/plugin.approval.resolved: ciclo di vita dell'approvazione Plugin.
Metodi helper per node
- I node possono chiamare
skills.binsper recuperare l'elenco corrente degli eseguibili Skills per i controlli di auto-allow.
Metodi helper per operator
- Gli operator possono chiamare
commands.list(operator.read) per recuperare l'inventario runtime dei comandi per un agente.agentIdè facoltativo; omettilo per leggere il workspace dell'agente predefinito.scopecontrolla quale superficie usa ilnameprimario:textrestituisce il token del comando testuale primario senza la/inizialenativee il percorso predefinitobothrestituiscono nomi nativi consapevoli del provider quando disponibili
textAliasescontiene alias slash esatti come/modele/m.nativeNamecontiene il nome del comando nativo consapevole del provider quando esiste.providerè facoltativo e influisce solo sulla denominazione nativa più la disponibilità dei comandi Plugin nativi.includeArgs=falseomette i metadati serializzati degli argomenti dalla risposta.
- Gli operator possono chiamare
tools.catalog(operator.read) per recuperare il catalogo runtime dei tool per un agente. La risposta include tool raggruppati e metadati di provenienza:source:coreoppurepluginpluginId: proprietario del Plugin quandosource="plugin"optional: se un tool Plugin è facoltativo
- Gli operator possono chiamare
tools.effective(operator.read) per recuperare l'inventario dei tool runtime-effettivi per una sessione.sessionKeyè obbligatorio.- Il gateway deriva il contesto runtime fidato dalla sessione lato server invece di accettare contesto auth o di consegna fornito dal chiamante.
- La risposta ha scope di sessione e riflette ciò che la conversazione attiva può usare in questo momento, inclusi tool core, Plugin e canale.
- Gli operator possono chiamare
skills.status(operator.read) per recuperare l'inventario visibile delle Skills per un agente.agentIdè facoltativo; omettilo per leggere il workspace dell'agente predefinito.- La risposta include idoneità, requisiti mancanti, controlli di configurazione e opzioni di installazione sanificate senza esporre valori secret grezzi.
- Gli operator possono chiamare
skills.searcheskills.detail(operator.read) per i metadati di discovery di ClawHub. - Gli operator possono chiamare
skills.install(operator.admin) in due modalità:- modalità ClawHub:
{ source: "clawhub", slug, version?, force? }installa una cartella skill nella directoryskills/del workspace dell'agente predefinito. - modalità installer Gateway:
{ name, installId, dangerouslyForceUnsafeInstall?, timeoutMs? }esegue un'azionemetadata.openclaw.installdichiarata sull'host del gateway.
- modalità ClawHub:
- Gli operator possono chiamare
skills.update(operator.admin) in due modalità:- la modalità ClawHub aggiorna uno slug tracciato o tutte le installazioni ClawHub tracciate nel workspace dell'agente predefinito.
- la modalità Config esegue patch dei valori
skills.entries.<skillKey>comeenabled,apiKeyedenv.
Approvazioni exec
- Quando una richiesta exec richiede approvazione, il gateway trasmette
exec.approval.requested. - I client operator risolvono chiamando
exec.approval.resolve(richiede scopeoperator.approvals). - Per
host=node,exec.approval.requestdeve includeresystemRunPlan(argv/cwd/rawCommand/metadati di sessione canonici). Le richieste prive disystemRunPlanvengono rifiutate. - Dopo l'approvazione, le chiamate
node.invoke system.runinoltrate riusano quelsystemRunPlancanonico come contesto autorevole di comando/cwd/sessione. - Se un chiamante modifica
command,rawCommand,cwd,agentIdosessionKeytra prepare e l'inoltro finale approvato disystem.run, il gateway rifiuta l'esecuzione invece di fidarsi del payload modificato.
Fallback di consegna dell'agente
- Le richieste
agentpossono includeredeliver=trueper richiedere la consegna in uscita. bestEffortDeliver=falsemantiene il comportamento rigoroso: le destinazioni di consegna non risolte o solo-internal restituisconoINVALID_REQUEST.bestEffortDeliver=trueconsente il fallback alla sola esecuzione di sessione quando non può essere risolta alcuna route esterna consegnabile (ad esempio sessioni internal/webchat o configurazioni multi-canale ambigue).
Versionamento
PROTOCOL_VERSIONsi trova insrc/gateway/protocol/schema/protocol-schemas.ts.- I client inviano
minProtocol+maxProtocol; il server rifiuta le incompatibilità. - Schemi + modelli sono generati da definizioni TypeBox:
pnpm protocol:genpnpm protocol:gen:swiftpnpm protocol:check
Costanti client
Il client di riferimento in src/gateway/client.ts usa questi valori predefiniti. I valori sono
stabili in tutto il protocollo v3 e costituiscono la baseline attesa per i client di terze parti.
| Constant | Default | Source |
|---|---|---|
PROTOCOL_VERSION |
3 |
src/gateway/protocol/schema/protocol-schemas.ts |
| Timeout richiesta (per RPC) | 30_000 ms |
src/gateway/client.ts (requestTimeoutMs) |
| Timeout preauth / connect-challenge | 10_000 ms |
src/gateway/handshake-timeouts.ts (clamp 250–10_000) |
| Backoff di reconnessione iniziale | 1_000 ms |
src/gateway/client.ts (backoffMs) |
| Backoff massimo di reconnessione | 30_000 ms |
src/gateway/client.ts (scheduleReconnect) |
| Clamp fast-retry dopo chiusura device-token | 250 ms |
src/gateway/client.ts |
Grace di arresto forzato prima di terminate() |
250 ms |
FORCE_STOP_TERMINATE_GRACE_MS |
Timeout predefinito stopAndWait() |
1_000 ms |
STOP_AND_WAIT_TIMEOUT_MS |
Intervallo tick predefinito (pre hello-ok) |
30_000 ms |
src/gateway/client.ts |
| Chiusura per timeout tick | codice 4000 quando il silenzio supera tickIntervalMs * 2 |
src/gateway/client.ts |
MAX_PAYLOAD_BYTES |
25 * 1024 * 1024 (25 MB) |
src/gateway/server-constants.ts |
Il server pubblicizza in hello-ok i valori effettivi policy.tickIntervalMs, policy.maxPayload
e policy.maxBufferedBytes; i client dovrebbero rispettare tali valori
piuttosto che i predefiniti pre-handshake.
Autenticazione
- L'autenticazione del gateway con secret condiviso usa
connect.params.auth.tokenoppureconnect.params.auth.password, a seconda della modalità di autenticazione configurata. - Le modalità che portano identità, come Tailscale Serve
(
gateway.auth.allowTailscale: true) ogateway.auth.mode: "trusted-proxy"non-loopback, soddisfano il controllo di autenticazioneconnectdagli header della richiesta invece che daconnect.params.auth.*. gateway.auth.mode: "none"su ingress privato salta completamente l'autenticazioneconnectcon secret condiviso; non esporre questa modalità su ingress pubblici/non fidati.- Dopo il pairing, il Gateway emette un device token con scope limitato al
role + scope della connessione. Viene restituito in
hello-ok.auth.deviceTokene dovrebbe essere persistito dal client per le connessioni future. - I client dovrebbero persistere il
hello-ok.auth.deviceTokenprimario dopo ogni connessione riuscita. - La riconnessione con quel device token memorizzato dovrebbe anche riutilizzare l'insieme di scope approvati memorizzato per quel token. Questo preserva l'accesso read/probe/status già concesso ed evita che le riconnessioni collassino silenziosamente verso un scope implicito solo-admin più ristretto.
- Composizione lato client dell'autenticazione
connect(selectConnectAuthinsrc/gateway/client.ts):auth.passwordè ortogonale e viene sempre inoltrato quando impostato.auth.tokenviene popolato in ordine di priorità: prima token condiviso esplicito, poideviceTokenesplicito, poi un token per-dispositivo memorizzato (indicizzato dadeviceId+role).auth.bootstrapTokenviene inviato solo quando nessuno dei precedenti ha risolto unauth.token. Un token condiviso o qualsiasi device token risolto lo sopprime.- L'auto-promozione di un device token memorizzato nel retry one-shot
AUTH_TOKEN_MISMATCHè limitata ai trusted endpoint — loopback, oppurewss://contlsFingerprintfissato.wss://pubblico senza pinning non è qualificato.
- Le voci aggiuntive
hello-ok.auth.deviceTokenssono token di bootstrap handoff. Persistile solo quando la connessione ha usato autenticazione bootstrap su un trasporto fidato comewss://o loopback/pairing locale. - Se un client fornisce un
deviceTokenesplicito oscopesespliciti, quell'insieme di scope richiesto dal chiamante resta autorevole; gli scope in cache vengono riutilizzati solo quando il client sta riusando il token per-dispositivo memorizzato. - I device token possono essere ruotati/revocati tramite
device.token.rotateedevice.token.revoke(richiede scopeoperator.pairing). - L'emissione/rotazione dei token resta limitata all'insieme di role approvati registrato nella voce di pairing di quel dispositivo; ruotare un token non può espandere il dispositivo a un role che l'approvazione del pairing non ha mai concesso.
- Per le sessioni con token di dispositivo associato, la gestione dei dispositivi è con scope sul dispositivo stesso a meno che il
chiamante non abbia anche
operator.admin: i chiamanti non-admin possono rimuovere/revocare/ruotare solo la propria voce dispositivo. device.token.rotatecontrolla anche l'insieme di scope operator richiesto rispetto agli scope della sessione corrente del chiamante. I chiamanti non-admin non possono ruotare un token verso un insieme di scope operator più ampio di quello che già possiedono.- I fallimenti di autenticazione includono
error.details.codepiù suggerimenti di recupero:error.details.canRetryWithDeviceToken(boolean)error.details.recommendedNextStep(retry_with_device_token,update_auth_configuration,update_auth_credentials,wait_then_retry,review_auth_configuration)
- Comportamento del client per
AUTH_TOKEN_MISMATCH:- I client fidati possono tentare un retry limitato con un token per-dispositivo in cache.
- Se quel retry fallisce, i client dovrebbero interrompere i loop di riconnessione automatica e mostrare indicazioni per l'azione dell'operatore.
Identità dispositivo + pairing
- I node dovrebbero includere un'identità dispositivo stabile (
device.id) derivata da una fingerprint della keypair. - I gateway emettono token per dispositivo + role.
- Le approvazioni di pairing sono richieste per i nuovi ID dispositivo a meno che l'auto-approvazione locale non sia abilitata.
- L'auto-approvazione del pairing è centrata sulle connessioni dirette local loopback.
- OpenClaw ha anche un percorso ristretto di self-connect backend/container-local per flussi helper con secret condiviso fidati.
- Le connessioni tailnet o LAN sullo stesso host vengono comunque trattate come remote per il pairing e richiedono approvazione.
- Tutti i client WS devono includere l'identità
deviceduranteconnect(operator + node). Control UI può ometterla solo in queste modalità:gateway.controlUi.allowInsecureAuth=trueper compatibilità HTTP insicura solo localhost.- autenticazione riuscita di Control UI operator con
gateway.auth.mode: "trusted-proxy". gateway.controlUi.dangerouslyDisableDeviceAuth=true(break-glass, grave downgrade di sicurezza).
- Tutte le connessioni devono firmare il nonce
connect.challengefornito dal server.
Diagnostica di migrazione dell'autenticazione dispositivo
Per i client legacy che usano ancora il comportamento di firma pre-challenge, connect ora restituisce
codici di dettaglio DEVICE_AUTH_* sotto error.details.code con un error.details.reason stabile.
Errori comuni di migrazione:
| Message | details.code | details.reason | Meaning |
|---|---|---|---|
device nonce required |
DEVICE_AUTH_NONCE_REQUIRED |
device-nonce-missing |
Il client ha omesso device.nonce (o lo ha inviato vuoto). |
device nonce mismatch |
DEVICE_AUTH_NONCE_MISMATCH |
device-nonce-mismatch |
Il client ha firmato con un nonce obsoleto/errato. |
device signature invalid |
DEVICE_AUTH_SIGNATURE_INVALID |
device-signature |
Il payload della firma non corrisponde al payload v2. |
device signature expired |
DEVICE_AUTH_SIGNATURE_EXPIRED |
device-signature-stale |
Il timestamp firmato è fuori dallo skew consentito. |
device identity mismatch |
DEVICE_AUTH_DEVICE_ID_MISMATCH |
device-id-mismatch |
device.id non corrisponde alla fingerprint della chiave pubblica. |
device public key invalid |
DEVICE_AUTH_PUBLIC_KEY_INVALID |
device-public-key |
Il formato/canonicalizzazione della chiave pubblica non è riuscito. |
Obiettivo della migrazione:
- Attendere sempre
connect.challenge. - Firmare il payload v2 che include il nonce del server.
- Inviare lo stesso nonce in
connect.params.device.nonce. - Il payload di firma preferito è
v3, che vincolaplatformedeviceFamilyoltre ai campi device/client/role/scopes/token/nonce. - Le firme legacy
v2restano accettate per compatibilità, ma il pinning dei metadati del dispositivo associato continua a controllare la policy dei comandi alla riconnessione.
TLS + pinning
- TLS è supportato per le connessioni WS.
- I client possono facoltativamente fissare la fingerprint del certificato del gateway (vedi config
gateway.tlspiùgateway.remote.tlsFingerprinto CLI--tls-fingerprint).
Ambito
Questo protocollo espone l'intera API del gateway (stato, canali, modelli, chat,
agent, sessioni, node, approvazioni, ecc.). La superficie esatta è definita dagli
schemi TypeBox in src/gateway/protocol/schema.ts.