6 poin oleh GN⁺ 2025-09-23 | 1 komentar | Bagikan ke WhatsApp
  • Cap'n Web adalah protokol RPC baru yang diimplementasikan dengan TypeScript, dioptimalkan untuk lingkungan web dan berjalan di berbagai runtime JavaScript
  • Tanpa skema atau boilerplate yang merepotkan, ia menyediakan serialisasi berbasis JSON serta format data yang mudah dibaca manusia
  • Melalui model berbasis object-capability, dimungkinkan pemanggilan dua arah, pengiriman referensi fungsi·objek, promise pipelining, dan implementasi pola keamanan
  • Mendukung berbagai lingkungan jaringan seperti WebSocket, HTTP, postMessage, serta merupakan open source ringan berukuran di bawah 10kB
  • Selain menyelesaikan masalah waterfall yang mirip dengan GraphQL, ia juga memungkinkan pemodelan RPC yang alami seperti API JavaScript biasa

Apa itu Cap'n Web

  • Cap'n Web adalah sistem RPC open source berbasis TypeScript yang dikembangkan oleh Cloudflare
  • Terinspirasi oleh Cap'n Proto, tetapi bekerja tanpa definisi skema terpisah dan mengadopsi metode serialisasi yang ramah manusia dengan memanfaatkan JSON
  • Terintegrasi dengan TypeScript untuk meningkatkan pengalaman developer seperti autocomplete dan type checking, sementara validasi tipe saat runtime dapat ditangani secara terpisah (misalnya dengan type guard)
  • Mendukung protokol jaringan seperti HTTP, WebSocket, dan postMessage, serta berjalan di browser utama, Cloudflare Workers, Node.js, dan lainnya
  • Dengan struktur ringan tanpa dependensi, ukurannya kurang dari 10kB setelah minify + gzip

Model berbasis object-capability (OCap) di Cap'n Web

  • Mengadopsi model berbasis object-capability, sehingga mampu mengekspresikan lebih banyak hal dibanding sistem RPC tradisional
    • Pemanggilan dua arah: klien dan server dapat saling memanggil fungsi
    • Pengiriman referensi fungsi·objek: ketika fungsi atau objek dikirim lewat RPC, pihak lawan menerima stub dan eksekusi terjadi di sisi asal saat dipanggil
    • Promise Pipelining: saat beberapa RPC dirangkai dalam chain, semuanya dapat diproses dalam satu round trip jaringan
    • Pola keamanan: kontrol keamanan seperti otorisasi dan manajemen sesi dapat diimplementasikan secara alami

Cara penggunaan dasar

  • Contoh klien

    import { newWebSocketRpcSession } from "capnweb"  
    let api = newWebSocketRpcSession("wss://example.com/api")  
    let result = await api.hello("World")  
    console.log(result)  
    
  • Contoh server (berbasis Cloudflare Worker)

    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})  
      }  
    }  
    
  • Menambahkan method ke API, mengirim fungsi callback dari klien, serta mendefinisikan dan menerapkan interface TypeScript dapat dilakukan dengan mudah

Apa itu RPC dan karakteristiknya di Cap'n Web

  • RPC (Remote Procedure Call) adalah konsep yang memungkinkan dua program di jaringan berkomunikasi seolah-olah sedang melakukan pemanggilan fungsi
  • Berbeda dari protokol HTTP/REST tradisional, RPC menggunakan abstraksi pemanggilan fungsi sehingga memungkinkan penulisan kode yang selaras dengan cara berpikir developer
  • Cap'n Web sangat cocok dengan alur JavaScript modern, termasuk dukungan async/await, Promise, dan Exception
  • Berbeda dari kontroversi historis RPC (pemanggilan sinkron, error jaringan), di lingkungan JS modern ia bisa digunakan dengan lebih aman dan efisien

Skenario penggunaan Cap'n Web

  • Cocok untuk semua lingkungan yang membutuhkan komunikasi jaringan antar dua aplikasi JavaScript
    • Seperti klien-server, pemanggilan antar-microservice, dan lain-lain
    • Sangat cocok khususnya untuk web app kolaborasi real-time dan interaksi yang melintasi batas keamanan yang kompleks
  • Masih berada pada tahap eksperimental, sehingga lebih bermanfaat bagi developer yang terbuka pada adopsi teknologi terbaru

Berbagai fitur

Mode batch HTTP

  • Saat koneksi persisten tidak diperlukan, beberapa pemanggilan RPC dapat digabung dan diproses sekaligus dengan mode batch HTTP

    import { newHttpBatchRpcSession } from "capnweb"  
    let batch = newHttpBatchRpcSession("https://example.com/api";)  
    let result = await batch.hello("World")  
    console.log(result)  
    
  • Dalam satu batch, beberapa pemanggilan dapat dijalankan bersamaan dan hasilnya diterima secara paralel

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

Promise Pipelining (pemanggilan berantai)

  • Mendukung cara menggunakan hasil panggilan sebelumnya langsung sebagai argumen untuk panggilan berikutnya tanpa menunggu hasil sebelumnya selesai

  • Contoh) Promise hasil getMyName() langsung diberikan ke hello() sehingga diproses dalam satu round trip jaringan

    let namePromise = batch.getMyName()  
    let result = await batch.hello(namePromise)  
    
  • Promise di Cap'n Web bekerja sebagai objek proxy, sehingga pemanggilan method tambahan dapat dirangkai tanpa penundaan

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

Keamanan: autentikasi dan object-capability

  • Melalui method authenticate, objek hak akses (sesi) diberikan saat berhasil, dan setelah itu fitur dapat dipanggil tanpa tahap autentikasi tambahan
  • Berbeda dari RPC tradisional, objek sesi tidak dapat dipalsukan, dan method yang membutuhkan hak akses tidak bisa diakses tanpa autentikasi
  • Secara alami mengatasi keterbatasan struktural WebSocket dan menjaga konsistensi logika autentikasi
  • Saat mendeklarasikan interface API dengan TypeScript, penerapannya bisa otomatis ke sisi klien~server, sambil memperoleh autocomplete dan type safety

Perbandingan dengan GraphQL dan pembeda Cap'n Web

  • GraphQL meredakan masalah waterfall bertingkat pada REST, tetapi memerlukan pengenalan bahasa, skema, dan toolchain baru

  • Cap'n Web menyelesaikan masalah waterfall hanya dengan kode JavaScript,

    • Dengan dukungan promise pipelining/referensi objek, pemanggilan bertingkat maupun logika transaksi kompleks dapat dimodelkan secara alami
    let user = api.createUser({ name: "Alice" })  
    let friendRequest = await user.sendFriendRequest("Bob")  
    
  • Dapat digunakan mirip API JavaScript tanpa kompleksitas serta biaya belajar·pengelolaan ala GraphQL

Operasi array (array.map dan sebagainya) serta optimisasi

  • Di Cap'n Web, operasi map pada setiap elemen array dapat dilakukan tanpa round trip jaringan tambahan

  • Fungsi callback map dijalankan sekali di klien untuk merekam isi operasinya (record-replay), lalu dikirim ke server agar diproses secara massal di sisi server

    let friendsWithPhotos = friendsPromise.map(friend => {  
      return {friend, photo: api.getUserPhoto(friend.id)}  
    })  
    let results = await friendsWithPhotos  
    
  • Melalui bahasa spesifik domain (DSL) yang terbatas, ekspresinya tetap seperti fungsi JavaScript, tetapi di balik layar Cap'n Web mengoptimalkan banyak pemanggilan lewat protokolnya

Struktur protokol internal dan alur komunikasi

  • Mengirim data terstruktur melalui JSON + prapemrosesan khusus, dengan dukungan tipe khusus seperti array dan tanggal
  • Sebagai protokol simetris, ia memungkinkan komunikasi dua arah tanpa pembedaan klien·server
  • Setiap pihak (misalnya Alice dan Bob) mengelola tabel export/import dan membedakan referensi objek·fungsi dengan ID
  • Melalui pesan push/pull dan alokasi Promise ID, banyak pemanggilan dapat direfleksikan dalam satu round trip

Status saat ini dan contoh penerapan

  • Cap'n Web masih merupakan open source eksperimental, tetapi sudah digunakan dalam layanan nyata seperti remote bindings di Cloudflare Wrangler
  • Direncanakan akan ada posting blog tambahan dan berbagai eksperimen frontend
  • Dirilis dengan lisensi MIT, sehingga siapa pun dapat menggunakannya dengan bebas
  • Langsung ke repositori GitHub

1 komentar

 
GN⁺ 2025-09-23
Komentar Hacker News
  • Ada dua hal yang saya penasaran

    1. Saya penasaran bagaimana sebaiknya deployment aplikasi yang memperbarui semantik RPC dilakukan. Maksudnya, bagaimana menjamin bahwa klien dan server menggunakan versi RPC yang sama. Protocol Buffers (grpc/avro, dll.) mencoba menyelesaikan masalah ini secara langsung
    2. Saya penasaran bagaimana cara terbaik menangani koneksi jaringan yang tidak stabil. Tabel export/import tampaknya terikat langsung ke koneksi WebSocket yang stateful, jadi saya kira saat koneksi terputus, state juga akan hilang. Secara teori, klien/server bisa saja meng-cache state dan memulihkannya saat reconnect, tetapi karena tabel dapat berisi closure, serialisasinya akan sulit dan bisa menimbulkan masalah memori. Saya penasaran bagaimana tim memikirkan hal ini
      Menurut saya ini pekerjaan yang benar-benar inovatif
      1. Ini bisa dilihat mirip seperti memperbarui JavaScript API tanpa merusak pemanggil yang sudah ada. Selama mengikuti prinsip kompatibilitas yang sama seperti pada pemanggilan fungsi lokal, menambahkan method baru/argumen opsional dan semacamnya tidak masalah
      2. Jika koneksi terputus, Anda harus menyambung lagi lalu membangun ulang objek dari awal. Pada aplikasi React nyata, stub RPC utama diberikan ke komponen tingkat atas sebagai argumen. Komponen ini membuat beberapa objek turunan dan meneruskannya ke anak-anaknya. Jika koneksi terputus, buat stub baru lalu berikan lagi ke komponen tingkat atas. Setelah itu akan terjadi render ulang seperti state change lainnya, dan semua anak akan mengambil ulang objek turunan yang mereka butuhkan
        Jika ada objek subscription yang memiliki callback, API perlu dirancang agar saat memulai bisa menentukan “pesan terakhir yang sudah dilihat”. Dengan begitu data bisa langsung dilanjutkan tanpa ada yang terlewat di tengah jalan
        Sepertinya saya perlu merangkum seri design pattern seperti ini dalam sebuah blog post
  • Bagian tentang bagaimana mereka menyelesaikan masalah array itu sangat menarik sekaligus agak menakutkan tautan blog
    Dalam kasus .map(), mereka memang tidak langsung mengirim kode JavaScript ke server, tetapi mengirim sesuatu yang mirip “kode”, menggunakan domain-specific language (DSL) yang terbatas. Di sisi klien, callback dijalankan sekali dengan menyisipkan nilai placeholder, lalu perilakunya dilacak dengan cara record-replay sehingga instruction set bisa dikirim ke server. Di server, instruction itu diterima lalu dijalankan untuk setiap anggota array.
    Jadi pengembang hanya menulis method js biasa, tetapi di balik layar diterapkan trik yang mengubahnya menjadi DSL yang sempit. Callback hanya boleh berjalan secara sinkron, await tidak dimungkinkan. Sebagai gantinya hanya promise pipelining yang diizinkan, sehingga seluruh proses bisa ditangkap lalu dikirim ke server, dan di server dijalankan ulang sesuai kebutuhan

    • C# punya expression tree untuk menangani masalah seperti ini. Entity Framework memanfaatkannya saat menerima lambda expression lalu mengubahnya menjadi SQL query. Jadi kode bisa digunakan dengan cara dipindai atau ditransformasikan tanpa dieksekusi
      Misalnya, db.People.Where(p => p.Name == "Joe") bukan berarti Where menerima fungsi predicate sungguhan, melainkan expression, sehingga kode yang diterima bisa dipindai untuk memeriksa bahwa field Name cocok dengan "Joe" lalu diubah menjadi klausa SQL WHERE
      JavaScript tidak punya mekanisme seperti ini, jadi pendekatannya meniru dengan menyisipkan nilai placeholder lalu merekam satu per satu bagaimana perilakunya

    • Baru-baru ini kami juga memakai trik record-replay ini saat membuat query DSL untuk Tanstack DB tautan panduan. Objek RefProxy diberikan ke callback where/select/join, lalu dilacak prop/operasi apa yang terjadi pada objek itu.
      Karena di js operator biasa (==, >, dll.) tidak bisa dicegat secara langsung, kami membuat fungsi-fungsi kecil yang bisa dilacak seperti eq/gt/not, menjalankan callback sekali untuk menangkap expression yang terhubung, lalu membuat IR darinya
      Menariknya, operator spread js juga berhasil kami lacak
      Kenton, saya penasaran apakah konsep ini juga bisa ditambahkan ke capnweb dalam bentuk operator palsu (eq, gt, in, dll.) untuk memberi kemampuan remote tracing

    • Sepertinya percabangan kondisi dilarang (mirip seperti aturan hook di React), jadi saya penasaran bagaimana pembatasan seperti itu diimplementasikan

  • Proyek ini menarik
    Ada sisi yang mirip dengan pustaka kompiler ML (TensorFlow 1, JAX jit, PyTorch compile, dll.). Dengan pendekatan tracing, ia membuat operation graph, lalu meng-compile atau mentransformasikannya agar berjalan sesuai VM
    Saat ini, alih-alih mendefinisikan DSL baru dengan bahasa dinamis sebagai frontend, AST transformation disembunyikan di dalam bahasa scripting yang sudah ada
    Di ML, eksekusi GPU/linalg kernel ditunda agar kernel bisa digabungkan, sedangkan pada RPC seperti Cap'n Web, network request dapat ditunda agar beberapa network call bisa digabung
    Pada akhirnya kuncinya adalah memisahkan instruction plane dan data plane, dan bahkan CPU tunggal dalam skala sangat kecil pun memiliki struktur distributed system (pemisahan cache perintah/data)
    Di Cap'n Web, graph RPC itu sendiri berperan sebagai instruction
    Pola seperti ini sangat menarik, tetapi juga terasa seperti struktur stack (compiler di atas interpreter, interpreter di atas compiler...) yang berulang tanpa akhir. Rasanya seperti versi lain dari pola Lispy code is data, data is code. Sepertinya ada cerita yang secara fundamental lebih dalam di sini

    • Sangat setuju—sudut pandang yang melihat ini sebagai abstraksi universal itu keren
      Bahasa dinamis sekarang menjadi frontend untuk DSL baru, tetapi tanpa menetapkan sintaks baru; pembuatan AST justru dilebur ke dalam script
      Menurut saya TypeScript adalah game changer di sini. Karena kita bisa sekaligus mendapatkan fleksibilitas runtime JavaScript (seperti Cap'n Web yang memanfaatkan Proxy dengan cerdik) dan type safety
      Belakangan ini saya sangat tertarik pada konsep ini di dunia ORM. Kebanyakan ORM bersifat serial dan eager, jadi hanya bisa dimanipulasi tepat sebelum query dieksekusi
      Menurut saya ORM yang benar-benar composable harus bekerja seperti compiler: mendefinisikan DSL yang sepenuhnya type-safe di atas SQL dengan TypeScript untuk membuat query AST, lalu baru meng-compile ke SQL di tahap akhir
      Typegres yang sedang saya kembangkan juga persis memakai ide ini. Jika pola ini menarik bagi Anda, mungkin layak dilihat
  • Masalah inti pustaka RPC adalah kecenderungannya menyembunyikan di mana dan bagaimana round-trip terjadi
    Hanya dengan melihat .map() array di Cap'n Web pun sulit mengetahui di mana network round-trip sebenarnya terjadi.
    Menurut saya ini bukan “fitur”, melainkan “bug”—saat membaca kode, kita seharusnya bisa langsung memahami perilakunya, dan menyamarkan hal ini tidaklah ideal
    tautan referensi

    • round-trip terjadi saat memakai await
      promise pipelining memungkinkan beberapa statement disiapkan berurutan tanpa await, sehingga tidak ada tambahan network round-trip di tengah. Ketika akhirnya await dipanggil sekali, itulah keseluruhannya
  • Jika pernah memakai gRPC dan web, Anda pasti tahu betapa menyakitkannya menerapkan Protobuf ke web
    Saya sangat menyukai kesederhanaan Cap'n Web dokumentasi capnproto
    Berbeda dari Cap'n Proto, Cap'n Web sama sekali tidak punya schema. Karena hampir tidak ada boilerplate yang tidak perlu, rasanya sangat seperti RPC native JavaScript untuk Cloudflare Workers
    referensi github

  • Saya langsung datang begitu menemukan pustaka baru dari kentonv
    Setelah melihat kode di GitHub, saya kaget karena ukurannya ternyata sangat kecil. Saya penasaran apakah memang hanya segitu semuanya
    Secara teori, sepertinya porting sisi server ke bahasa lain juga tidak akan terlalu sulit, dan saya jadi ingin memakainya dengan server Elixir dan frontend JS/TS
    Menyuruh LLM melakukan porting bahasa seperti ini juga terdengar menarik. Saya penasaran apakah ada kode berbasis LLM yang masuk ke repo ini. Beberapa bulan lalu saya sempat melihat kentonv bercerita tentang POC buatan AI (yang ditinjau manusia)

    • Sebagian test memang dibuat oleh LLM, tetapi pustaka utamanya sama sekali tidak
      Pada titik saat ini, sepertinya akan sulit bagi LLM untuk membuat pustaka ini. Struktur internalnya dirancang seperti puzzle yang sangat presisi dan saling terkait
      Waktu yang dihabiskan untuk memikirkan desain lebih banyak daripada waktu untuk menulis kode sebenarnya
      Ini sangat berbeda dari pustaka workers-oauth-provider yang mengimplementasikan well-known spec dengan cara baru
      Struktur kodenya mungkin cukup mudah dipindahkan ke bahasa dinamis seperti Python, tetapi menurut saya akan sulit untuk bahasa bertipe statis. Ada banyak bagian yang bergantung pada tipe objek arbitrer
  • Ada kemiripan dan juga perbedaan penting dengan OCapN referensi
    Keduanya mendukung capability transfer, promise pipelining, dan model schemaless
    Cap'n Web tidak memiliki capability out-of-band seperti sturdyref (URI yang bisa dipulihkan) milik OCapN. Karena itu saya menduga autentikasi dengan API key menjadi perlu. sturdyref adalah semacam token yang tidak bisa ditebak; jika memilikinya, Anda mendapatkan hak akses ke endpoint tersebut
    Selain itu, Cap'n Web tidak memiliki kemampuan handoff tiga pihak di mana Alice memperkenalkan Bob kepada Carol. Ini penting untuk aplikasi terdistribusi, jadi Cap'n Web terasa lebih dekat ke layanan bergaya client-server SaaS tradisional yang hanya mengambil sebagian karakteristik ocap

    • Saya memang ingin menambahkan dukungan 3PH nanti, tetapi untuk rilis awal ini prioritas yang lebih penting adalah fokus pada komunikasi browser<->web server
      Untuk SturdyRef, cara pemulihannya berbeda-beda di tiap platform, jadi menurut saya lebih tepat diimplementasikan sesuai platform masing-masing daripada di level protokol RPC
      Misalnya di Cloudflare Workers, capability persistence dari Durable Object storage akan segera dimungkinkan, tetapi cara implementasinya spesifik pada platform worker
      Sandstorm juga punya persistent capability, tetapi terbatas pada layanan internal
      Karena itu konsep persistent capability sengaja dihilangkan dari Cap’n Proto, dan konsep yang paling mirip dalam standar web adalah OAuth
      Kita bisa membayangkan definisi sturdyref berbasis OAuth refresh token, tetapi itu bukan struktur yang bisa dipakai di semua platform
  • Dari tinjauan cepat saya, sistem ini tampaknya mengharuskan (atau mendorong) penyimpanan stateful atas tabel import/export atau state objek di sisi server
    Dalam RPC tradisional, semua pemanggilan masuk ke level teratas dan setiap pemanggilan membawa key dan sebagainya, sehingga tetap tidak masalah walau request tersebar ke banyak server, tetapi Cap’n Web tidak seperti itu
    Saya penasaran apakah tabel tersebut bisa diserialisasi lalu disimpan ke DB sehingga distribusi server tetap dimungkinkan dengan cara yang sama, atau apakah ia benar-benar membutuhkan server affinity atau struktur seperti Durable Objects

    • State hanya dipertahankan di dalam satu sesi RPC
      Jika menggunakan WebSocket, state akan tetap hidup selama koneksi WebSocket itu hidup
      Jika menggunakan pengiriman batch HTTP, sesi dibatasi pada keseluruhan satu request HTTP, dan semua pemanggilan di dalamnya diproses sekaligus
      Jadi Cap’n Web tidak perlu mempertahankan state lintas banyak request/koneksi HTTP
      Namun, jika desain Anda membuat semua capability hilang ketika sesi terputus di tengah jalan, maka desain seperti itu sebaiknya dihindari. Capability harus bisa dipulihkan kapan saja setelah koneksi di-reset

    • Setelah membaca dokumentasinya, sepertinya strukturnya mengandalkan affinity melalui websocket
      HTTP batching berarti semua request dikirim sekaligus lalu menunggu respons
      Pendekatan seperti ini membuat load balancing menjadi rumit. Jika klien chat sangat banyak, koneksi bisa menumpuk ke server tertentu. Kalau begitu ada risiko server tersebut kelebihan beban
      Scale in/out server juga jadi merepotkan. Mempertahankan koneksi jangka panjang sambil banyak request diproses bersamaan membuat pengelolaannya sangat sulit
      Satu hal lagi, jika klien terus mengirim push event tanpa pernah menerima respons, server harus terus menyimpan respons itu di memori, jadi menurut saya serangan DDOS akan mudah dilakukan

    • Dari dokumentasi Cap'n Proto yang pernah saya baca dulu, server dan klien bisa saling bertukar peer stub
      Jika server C menerima stub yang dibuat di A melalui klien B, maka C pun bisa langsung memanggil A

  • 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. Bagian ini membuat saya bingung. Jika asumsi inti RPC sangat bergantung pada bahasa tertentu atau model konkurensi tertentu, bagaimana ini bisa disebut protokol?

  • “RPC” pada dasarnya adalah paradigma pemrograman yang membuat pemanggilan jarak jauh tampak tidak bisa dibedakan dari pemanggilan fungsi internal
    Tentu saja, untuk itu dibutuhkan wire protocol, pustaka klien/server, dan sebagainya
    Belakangan ini pemahamannya banyak berubah, dan struktur yang mirip REST endpoint tetapi memiliki function signature menjadi arus utama
    Dengan adanya fitur bahasa pemrograman seperti Future, Optional, dan lain-lain, karakteristik seperti “operasi ini bisa tertunda” atau “bisa gagal” dapat dibedakan dengan jelas
    Pada RPC lama, semua sifat itu disembunyikan

  • Saya penasaran apa maksudnya. Pemrograman asinkron ada di banyak bahasa. Saya punya pengalaman memakai JavaScript, C++, Python, Rust, C#, dan hampir semuanya
    Intinya, sistem RPC awal bekerja dengan memblokir thread pemanggil selama request jaringan berlangsung, dan itu benar-benar desain yang buruk; itulah sebabnya sekarang pendekatan asinkron menjadi hal yang wajar

  • Saya sangat antusias karena Cap'n Web ada sebagai proyek terpisah dan tidak hanya terikat pada produk Cloudflare
    Setelah membaca bagian ini di dokumentasi, saya punya pertanyaan

    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. Jika kedua sisi nantinya mencapai feature parity, apakah Anda berniat terus menjaga sinkronisasinya ke depan, atau Cap'n Web pada akhirnya akan tertinggal dari cloudflare workers, dan seberapa besar jarak itu?

    • Rencananya, untuk fitur-fitur yang memang bermakna bagi keduanya, kami akan terus menjaga sinkronisasi hampir sepenuhnya
      Bahkan saya pikir Cap'n Web bisa melampaui worker RPC (faktanya kemampuan pipeline sudah lebih maju)
      Struktur Cap'n Web jauh lebih sederhana, jadi eksperimen fitur baru juga kemungkinan besar akan lebih dulu dilakukan di Cap'n Web