Zum Artikel springen
HB CodeDocsApp herunterladen

HandbuchRelay & Konten

Relay-Wire-Protokoll

Die WebSocket-Envelopes, der Methodenvertrag für beide Agenten, die Versionsschranke, die Frist jeder Station, die Routing-Autorität und die Mutations-Replay-Regeln, die die Telefon-Apps verwenden.

Gegen den Quellcode geprüft am 5. Oktober 2026

Auf dieser Seite

Den kanonischen Methodenvertrag finden

Jede Station prüft Anfragen gegen ihre eigene Kopie der Methodenliste
Jede Station prüft Anfragen gegen ihre eigene Kopie der MethodenlisteKein laufendes Programm liest rpc-contract.v1.json. Die Telefon-App, das Relay und der Desktop-Router führen jeweils eine eigene Methodenliste und lehnen ab, was darin fehlt, in dieser Reihenfolge. Vor jeder Auslieferung liest pnpm check:rpc-contract das Manifest und jede Kopie und schlägt bei jeder Abweichung fehl. Bei den Telefon-Apps vergleicht es nur die Methodennamen.
vor der Auslieferung
rpc-contract.v1.json69 Methoden · 24 Mutationen
Vertragsprüfungpnpm check:rpc-contract
liest
liest jede Kopie
bei jeder Anfrage
Telefon-AppRpcMethod-Enum
Relayrequest_validation.rs
Desktop-Routerrouter/mod.rs · Handler
nur Namen
eine Methode außerhalb des Enums oder eine Mutation ohne Schlüssel wird nie gesendet
-32601Methode fehlt in seiner Liste-32027API-Schlüssel ohne nötigen Scope-32026Mutation ohne Schlüssel
-32601unbekannte Methode-32602leerer oder zu langer Schlüssel-32602unbekanntes oder snake_case-Feld
  • rpc-contract.v1.jsonmethods · mutationsRequiringIdempotency

    Das Methodeninventar. Kein Programm lädt es, und kein Code wird daraus erzeugt. Die Prüfung liest es neben jeder Kopie und schlägt bei jeder Abweichung fehl.

  • Relay-Listerequest_validation.rs

    Wird vor dem Weiterleiten geprüft. Eine Methode, die nur hier fehlt, scheitert daher bei jedem Aufruf mit -32601, auch wenn beide Apps und der Desktop sie kennen. -32027 gilt nur für Verbindungen mit API-Schlüssel.

  • Desktop-Routerrouter/mod.rs

    Verlangt für die 24 gelisteten Mutationen einen nicht leeren Idempotenzschlüssel von höchstens 256 Bytes. Eine unbekannte Methode scheitert in ihrem Namespace-Handler.

  • Telefon-EnumsRpcMethod · requiresIdempotencyKey

    iOS und Android können keine Anfrage außerhalb des Enums und keine Mutation ohne Schlüssel bauen. Die Prüfung vergleicht ihre Methodennamen und ignoriert diese Flags.

Das Manifest führt 74 Methoden in vier Namespaces auf. 25 davon sind Mutationen, die einen Idempotenzschlüssel brauchen. Das Relay und der Desktop-Router halten jeweils dieselben zwei Listen im Code.

NamespaceBeispiele
system, 13 Methodensystem.ping, system.codexAuthProfiles, system.claudeAccounts, system.activateCodexAuthProfile, system.activateClaudeAccount, system.agentUsageHistory, system.getVoiceVocabulary, system.getReplyStyle
session, 15 Methodensession.listProjects, session.create, session.getHistoryState
run, 23 Methodenrun.chatLoad, run.chatLoadPageBefore, run.chatOutboxSubmit, run.chatOutboxStopAndSendNow, run.cancel, run.chatContextUsage
files, 23 Methodenfiles.readContent, files.readBinary, files.getGitFileDiffPatch, files.beginChatAttachmentUpload

Vier Methoden bedienen den Antwortstil, die Textverbesserung und die Kontextübersicht. Die ganze Arbeit dieser Methoden macht der Desktop.

MethodeParameter und Ergebnis
system.getReplyStyleKeine Parameter. Gibt die Einstellung des Antwortstils zurück: label, description, style und options. Jede Option hat value, label und description. Alle diese Wörter schreibt der Desktop, und die Clients zeigen sie unverändert.
system.updateReplyStyleMutation. Parameter: style, ste oder default. Speichert den einen Stil aller Agenten und gibt dieselbe Einstellung wie system.getReplyStyle zurück.
run.improveTextParameter: appSessionId, text und mode, improve oder ste. Führt einen flüchtigen Codex-Turn auf dem Desktop aus und gibt text zurück. improve verwendet den Prompt des Servers für die Textverbesserung. ste verwendet die Umschreiberegeln des Antwortstils.
run.chatContextUsageParameter: appSessionId. Gibt contextUsage zurück, die letzte Kontextübersicht, die der Agent für die Session gemessen hat, oder null vor der ersten Messung.

Eine neue Kontextmessung kommt im runtime-Event von chat:stream an.

Eine registrierte Verbindung sendet einen Umschlag aus type und payload. RegisterPayload verwendet deviceId, relayProtocolVersion und optional deviceName, sessionId, resumeToken und targetDesktopDeviceId. Die Geräte-UUID muss zu einer Gerätezeile passen, die das Konto über HTTP registriert hat, die nicht vergessen ist und zu Rolle und Relay-Berechtigung der Verbindung passt. Anfrage-Payloads weisen unbekannte Felder zurück, sodass ein Client, der eine userId in seine eigene Payload schreibt, den ganzen Frame scheitern lässt.

Das Manifest legt auch Größengrenzen fest, die der Desktop im Code durchsetzt.

GrenzeWert und Methode
Relay-Frame32 MiB, jeder Frame
Sessions pro Aufruf16, run.chatReconcileSessions
Größe des Ergebnisses8 MiB, run.chatReconcileSessions
Outbox-Einträge pro Session256, run.chatReconcileSessions
Seite roher Befehlsausgabe256 KiB, run.chatCommandOutputRead
Roher Binär-Lesevorgang16 MiB, files.readBinary
Vergleichen Sie jede Kopie vom Repository-Root aus mit dem Manifest
pnpm check:rpc-contract

Die Versionsschranke sitzt bei der Registrierung

Die Relay-Protokollversion ist 1.7. Das Manifest nennt 1.7 als aktuelle und als minimale Version, und der Desktop, die iOS-App und die Android-App melden alle 1.7 in RegisterPayload. Version 1.7 gab den Chat-Methoden ihre agentenneutralen run.chat-Namen. Das Relay enthält keine Übersetzung für ein älteres Protokoll.

Bei der Registrierung berechnet das Relay die erforderliche Version als das Höhere aus 1.7 und der höchsten minimum_relay_protocol_version der Kompatibilitätsregeln in der Datenbank. Es hält die Regeln 60 Sekunden im Cache. Eine Verbindung unterhalb der erforderlichen Version wird abgewiesen, bevor sie eine Anfrage senden kann.

Die Registrierung scheitert mit einem von drei Fehlercodes.

  • protocolUpdateRequired: relayProtocolVersion fehlt oder ist niedriger als die erforderliche Version. Die Meldung fordert den Benutzer auf, die App zu aktualisieren.
  • invalidRelayProtocolVersion: relayProtocolVersion lässt sich nicht als Version lesen.
  • relayProtocolPolicyUnavailable: Das Relay kann die Kompatibilitätsregeln nicht lesen, oder eine Regel enthält eine Version, die sich nicht lesen lässt.

Eine zweite Prüfung läuft über HTTP, bevor sich der Socket öffnet. POST /public/compatibility/check nimmt einen desktop-Deskriptor, einen mobile-Deskriptor oder beide entgegen, jeweils mit appVersion, platform und relayProtocolVersion. Die Antwort enthält compatible, decision, message, policyVersion, requiredRelayProtocolVersion und je Deskriptor ein Ergebnis mit knownVersion und protocolSupported. decision ist einer der Werte compatible, missingClient, invalidVersion, unsupportedDesktopVersion, unsupportedMobileVersion, unsupportedPair, protocolUpdateRequired und blockedByPolicy.

Der Desktop ruft diese Prüfung mit einem Timeout von 5 Sekunden auf, bevor er sich verbindet. Er sendet nur seinen eigenen Deskriptor. Die Prüfung lässt im Fehlerfall durch: Ist sie nicht erreichbar, liefert sie einen Fehlerstatus, einen nicht lesbaren Body oder kein Desktop-Ergebnis, verbindet sich der Desktop trotzdem, und die Schranke bei der Registrierung bleibt die durchsetzende Prüfung. Eine negative Antwort beendet die Verbindung mit einem Kompatibilitätsfehler.

Beide Agenten nutzen dieselben RPC-Methoden

Codex- und Claude-Sessions verwenden dieselben Methoden. Kein Methodenname enthält einen Agenten, außer den Konto- und Nutzungsmethoden im Namespace system. Der Desktop liest den Agenten aus dem Session-Datensatz und leitet jeden run.chat-Aufruf an diesen Agenten.

Wo der Agent im Protokoll sichtbar wirdVertrag
Session anlegensession.create. agent ist erforderlich und lautet codex oder claude. conversationId ist optional und nennt eine gespeicherte Unterhaltung dieses Agenten, die fortgesetzt wird: eine Codex-Thread-ID oder eine Claude-Session-ID. Hält eine Session im selben Projekt diese Unterhaltung bereits, gibt der Desktop sie zurück und legt nichts an.
Laden und abstimmenrun.chatLoad und run.chatReconcileSessions. Das Ergebnis enthält storageFormat. Eine Codex-Session antwortet mit indexed und eine Claude-Session mit native. Das Manifest legt beide Werte unter timelineStorageFormat.byAgent fest, und die Vertragsprüfung scheitert bei jedem anderen Paar.
Senden, freigeben und stoppenrun.chatOutboxSubmit, run.chatOutboxStopAndSendNow, run.chatResolveApproval und run.cancel. Dieselben Parameter für beide Agenten. Die Session-ID wählt den Agenten.
Codex-Kontensystem.codexAuthProfiles, system.activateCodexAuthProfile und system.codexUsageLimits. Nur Codex. activateCodexAuthProfile nimmt profileId entgegen und liefert die frische Profilliste.
Claude-Kontensystem.claudeAccounts und system.activateClaudeAccount. Nur Claude. activateClaudeAccount nimmt accountId entgegen und liefert die frische Kontoliste.
Nutzungsverlaufsystem.agentUsageHistory. Nimmt agent entgegen. Jeder andere Wert als codex oder claude ist ein Fehler wegen ungültiger Parameter.

Beide activate-Methoden sind Mutationen. Der Desktop wechselt das Konto, sendet ein Desktop-Ereignis, das den Agenten nennt, und antwortet mit der neuen Liste. Ein fehlgeschlagenes Ereignis verzögert nur das Desktop-Fenster bis zu dessen nächstem periodischen Lesen.

Die Anfrage- und Antwortstrukturen untersuchen

Das Relay ergänzt, wer fragt und wohin die Antwort geht
Das Relay ergänzt, wer fragt und wohin die Antwort gehtEin Telefon sendet nur, worum es bittet. Das Relay ergänzt aus dem authentifizierten Socket, wer fragt, und legt einen ausstehenden Eintrag für die Desktop-Verbindung an, an die es weiterleitet. Der Desktop antwortet mit derselben id und Route, die Antwort wird nur zugestellt, wenn sie zu diesem Eintrag passt, und das Relay entfernt die Route, bevor das Telefon die Antwort sieht.
Telefon
Relay
Desktop
Identität stammt aus diesem Socket
rpc.request
idmethodparamsidempotencyKeytraceparent
rpc.request
clientIduserIdtraceparentidmethodparamsidempotencyKey
angelegt
Ausstehender Eintrag(user, desktop, id) → Route, Desktop-Verbindung
jedes andere Feld, etwa userId oder ein Ziel-Desktop, lässt den ganzen Frame scheitern
eine userId, die nicht zum angemeldeten Konto gehört, ist verboten
rpc.response
clientIdidresulterrorisFinaltraceparent
rpc.response
idresulterrorisFinaltraceparentclientId
muss passen
  • Vom Telefon geschriebenrpc.request

    Alles, was ein Telefon senden darf. traceparent ist Pflicht, tracestate ist optional. Der Ziel-Desktop stammt aus der Registrierung und nie aus einer Anfrage.

  • Vom Relay geschriebenclientId · userId

    Aus dem authentifizierten Socket übernommen. clientId ist die Route mobileDeviceId::desktopDeviceId, die der Desktop in Idempotenzschlüsseln als Gerät verwendet und die das Telefon nie sieht.

  • Vom Desktop geschriebenrpc.response

    Der Desktop übernimmt die id des Telefons als correlationId und antwortet damit, sodass eine einzige id den ganzen Hin- und Rückweg kennzeichnet.

  • Ausstehender Eintrag(user, desktop, id)

    Wird für die Desktop-Verbindung angelegt, an die die Anfrage ging. Eine Antwort wird nur zugestellt, wenn ihre id und clientId zu einem Eintrag derselben Verbindung passen, und eine finale Antwort entfernt den Eintrag.

Telefon → Relay: eine lesende RPC-Anfrage
{
  "type": "rpc.request",
  "payload": {
    "id": "request-101",
    "method": "system.ping",
    "params": {},
    "traceparent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
  }
}
Desktop → Relay: die Antwort auf system.ping
{
  "type": "rpc.response",
  "payload": {
    "clientId": "<source-client-id>",
    "id": "request-101",
    "result": {
      "ok": true,
      "service": "desktop-relay",
      "timestampMs": 1789214400000,
      "capabilities": ["run.cancel.exact-run.v1"]
    },
    "isFinal": true
  }
}

Der Desktop vertraut dem Envelope nicht allein. Er vergleicht die userId des Relays mit dem Benutzer seiner eigenen Anmeldung und antwortet bei einer Abweichung mit forbidden. Er verwendet die clientId des Relays als Geräte-ID in den Idempotenz-Scope-Schlüsseln. Die Berechtigungsliste einer Relay-Anfrage ist immer rpc.

Bevor eine Antwort den Desktop verlässt, durchsucht der Sender das Ergebnis nach einem snake_case-Schlüssel. Ein einziger solcher Schlüssel ersetzt die ganze Antwort durch einen Validierungsfehler, sodass ein Handler keinen internen Feldnamen an ein Telefon durchreichen kann.

Jede Station hat ihre eigene Frist

Der Desktop antwortet, bevor das Telefon aufhört zu warten, außer bei einer Outbox-Übermittlung von iOS
Der Desktop antwortet, bevor das Telefon aufhört zu warten, außer bei einer Outbox-Übermittlung von iOSDrei Anfragen auf einer Zeitachse in Sekunden, jede mit einer Bahn für Desktop, Telefon und Relay. Bei einer älteren Timeline-Seite antwortet der Desktop innerhalb von 58 Sekunden, das Telefon wartet 60, und das Relay hält seinen ausstehenden Eintrag 90. Beim ersten Timeline-Ladevorgang lauten die Zahlen 108, 115 und 120. Bei einer Outbox-Übermittlung hört die iOS-App nach 20 Sekunden auf, vor der Desktop-Grenze von 58. Die Android-App wartet 60, und das Relay hält den Eintrag 31 Minuten.
Eine ältere Timeline-Seiterun.chatLoadPageBefore
Desktop
Telefon
Relay
8 s
Dispatch, höchstens 45 s
Antwort bis 58 s
wartet 60 s
ausstehender Eintrag 90 s
Der erste Timeline-Ladevorgangrun.chatLoad
Desktop
Telefon
Relay
8 s
Codex-Ladefrist 90 s
bis 108 s
wartet 115 s
ausstehender Eintrag 120 s
Eine Outbox-Übermittlungrun.chatOutboxSubmit
Desktop
iOS
Android
wartet 60 s
Relay
8 s
Dispatch, höchstens 45 s
Antwort bis 58 s
wartet 20 s
ausstehender Eintrag, 31 min
hört zuerst auf, Wiederholung nutzt den Schlüssel
0 s
20 s
40 s
60 s
80 s
100 s
120 s
Warten auf einen Platz
Antwort einreihen, 5 s
  • Desktopserver_message_handler.rs

    Höchstens 8 Sekunden für einen Platz, dann 45 Sekunden Dispatch, oder 95 für run.chatLoad und run.chatLoadThread, dann 5 Sekunden, um die Antwort einzureihen. Ein Codex-Timeline-Ladevorgang hat seine eigene Frist von 90 Sekunden innerhalb der 95.

  • TelefonmobileChatPolicy

    Beide Apps warten 60 Sekunden auf eine ältere Seite und 115 auf den ersten Ladevorgang, länger, als der Desktop braucht. Auf eine Outbox-Übermittlung wartet die iOS-App nur 20 Sekunden, die Android-App 60. Eine Wiederholung muss dieselbe Operation und denselben Schlüssel tragen.

  • Relaypending_requests.rs

    Hält den ausstehenden Eintrag 90 Sekunden, 120 für die zwei ersten Ladevorgänge und 31 Minuten für run.chatOutboxSubmit. Läuft der Eintrag ab, antwortet das Relay mit -32012, und eine spätere Desktop-Antwort findet keine Route.

Auf dem Desktop wartet jeder Relay-RPC auf eine von zwei Semaphoren. 8 Plätze bedienen leichte Aufrufe. 2 Plätze bedienen schwere Aufrufe, sodass ein langsamer Medien-Lesevorgang system.ping nicht aufhalten kann.

Schwere GruppeMethoden
Timelinerun.chatLoad, run.chatLoadPageBefore, run.chatLoadThread, run.chatLoadThreadPageBefore, run.chatReconcileSessions, run.chatPreviewExistingSession
Dateien und Medienfiles.getPreviewMetadata, files.readBinary, files.readMediaRendition, files.prepareMediaStream, files.readMediaRange
Anhängefiles.saveChatAttachment und die vier Methoden für den Upload in Chunks
Git und Webfiles.getGitDiffDetail, files.getGitFileDiffPatch, run.chatFetchWebPageMarkdownPreview
Desktop-StufeGrenzeAntwort, wenn sie endet
Warten auf einen Platz8 Sekunden-32603 mit dem Text „Desktop is busy; retry the request shortly.“
Dispatch45 Sekunden, oder 95 Sekunden für run.chatLoad und run.chatLoadThread-32603 mit dem Text „Desktop request timed out.“
Antwort in die Socket-Warteschlange stellen5 SekundenKeine. Der Desktop protokolliert den Fehler, und der ausstehende Eintrag des Relays entscheidet.

Beide -32603-Antworten sind einfache interne Fehler ohne retryable-Kennzeichen. Ein Telefon darf sie nicht als Beweis lesen, dass nichts ausgeführt wurde.

Die Dispatch-Grenze ist ein Timeout um das Future des Handlers. An der Grenze verwirft der Desktop dieses Future, und der Handler endet an seinem nächsten await-Punkt. Eine Mutation, die dort abgeschnitten wird, lässt ihren dauerhaften Claim im Zustand running zurück. Ein Handler, dessen Arbeit zu Ende laufen muss, startet sie als eigenen Task und wartet nur auf das Ergebnis. run.chatOutboxStopAndSendNow macht das: Die Methode stoppt den Run und sendet dann die Nachricht, auch wenn das Relay nicht mehr wartet.

Eine konkrete Mutation: Eine Outbox-Nachricht übermitteln

Übermittlungsstruktur. Ersetzen Sie die Identitäten durch eine reale Session. Die Zeitstempel sind Beispiele.
{
  "type": "rpc.request",
  "payload": {
    "id": "request-102",
    "method": "run.chatOutboxSubmit",
    "idempotencyKey": "submit-operation-201",
    "params": {
      "queueId": "queue-201",
      "operationId": "operation-201",
      "sessionId": "<existing-session-id>",
      "userText": "Explain the settings flow.",
      "promptText": "Explain the settings flow.",
      "attachments": [],
      "speedMode": "standard",
      "createdAt": 1789214400000,
      "updatedAt": 1789214400000,
      "intent": "enqueue",
      "sendMode": "queue",
      "source": "composer"
    }
  }
}

Der Remote-Adapter dekodiert WorkspaceChatOutboxSubmitRequest, prüft, dass die Session existiert, und ruft dann submit_outbox_request auf. Das Ergebnis enthält entries und receipt, und receipt hat queueId und operationId. Dieses enqueue-Beispiel speichert die Nachricht am Ende der Warteschlange und sendet sie, wenn die Session untätig ist.

Vier weitere Felder sind optional: threadId, expectedChatSettings, accessMode und expectedActiveRunId. Die Struktur weist jedes Feld zurück, das sie nicht kennt.

Um dieselbe unsichere Operation erneut zu versuchen, behalten Sie deren unveränderliche Operations-Payload und Idempotenz-Identität bei. Generieren Sie keine neue operationId, weil bei einer Netzwerkanfrage ein Timeout aufgetreten ist. Eine bearbeitete Nachricht ist eine andere Operation und muss über die explizite Queue- oder Composer-Aktion abgewickelt werden.

run.chatOutboxStopAndSendNow ist die zweite Outbox-Mutation mit einer Vorbedingung. Ihre Parameter sind sessionId, queueId, operationId, expectedRunId, expectedThreadId und expectedTurnId. Die drei expected-Werte benennen den Run, den das Telefon gesehen hat, sodass der Desktop nie einen anderen Run stoppt.

Routing ist an aktive Verbindungsgenerationen gebunden

Die Zulassung ausstehender RPCs erwirbt das Benutzer-Autoritäts-Gate und die Sperre für Routing-Übergänge. Sie weist einen Server im Draining-Zustand, eine veraltete Quellregistrierung oder eine geänderte Desktop-Verbindungsgeneration ab. Der Pending-Schlüssel enthält die Benutzer-UUID, die normalisierte Desktop-Geräte-ID und die getrimmte Anfrage-ID. Der Datensatz behält außerdem die Desktop-Verbindungs-ID und den Quell-Client.

Dies verhindert, dass eine verspätete Antwort eines vorherigen Desktop-Sockets als Ergebnis der aktuellen Anfrage behandelt wird. Ausstehende Anfragen sind auf Prozess-, Benutzer-, Desktop- und Client-Ebene beschränkt. Ein identischer vorhandener ausstehender Eintrag ist eine duplizierte Anfrage. Ein abweichender Eintrag unter demselben Schlüssel ist ein Konflikt. Keiner der beiden Fälle ist eine neue, nicht nachverfolgte Weiterleitung.

Relay-EntscheidungCode und Verhalten
Anfrage über dem Ratenlimit der Verbindung-32028, retryable. Das Relay verwirft den Frame vor der Validierung und leitet nichts weiter, eine Wiederholung kann eine Mutation also nicht zweimal ausführen. Es beantwortet nur eine rpc.request mit einer Anfrage-ID von höchstens 128 Bytes und einem gültigen traceparent, aus einem eigenen Token-Bucket von 300 Burst und 150 pro Sekunde. Jeder andere Frame über dem Limit erhält keine Antwort.
Methode fehlt in der einkompilierten Erlaubnisliste-32601 method-not-found.
API-Schlüssel-Verbindung ohne Scope-32027 mit requiredScopes, retryable false. Ein HB-Code-JWT trägt immer rpc, read und write, ein Telefon sieht dies also nie.
Mutation ohne SchlüsselidempotencyKey fehlt. -32026, Grund protocolUpdateRequired, retryable false.
Fehlerhafter Trace-Kontext oder fehlerhafte ParameterEin fehlerhafter Trace-Kontext und fehlerhafte session.syncHistoryState-Parameter werden vor der Weiterleitung abgewiesen. Das ist die einzige Methode, deren Parameter das Relay typprüft. Jede andere Payload ist für das Relay undurchsichtig und wird auf dem Desktop validiert.
Grenze ausstehender Anfragen erreicht-32020 mit scope, limit und current, retryable. Die Grenzen sind 4.096 pro Prozess, 512 pro Benutzer, 128 pro Desktop, 64 pro Client.
Dieselbe Anfrage-ID bereits ausstehend-32021 für ein identisches Duplikat, -32022 für eine andere Anfrage unter demselben Schlüssel. IDs sind pro Benutzer und Desktop eindeutig, sodass zwei Telefone, die eine ID gegen einen Desktop wiederverwenden, kollidieren.
Routing hat sich unter der Anfrage verschoben-32023, wenn sich die Desktop-Generation vor der Weiterleitung geändert hat, -32024, während die Routing-Autorität wechselt. Beide sind retryable.
Desktop nicht erreichbar-32010 offline, -32011 reconnecting, -32012 Relay-Timeout, alle retryable. Ein ausstehender Eintrag läuft nach 90 Sekunden ab, nach 120 Sekunden für run.chatLoad und run.chatLoadThread oder nach 31 Minuten für run.chatOutboxSubmit. Eine Desktop-Trennung beantwortet jede ausstehende Anfrage dieser Generation sofort mit -32011.

Zwei Schichten schützen unterschiedliche Operationen

Was eine wiederholte Mutation erhält, hängt davon ab, wann sie ankommt
Was eine wiederholte Mutation erhält, hängt davon ab, wann sie ankommtDie erste Kopie beansprucht ihren Schlüssel im Arbeitsspeicher und in SQLite. Eine Wiederholung, die während der Ausführung ankommt, wartet auf deren Antwort, eine spätere erhält die gespeicherte Antwort, und 48 Stunden nach dem Speichern der Antwort ist der Schlüssel vergessen und eine Wiederholung läuft erneut. Wird die erste Kopie abgebrochen, bleibt ihre SQLite-Zeile running, sodass Wiederholungen busy erhalten, bis die 5-Minuten-Lease endet, und danach eine mehrdeutige Antwort.
Erste Kopiewird fertig
Erste Kopiewird abgebrochen
erste Kopie läuft
Antwort aufbewahrt
eine Wiederholung kommt an
wartet auf dieselbe Antwort
gespeicherte Antwort, nichts läuft
Schlüssel vergessen, läuft erneut
läuft
Wiederverbindung oder Neustart
Zeile läuft noch
busy, nichts läuft
mehrdeutig, läuft nie
Anspruch
Antwort liegt vor
Anspruch + 5 min
gespeichert + 48 h
nicht maßstäblich
vier replay-sichere Methoden laufen stattdessen erneut
gleicher Schlüssel, andere Parameter → Konflikt · neuer Schlüssel bei 4.096 Zeilen → Kapazitätsgrenze
  • Anspruch im Arbeitsspeicherregistry.rs

    Prozesslokal und nach einem Neustart leer. Eine Wiederholung schließt sich der laufenden Kopie an, mit bis zu 32 Wartenden pro Schlüssel. Gehalten werden höchstens 512 abgeschlossene Ergebnisse und 64 laufende Ansprüche.

  • Dauerhafter Anspruchremote_rpc_idempotency

    Eine SQLite-Zeile pro Benutzer, Gerät, Methode und Schlüssel, mit einem SHA-256 der Parameter. Sie übersteht Neustarts, die Tabelle hält höchstens 4.096 nicht abgelaufene Zeilen, und die Zeile wird 48 Stunden nach dem Speichern ihrer Antwort gelöscht.

  • Laufende Leaselease_expires_at

    Fünf Minuten ab dem Anspruch und nie verlängert. Eine noch laufende Zeile ohne lebenden Besitzer antwortet bis dahin mit busy und speichert danach eine mehrdeutige Antwort, die Wiederholungen bis zu ihrem Ablauf erhalten.

Der Scope-Schlüssel verbindet Benutzer, Gerät, Methode und Schlüssel, sodass derselbe Schlüssel von einem anderen Telefon oder für eine andere Methode ein anderer Anspruch ist, und der Fingerabdruck ist ein SHA-256 der serialisierten Parameter. Eine retryable-Zeile mit demselben Fingerabdruck wird zurückgefordert und erneut ausgeführt. Eine gespeicherte Antwort über 256 KiB wird als mehrdeutig festgehalten. 48 Stunden nach dem Speichern seiner Antwort ist ein Schlüssel vergessen, und eine Wiederholung läuft als neue Mutation.

Fehler werden nach Code klassifiziert. Ungültige Anfrage, Methode nicht gefunden, ungültige Parameter, nicht autorisiert, verboten, nicht gefunden, Konflikt, Validierung, Abrechnung, Zahlung erforderlich und nicht implementiert sind endgültig und werden wie gespeichert wiedergegeben. Parse-Fehler und alles mit retryable-Kennzeichen sind wiederholbar. Interne, Datenbank-, Fremddienst- und unbekannte Codes sind mehrdeutig. Scheitert das Erwerben des dauerhaften Claims, wird keine Mutation ausgeführt. Endet die Ausführung, ohne dass ihr Ergebnis gespeichert werden kann, meldet der Router ein mehrdeutiges Ergebnis statt eines Erfolgs, der einen fehlenden Replay-Eintrag verbirgt.

Vier Methoden dürfen nach einer unterbrochenen ersten Kopie erneut laufen: run.chatOutboxSubmit, files.appendChatAttachmentUploadChunk, files.finishChatAttachmentUpload und files.cancelChatAttachmentUpload. Jede hat ihren eigenen Domänen-Eintrag, der einen zweiten Lauf unschädlich macht. Chunk-Append gibt außerdem seinen abgeschlossenen Claim frei, weil das Upload-Ledger den Abschluss abgleicht. Der separate Fingerprint (sessionId, operationId) der Outbox schützt die Nachrichtenzustellung, nachdem der RPC-Handler zurückkehrt. Diese Schichten machen nicht jeden beliebigen Nebeneffekt zu genau einmal (exactly-once).