6 Punkte von GN⁺ 2025-09-23 | 1 Kommentare | Auf WhatsApp teilen
  • Cap'n Web ist ein neues, in TypeScript implementiertes RPC-Protokoll, das für Web-Umgebungen optimiert ist und in verschiedenen JavaScript-Runtimes läuft
  • Es bietet JSON-basierte Serialisierung und ein menschenlesbares Datenformat – ganz ohne Schema oder umständlichen Boilerplate-Code
  • Durch ein Object-Capability-basiertes Modell sind bidirektionale Aufrufe, die Übergabe von Funktions- und Objektreferenzen, Promise-Pipelining und die Umsetzung von Sicherheitsmustern möglich
  • Es unterstützt verschiedene Netzwerkumgebungen wie WebSocket, HTTP, postMessage und ist ein leichtgewichtiges Open-Source-Projekt mit weniger als 10 kB
  • Es löst nicht nur das Waterfall-Problem ähnlich wie GraphQL, sondern ermöglicht auch eine natürliche Modellierung von RPCs wie bei normalen JavaScript-APIs

Was ist Cap'n Web?

  • Cap'n Web ist ein von Cloudflare entwickeltes Open-Source-RPC-System auf TypeScript-Basis
  • Es ist von Cap'n Proto inspiriert, arbeitet aber ohne separate Schemadefinition und nutzt eine menschenfreundliche Serialisierung auf JSON-Basis
  • Es ist in TypeScript integriert und verbessert damit die Developer Experience durch Autovervollständigung, Type-Checking usw.; Laufzeit-Typvalidierung kann separat behandelt werden, etwa mit Type Guards
  • Es unterstützt Netzwerkprotokolle wie HTTP, WebSocket und postMessage und läuft in gängigen Browsern, Cloudflare Workers, Node.js und mehr
  • Dank der leichtgewichtigen, abhängigkeitslosen Struktur bleibt es bei minify + gzip unter 10 kB

Das Object-Capability-Modell (OCap) von Cap'n Web

  • Es verwendet ein Object-Capability-Modell, das mehr Ausdrucksmöglichkeiten bietet als herkömmliche RPC-Systeme
    • Bidirektionale Aufrufe: Client und Server können gegenseitig Funktionen aufrufen
    • Übergabe von Funktions- und Objektreferenzen: Wenn Funktionen oder Objekte per RPC übergeben werden, erhält die Gegenseite einen Stub, dessen Aufruf am Ursprungsort ausgeführt wird
    • Promise Pipelining: Beim Verketten mehrerer RPCs ist nur ein einziger Netzwerk-Roundtrip nötig
    • Sicherheitsmuster: Sicherheitskontrollen wie Autorisierung und Session-Management lassen sich auf natürliche Weise umsetzen

Grundlegende Nutzung

  • Client-Beispiel

    import { newWebSocketRpcSession } from "capnweb"  
    let api = newWebSocketRpcSession("wss://example.com/api")  
    let result = await api.hello("World")  
    console.log(result)  
    
  • Server-Beispiel (auf Basis eines Cloudflare Workers)

    import { RpcTarget, newWorkersRpcResponse } from "capnweb"  
    class MyApiServer extends RpcTarget {  
      hello(name) {  
        return `Hello, ${name}!`  
      }  
    }  
    export default {  
      fetch(request, env, ctx) {  
        let url = new URL(request.url)  
        if (url.pathname === "/api") {  
          return newWorkersRpcResponse(request, new MyApiServer())  
        }  
        return new Response("Not found", {status: 404})  
      }  
    }  
    
  • Zusätzliche Methoden für die API, die Übergabe von Callback-Funktionen vom Client sowie die Definition und Anwendung von TypeScript-Interfaces lassen sich einfach umsetzen

Was ist RPC und was zeichnet Cap'n Web aus?

  • RPC (Remote Procedure Call) ist ein Konzept, mit dem zwei Programme über ein Netzwerk so kommunizieren können, als würden sie Funktionen aufrufen
  • Anders als bei traditionellen HTTP/REST-Protokollen ermöglicht RPC durch die Abstraktion von Funktionsaufrufen Code, der der Denkweise von Entwickler:innen entspricht
  • Cap'n Web passt gut zum modernen JavaScript-Stil mit async/await, Promise und Exception-Support
  • Anders als bei den historischen Kontroversen um RPC (synchrone Aufrufe, Netzwerkfehler) ist eine Nutzung in modernen JS-Umgebungen heute sicherer und effizienter möglich

Einsatzszenarien für Cap'n Web

  • Geeignet für alle Umgebungen, in denen Netzwerkkommunikation zwischen zwei JavaScript-Anwendungen nötig ist
    • Client-Server-Kommunikation, Aufrufe zwischen Microservices usw.
    • Besonders geeignet für Web-Apps mit Echtzeit-Zusammenarbeit und für Interaktionen über komplexe Sicherheitsgrenzen hinweg
  • Noch in einer experimentellen Phase und daher besonders interessant für Entwickler:innen, die offen für moderne Technologien sind

Verschiedene Funktionen

HTTP-Batch-Modus

  • Wenn keine dauerhafte Verbindung nötig ist, können im HTTP-Batch-Modus mehrere RPC-Aufrufe gebündelt und auf einmal verarbeitet werden

    import { newHttpBatchRpcSession } from "capnweb"  
    let batch = newHttpBatchRpcSession("https://example.com/api";)  
    let result = await batch.hello("World")  
    console.log(result)  
    
  • Innerhalb eines einzelnen Batches können mehrere Aufrufe gleichzeitig ausgeführt und die Ergebnisse parallel empfangen werden

    let promise1 = batch.hello("Alice")  
    let promise2 = batch.hello("Bob")  
    let [result1, result2] = await Promise.all([promise1, promise2])  
    

Promise Pipelining (verkettete Aufrufe)

  • Unterstützt ein Modell, bei dem das Ergebnis eines vorherigen Aufrufs direkt als Argument für den nächsten Aufruf verwendet wird, ohne auf die vorherige Antwort zu warten

  • Beispiel: Das Ergebnis-Promise von getMyName() wird direkt an hello() übergeben und mit nur einem Netzwerk-Roundtrip verarbeitet

    let namePromise = batch.getMyName()  
    let result = await batch.hello(namePromise)  
    
  • Die Promises von Cap'n Web verhalten sich wie Proxy-Objekte, sodass zusätzliche Methodenaufrufe ohne Verzögerung verkettet werden können

    let sessionPromise = batch.authenticate(apiKey)  
    let name = await sessionPromise.whoami()  
    

Sicherheit: Authentifizierung und Object Capability

  • Über die Methode authenticate wird bei Erfolg ein Berechtigungsobjekt (Session) vergeben; danach können Funktionen ohne zusätzliche Authentifizierungsschritte aufgerufen werden
  • Anders als bei klassischen RPCs kann ein Session-Objekt nicht gefälscht werden, und auf Methoden mit Berechtigungsanforderungen kann ohne Authentifizierung nicht zugegriffen werden
  • Strukturelle Einschränkungen von WebSocket werden auf natürliche Weise überwunden, während die Konsistenz der Authentifizierungslogik gewahrt bleibt
  • Wenn API-Interfaces in TypeScript deklariert werden, lassen sie sich automatisch auf Client und Server anwenden und sorgen für Autovervollständigung sowie Typsicherheit

Vergleich mit GraphQL und das Alleinstellungsmerkmal von Cap'n Web

  • GraphQL entschärft das Waterfall-Problem von REST, erfordert aber die Einführung einer neuen Sprache, eines neuen Schemas und einer neuen Toolchain

  • Cap'n Web löst das Waterfall-Problem allein mit JavaScript-Code und

    • unterstützt Promise Pipelining und Objektreferenzen, wodurch sich verschachtelte Aufrufe oder komplexe Transaktionslogik auf natürliche Weise modellieren lassen
    let user = api.createUser({ name: "Alice" })  
    let friendRequest = await user.sendFriendRequest("Bob")  
    
  • Ohne die Komplexität sowie Lern- und Verwaltungskosten von GraphQL lässt es sich ähnlich wie eine JavaScript-API nutzen

Array-Operationen (array.map usw.) und Optimierung

  • Mit Cap'n Web sind map-Operationen auf jedem Element eines Arrays ohne zusätzliche Netzwerk-Roundtrips möglich

  • Die Callback-Funktion von map wird einmal auf dem Client ausgeführt, wobei der Recheninhalt aufgezeichnet wird (Record-Replay), dann an den Server gesendet und dort gesammelt verarbeitet

    let friendsWithPhotos = friendsPromise.map(friend => {  
      return {friend, photo: api.getUserPhoto(friend.id)}  
    })  
    let results = await friendsWithPhotos  
    
  • Über eine begrenzte domänenspezifische Sprache (DSL) lässt sich dies wie eine JavaScript-Funktion ausdrücken, während intern das Cap'n-Web-Protokoll zur Optimierung mehrerer Aufrufe verwendet wird

Interne Protokollstruktur und Kommunikationsablauf

  • Strukturierte Datenübertragung über JSON plus spezielle Vorverarbeitung, mit Unterstützung für spezielle Typen wie Arrays und Datumswerte
  • Als symmetrisches Protokoll ermöglicht es bidirektionale Kommunikation ohne feste Trennung zwischen Client und Server
  • Jede Partei (zum Beispiel Alice und Bob) verwaltet Export-/Import-Tabellen und unterscheidet Objekt- und Funktionsreferenzen anhand von IDs
  • Durch Push-/Pull-Nachrichten und die Vergabe von Promise-IDs können viele Aufrufe in einem einzigen Roundtrip verarbeitet werden

Status und Einsatzbeispiele

  • Cap'n Web ist noch experimentelle Open Source, wird aber bereits in realen Diensten wie den Remote Bindings von Cloudflare Wrangler eingesetzt
  • Weitere Blogposts und verschiedene Frontend-Experimente sind geplant
  • Es wird unter der MIT-Lizenz veröffentlicht und ist frei für alle nutzbar
  • Direkt zum GitHub-Repository

1 Kommentare

 
GN⁺ 2025-09-23
Hacker-News-Kommentare
  • Ich habe zwei Fragen.

    1. Mich interessiert, wie man App-Deployments handhaben sollte, bei denen sich die RPC-Semantik aktualisiert. Anders gesagt: Wie kann man sicherstellen, dass Client und Server dieselbe RPC-Version verwenden? Protocol Buffers (grpc/avro usw.) versuchen, dieses Problem direkt zu lösen.
    2. Mich interessiert, wie man mit instabilen Netzwerkverbindungen umgehen sollte. Da die Export-/Import-Tabellen direkt an eine zustandsbehaftete WebSocket-Verbindung gebunden sind, würde bei einem Verbindungsabbruch vermutlich auch der Zustand verloren gehen. Theoretisch könnten Client/Server den Zustand cachen und beim Reconnect wiederherstellen, aber da die Tabellen Closures enthalten können, stelle ich mir die Serialisierung schwierig vor und vermute außerdem Speicherprobleme. Mich würde interessieren, wie das Team darüber nachgedacht hat.
      Ich halte das für wirklich innovative Arbeit.
      1. Am besten betrachtet man es ähnlich wie das Aktualisieren einer JavaScript-API, ohne bestehende Aufrufer kaputtzumachen. Solange man die grundlegenden Kompatibilitätsregeln beachtet, die auch für lokale Funktionsaufrufe gelten, kann man neue Methoden, optionale Argumente usw. hinzufügen.
      2. Wenn die Verbindung abbricht, muss man sich erneut verbinden und die Objekte von Grund auf neu aufbauen. In einer echten React-App übergibt man dem Top-Level-Component den Haupt-RPC-Stub als Argument. Dieses Component erzeugt mehrere Unterobjekte und reicht sie an seine Kinder weiter. Wenn die Verbindung abbricht, erstellt man einen neuen Stub und übergibt ihn erneut an das Top-Level-Component. Dann wird wie bei jeder anderen State-Änderung ein Re-Render ausgelöst, und alle Kinder holen sich die benötigten Unterobjekte erneut.
        Falls es ein Subscription-Objekt mit Callback gibt, sollte man die API so entwerfen, dass beim Start der „zuletzt gesehene Nachricht“-Stand angegeben werden kann. Dann kann der Datenstrom direkt nahtlos fortgesetzt werden, ohne dass zwischendurch etwas verloren geht.
        Ich sollte dazu wohl mal eine Blogpost-Serie über solche Designmuster schreiben.
  • Der Abschnitt darüber, wie das Array-Problem gelöst wurde, ist wirklich interessant und gleichzeitig ein wenig beängstigend: Blog-Link
    Im Fall von .map() wird nicht direkt JavaScript-Code an den Server geschickt, aber doch etwas Code-Ähnliches, und zwar mithilfe einer begrenzten domänenspezifischen Sprache (DSL). Auf Client-Seite wird der Callback einmal mit Platzhalterwerten ausgeführt, sein Verhalten per Record-Replay nachverfolgt und dann ein Instruction-Set an den Server geschickt. Dort werden diese Instruktionen empfangen und für jedes Array-Mitglied ausgeführt.
    Der Entwickler verwendet also einfach normale JS-Methoden, tatsächlich wird das Ganze aber per Trick in eine enge DSL umgewandelt. Callbacks dürfen nur synchron arbeiten, await ist nicht möglich. Stattdessen ist nur Promise-Pipelining erlaubt, sodass der gesamte Ablauf erfasst und an den Server übergeben werden kann, wo er bei Bedarf erneut ausgeführt wird.

    • In C# gibt es dafür Expression Trees. Entity Framework nutzt sie, um Lambdas entgegenzunehmen und in SQL-Abfragen umzuwandeln. Man kann den Code also verwenden, indem man ihn scannt oder transformiert, ohne ihn tatsächlich auszuführen.
      Zum Beispiel ist bei db.People.Where(p => p.Name == "Joe") Where keine Funktion, die wirklich ein Predicate entgegennimmt, sondern einen Ausdruck. Der übergebene Code wird also gescannt, es wird geprüft, ob das Feld Name mit "Joe" übereinstimmt, und daraus wird eine SQL-WHERE-Klausel erzeugt.
      JavaScript hat keinen solchen Mechanismus, daher wird es emuliert, indem Platzhalterwerte eingefügt und dann Schritt für Schritt aufgezeichnet wird, wie sich der Code verhält.

    • Beim Erstellen der Query-DSL von Tanstack DB wurde dieser Record-Replay-Trick kürzlich ebenfalls verwendet: Guide-Link. Den where-/select-/join-Callbacks werden RefProxy-Objekte übergeben, und es wird verfolgt, welche Properties/Operationen auf diesen Objekten stattfinden.
      Da sich in JS allgemeine Operatoren (==, > usw.) nicht direkt abfangen lassen, erstellt man kleine, nachvollziehbare Funktionen wie eq/gt/not, führt den Callback nur einmal aus, fängt den verknüpften Ausdruck ab und baut daraus eine IR.
      Erstaunlicherweise konnte sogar der JS-Spread-Operator nachverfolgt werden.
      Kenton, könntest du dieses Konzept vielleicht auch in Cap'n Web aufnehmen und Fake-Operatoren (eq, gt, in usw.) hinzufügen, um Remote-Tracing zu ermöglichen?

    • Bedingungen scheinen verboten zu sein (fast wie bei den Regeln für React Hooks). Mich würde interessieren, wie solche Einschränkungen umgesetzt werden.

  • Dieses Projekt ist faszinierend.
    Es hat Ähnlichkeiten mit ML-Compiler-Bibliotheken (TensorFlow 1, JAX jit, PyTorch compile usw.). Per Tracing wird ein Operationsgraph aufgebaut, der dann kompiliert oder für eine VM transformiert und ausgeführt wird.
    Derzeit dienen dynamische Sprachen als Frontend, um keine neue DSL definieren zu müssen; stattdessen werden AST-Transformationen in bestehende Skriptsprachen eingebettet.
    In ML wird die Ausführung von GPU-/Linalg-Kernels verzögert, um Kernel zusammenzufassen; bei einem RPC wie Cap'n Web kann man Netzwerk-Requests verzögern, um mehrere Network-Calls zusammenzufassen.
    Im Kern geht es darum, Instruction Plane und Data Plane zu trennen, und selbst eine einzelne CPU in sehr kleinem Maßstab besitzt eine Distributed-System-Struktur (Trennung von Befehls- und Datencache).
    In Cap'n Web übernimmt der RPC-Graph selbst die Rolle der Instruktionen.
    Dieses Muster ist wirklich spannend, aber es fühlt sich auch so an, als würde sich die Stack-Struktur (Compiler über Interpreter, Interpreter über Compiler ...) endlos wiederholen. Es wirkt wie eine weitere Variante des lispy-Musters „code is data, data is code“. Ich habe das Gefühl, dass dahinter eine noch grundlegendere Geschichte steckt.

    • Stimme völlig zu — die Perspektive, das als universelle Abstraktion zu sehen, ist großartig.
      Dynamische Sprachen werden jetzt zum Frontend neuer DSLs, und statt neue Syntax festzulegen, bettet man die AST-Erzeugung direkt in Skripte ein.
      Ich denke, TypeScript ist hier ein Gamechanger. Man bekommt sowohl die Laufzeitflexibilität von JavaScript (wie Cap'n Web mit clever eingesetzten Proxys) als auch Typsicherheit.
      In letzter Zeit bin ich von diesem Konzept im ORM-Bereich regelrecht besessen. Die meisten ORMs arbeiten seriell und eager, sodass man sie erst direkt vor der Query-Ausführung manipulieren kann.
      Ein wirklich composable ORM müsste meiner Meinung nach wie ein Compiler arbeiten: Man definiert in TypeScript eine vollständig typsichere DSL über SQL, baut daraus eine Query-AST und kompiliert erst ganz am Ende zu SQL.
      Typegres, an dem ich gerade arbeite, verfolgt genau diese Idee. Wenn dich dieses Muster interessiert, könnte es einen Blick wert sein.
  • Das Kernproblem von RPC-Bibliotheken ist, dass sie zu verbergen versuchen, wo und wie Round-Trips stattfinden.
    Schon bei .map() auf Arrays in Cap'n Web ist schwer zu erkennen, wo genau tatsächlich ein Network-Round-Trip auftritt.
    Ich halte das nicht für ein „Feature“, sondern eher für einen „Bug“ — wenn man den Code liest, sollte man sofort verstehen können, wie er sich verhält; das zu verschleiern ist nicht wünschenswert.
    Referenz-Link

    • Der Round-Trip findet statt, wenn await verwendet wird.
      Promise-Pipelining ermöglicht es, mehrere Statements ohne await nacheinander aufzusetzen, daher gibt es dazwischen keine zusätzlichen Network-Round-Trips. Ein einziges await am Ende ist alles.
  • Wenn man schon mit gRPC und dem Web gearbeitet hat, weiß man, wie schmerzhaft es ist, Protobuf fürs Web nutzbar zu machen.
    Die Einfachheit von Cap'n Web gefällt mir wirklich gut: Cap'n-Proto-Dokumentation
    Anders als Cap'n Proto hat Cap'n Web überhaupt kein Schema. Es gibt fast keinen unnötigen Boilerplate-Code, weshalb es sich stark nach nativer JavaScript-RPC in Cloudflare Workers anfühlt.
    GitHub-Referenz

  • Ich habe entdeckt, dass kentonv eine neue Bibliothek veröffentlicht hat, und bin sofort hergekommen.
    Als ich mir den Code auf GitHub ansah, war ich überrascht, wie klein der Umfang tatsächlich ist. Ich frage mich, ob das wirklich alles ist.
    Theoretisch scheint es auch nicht allzu schwer zu sein, die Server-Seite in andere Sprachen zu portieren; ich hätte Lust, es mit einem Elixir-Server und einem JS/TS-Frontend auszuprobieren.
    Es wäre auch interessant, so ein Sprach-Porting einem LLM zu überlassen. Mich würde interessieren, ob in diesem Repo LLM-basierter Code steckt. Ich habe vor ein paar Monaten irgendwo gesehen, dass kentonv ein von KI erzeugtes (von Menschen überprüftes) POC gebaut hatte.

    • Für einige Tests wurde von LLMs erzeugter Code verwendet, aber für die Bibliothek selbst überhaupt nicht.
      Ich glaube nicht, dass ein LLM diese Bibliothek zum jetzigen Zeitpunkt hätte entwickeln können. Die interne Struktur ist wie ein sehr fein verzahntes Puzzle aufgebaut.
      Tatsächlich hat die Designarbeit mehr Zeit gekostet als der eigentliche Code.
      Das ist etwas völlig anderes als die Bibliothek workers-oauth-provider, die eine bekannte Spezifikation auf neuartige Weise implementiert.
      Die Code-Struktur dürfte sich relativ leicht in dynamische Sprachen wie Python portieren lassen, in statisch typisierte Sprachen aber eher nicht. Es gibt viele Stellen, die von beliebigen Objekttypen abhängen.
  • Es gibt Ähnlichkeiten mit OCapN, aber auch wichtige Unterschiede: Referenz
    Beide unterstützen Capability-Transfer, Promise-Pipelining und ein schemaloses Modell.
    Cap'n Web hat keine Out-of-Band-Capabilities wie sturdyref in OCapN (wiederherstellbare URIs). Deshalb vermute ich, dass API-Key-Authentifizierung notwendig ist. Ein sturdyref ist eine Art nicht erratbares Token; wer es besitzt, erhält Zugriffsrechte auf den entsprechenden Endpunkt.
    Außerdem hat Cap'n Web keine Drei-Parteien-Übergabe, bei der Alice Bob bei Carol einführt. Für verteilte Apps ist das essenziell, daher wirkt Cap'n Web eher wie ein traditioneller SaaS-artiger Client-Server-Einsatz mit einigen ocap-Eigenschaften.

    • Ich würde 3PH gern später hinzufügen, aber für diesen ersten Release hatte der Fokus auf Browser<->Webserver-Kommunikation Vorrang.
      Bei SturdyRef hängt die Wiederherstellung je nach Plattform unterschiedlich ab, daher halte ich es für sinnvoller, das plattformspezifisch umzusetzen als auf Ebene des RPC-Protokolls.
      In Cloudflare Workers wird es zum Beispiel bald möglich sein, Capabilities im Durable-Object-Storage persistent zu speichern, aber die Umsetzung ist stark workers-spezifisch.
      Auch Sandstorm kennt persistente Capabilities, allerdings nur für interne Dienste.
      Deshalb wurde das Konzept persistenter Capabilities aus Cap’n Proto ganz entfernt, und im Web-Standard kommt OAuth dem noch am nächsten.
      Man könnte sich zwar einen sturdyref auf Basis von OAuth-Refresh-Tokens vorstellen, aber das wäre nichts, was sich plattformübergreifend nutzen ließe.
  • Nach einem schnellen Blick scheint dieses System zu verlangen (oder zumindest zu fördern), dass Import-/Export-Tabellen oder Objektzustand serverseitig zustandsbehaftet gespeichert werden.
    Bei traditionellem RPC kommen alle Aufrufe auf der obersten Ebene an, und jeder Aufruf übergibt Schlüssel usw., sodass es kein Problem ist, Requests auf mehrere Server zu verteilen — bei Cap’n Web scheint das anders zu sein.
    Ich frage mich, ob man die Tabellen serialisieren und in einer Datenbank speichern kann, um die gleiche Art von Server-Verteilung zu ermöglichen, oder ob Server-Affinity bzw. Strukturen wie Durable Objects zwingend erforderlich sind.

    • Zustand wird nur innerhalb einer einzigen RPC-Session erhalten.
      Bei Verwendung von WebSocket bleibt der Zustand so lange erhalten, wie die WebSocket-Verbindung besteht.
      Wenn HTTP-Batch-Übertragung verwendet wird, ist die Session auf die Dauer eines einzelnen HTTP-Requests begrenzt, und alle Aufrufe darin werden auf einmal verarbeitet.
      Cap'n Web muss also keinen Zustand über mehrere HTTP-Requests oder Verbindungen hinweg aufrechterhalten.
      Man sollte allerdings Designs vermeiden, bei denen beim Abbruch einer Session alle Capabilities verloren gehen. Es muss jederzeit möglich sein, nach einem Reset der Verbindung die Capabilities wiederherzustellen.

    • Aus der Dokumentation lese ich heraus, dass die Affinity über WebSockets hergestellt wird.
      HTTP-Batching bedeutet, dass alle Requests auf einmal gesendet werden und man dann auf die Antworten wartet.
      Dadurch wird Load-Balancing schwieriger. Wenn es viele Chat-Clients gibt, könnten sich die Verbindungen auf bestimmte Server konzentrieren. Dann besteht die Gefahr, dass diese Server überlastet werden.
      Auch Scale-in/Scale-out des Servers wird komplizierter. Wenn lang laufende Verbindungen bestehen und gleichzeitig mehrere Requests parallel verarbeitet werden, wird die Verwaltung sehr schwierig.
      Noch ein Punkt: Wenn Clients dauerhaft nur Push-Events senden und nie Antworten entgegennehmen, muss der Server diese Antworten ständig im Speicher behalten; dadurch wären DDOS-Angriffe meiner Meinung nach leicht möglich.

    • Soweit ich mich an die Cap'n-Proto-Dokumentation erinnere, können Server und Clients sich gegenseitig Peer-Stubs übergeben.
      Wenn Server C über Client B einen Stub erhält, der auf A erzeugt wurde, kann C A auch direkt aufrufen.

  • RPC is often accused of committing many of the fallacies of distributed computing.

But this reputation is outdated. When RPC was first invented some 40 years ago, async programming barely existed. We did not have Promises, much less async and await. Dieser Teil hat mich verwirrt. Wenn die zentrale Annahme von RPC stark von einer bestimmten Sprache oder einem bestimmten Nebenläufigkeitsmodell abhängt, wie kann es dann ein Protokoll sein?

  • „RPC“ ist ursprünglich ein Programmierparadigma, bei dem Remote-Aufrufe so wirken sollen, als seien sie nicht von internen Funktionsaufrufen zu unterscheiden.
    In der Praxis braucht man dafür natürlich ein Wire-Protokoll, Client-/Server-Bibliotheken usw.
    Inzwischen hat sich die Sicht stark verändert, und Strukturen mit Funktionssignaturen ähnlich wie REST-Endpunkte sind verbreitet.
    Durch Sprachfeatures wie Future, Optional usw. lassen sich Eigenschaften wie „dieser Vorgang kann verzögert sein“ oder „dieser Vorgang kann fehlschlagen“ klar kennzeichnen.
    Früher wurden all diese Eigenschaften bei RPC verborgen.

  • Ich frage mich, was genau damit gemeint ist. Asynchrone Programmierung gibt es in vielen Sprachen. Ich habe sie in JavaScript, C++, Python, Rust, C# und fast allen anderen verwendet.
    Der Punkt ist wohl, dass frühe RPC-Systeme den aufrufenden Thread blockierten, während die Netzwerk-Anfrage lief — und das war wirklich ein schlechtes Design, weshalb Asynchronität heute selbstverständlich geworden ist.

  • Ich freue mich sehr darüber, dass Cap'n Web nicht nur an Cloudflare-Produkte gebunden ist, sondern auch eigenständig existiert.
    Beim Lesen dieses Abschnitts in der Dokumentation kam mir eine Frage:

    as of this writing, the feature set is not exactly the same between the two. We aim to fix this over time, by adding missing features to both sides until they match. Wenn beide Seiten Feature-Parität erreichen, habt ihr dann vor, sie auch danach weiter synchron zu halten? Oder wird Cap'n Web am Ende doch hinter Cloudflare Workers zurückbleiben? Mich interessiert auch, wie groß dieser Abstand wäre.

    • Wir planen, die beiden Produkte bei allen gemeinsam sinnvoll nutzbaren Features praktisch weiterhin synchron zu halten.
      Ich glaube sogar, dass Cap'n Web Worker-RPC überholen könnte (tatsächlich ist es bei den Pipelining-Funktionen bereits voraus).
      Die Struktur von Cap'n Web ist deutlich einfacher, daher werden neue Features wahrscheinlich sogar zuerst in Cap'n Web ausprobiert.