A playground for integrating Yjs with a Go webserver using go-plugin.
This is mostly a project for fun and not meant to be equivalent to y-websocket-server, but it'd be nice if there was some parity there.
I use Go and didn't really want to run a separate Node server, especially with authorization and persistence in mind.
I tried to use yffi and cgo, but it both didn't work and using cgo was pretty unsavory as well. So I decided to try out go-plugin and see if I could just let a Node subprocess handle all the Yjs operations.
Also, fun fact, I couldn't find anything else on GitHub that used go-plugin to dispatch to Node/JavaScript so this may be a good example for that in general.
Unfortunately over the course of the project I realized that I would still have to figure out horizontal scaling, which made me reconsider why this is a useful project at all. However if I continue to use Yjs in the future, it'd be pretty nice to have Go at least be able to read/update documents even if it didn't own the websocket connection.
- Node >=22.6.0
- Go >=1.23.0
- (development only)
protoc>= 29.3
proto- protobuf definitions for go-plugin and Yjs operationsyjs-go-plugin- a Node go-plugin that manages Yjs documents (in memory)react-rich-collab- a clone of a lexical demo, only edits done were to point to the local Go server
- Run
cd yjs-go-plugin && npm install && cd ..to set up the plugin dependencies - Run
go run main.goto start the webserver - In a new tab, run
cd react-rich-collab && npm install && npm run devto start the frontend - Visit http://localhost:5173 in your browser and start editing
The Go websocket server in this project dispatches incoming websocket messages from Yjs clients to a Node subprocess using gRPC.
The basic flow works like:
- Go server starts. go-plugin starts an instance of Node immidiately
- A new client connects to Go
- Go sends a SyncV1 message to Node over gRPC, gets back a response for the client to init
- Go starts a timer and regularly sends the client the entire document as an encoded update
- All future websocket messages from the client gets sent/brokered to Node via gRPC
To make this "feature complete", ignoring horizontal scaling, you'd want to:
- Ensure awareness updates are handled correctly. Disconnecting clients feel a bit buggy.
- Don't send updates on a timer
- Ensure sync steps are correct, not sending sync step 2 is probably bad
- Handle auth, persistence (only task that seems easy here, hah)
After editing proto/yjs.proto, run these commands:
protoc --go_out=. --go-grpc_out=. proto/yjs.proto
cd yjs-go-plugin && npm run protoWhich will generate Go and Typescript definitions, respectively.