collab-go
WebSocket gateway for real-time collaborative document editing. Authenticates JWT tokens and proxies collaboration traffic to collab-core.
Overview
collab-go is a Go service that acts as the entry point for browser WebSocket connections in the Yoresee Doc collaborative editing platform. It handles:
- JWT token validation (HS256/384/512)
- Document existence verification via gRPC to backend
- Bidirectional WebSocket proxy to
collab-core(Node.js Yjs service) - Health checks and graceful shutdown
Connection Flow
Browser --ws://host/ws/doc/{docId}?token={jwt}--> collab-go --> collab-core
- Browser connects to
/ws/doc/{docId}with JWT in query parameter collab-govalidates the JWT tokencollab-gocalls backend gRPCDocumentService.GetDocumentSettings()to verify document exists- On success, upgrades HTTP to WebSocket
- Dials
collab-coreat{COLLAB_CORE_URL}/doc-{docId} - Proxies messages bidirectionally between browser and
collab-core
Prerequisites
Proto Stubs
pkg/gen/ contains generated protobuf code and is gitignored. You must generate it before building:
# Recommended: use parent repo script
bash ../deploy/script/gen_proto.sh
# Or manual protoc
mkdir -p pkg/gen
protoc -I ../proto \
--go_out=pkg/gen --go_opt=paths=source_relative \
--go-grpc_out=pkg/gen --go-grpc_opt=paths=source_relative \
../proto/yoresee_doc/v1/yoresee_doc.proto
Required tools:
protoc-gen-go:go install google.golang.org/protobuf/cmd/protoc-gen-go@latestprotoc-gen-go-grpc:go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
Go
Go 1.24 or later.
Build & Run
# Build
go build -o collab-go main.go
# Run
./collab-go
Or directly:
go run main.go
Default port: 1234
Configuration
Environment variables:
| Variable | Default | Required | Description |
|---|---|---|---|
ADDR |
:1234 |
No | Listen address |
JWT_SECRET |
(empty) | Yes | JWT signing secret. Empty = accept any parseable JWT (dev mode) |
COLLAB_CORE_URL |
ws://collab-core:1234 |
No | WebSocket URL for collab-core |
BACKEND_GRPC_ADDR |
backend:9090 |
No | gRPC address for backend service |
INTERNAL_RPC_KEY |
(empty) | No | Sent as x-internal-key gRPC metadata for internal auth |
JWT Modes
Production mode (with JWT_SECRET):
- Validates JWT signature using HS256/384/512
- Token must be valid and properly signed
Development mode (empty JWT_SECRET):
- Parses JWT but does not verify signature
- Accepts any structurally valid JWT
- Useful for local development without shared secrets
API Endpoints
WebSocket
GET /ws/doc/{docId}?token={jwt}
- Upgrades to WebSocket on success
- Returns
401 Unauthorizedif JWT is invalid - Returns
404 Not Foundif document doesn't exist - Returns
503 Service Unavailableif backend gRPC is unreachable
Health Probes
GET /health
GET /readyz
GET /livez
All return JSON:
{
"status": "ok",
"detail": "",
"backend": "ok"
}
/health: Always returns200 OK. Includes backend status if available./readyz: Returns503 Service Unavailableduring shutdown draining or if backend is unhealthy./livez: Always returns200 OK. Used for liveness checks.
Architecture
collab-go/
├── auth/ JWT validation
├── config/ Environment-based configuration
├── handler/ WebSocket handler, auth + doc check + proxy
├── health/ Health probe handlers
├── proxy/ Bidirectional WebSocket proxy
├── pkg/gen/ Generated protobuf stubs (gitignored)
└── main.go Entry point
Key Components
auth/
Authenticatorvalidates JWT tokens- Supports HS256, HS384, HS512 signing methods
- Falls back to unverified parsing when secret is empty
config/
- Uses
caarlos0/env/v11for environment variable parsing - All configuration via environment variables
handler/
WSHandlerimplementshttp.Handlerfor WebSocket connections- Validates JWT from
?token=query parameter - Calls backend gRPC to verify document exists
- Upgrades connection and delegates to proxy
health/
- Implements Kubernetes-style health probes
- Readiness checks backend gRPC health
- Supports graceful shutdown draining
proxy/
Proxymanages WebSocket connection tocollab-coreProxyWSruns two goroutines for bidirectional message forwarding- Connection path:
/doc-{docId}oncollab-core
Graceful Shutdown
On receiving SIGINT or SIGTERM:
- Sets health checker to draining mode (readiness returns
not_ready) - Stops accepting new connections
- Waits up to 15 seconds for existing connections to close
- Exits
Load balancers should stop routing traffic when /readyz returns non-200.
Docker
Development
docker build -t collab-go-dev -f Dockerfile .
docker run -p 1234:1234 --env-file .env collab-go-dev
Runs go run main.go (no build cache).
Production
# Build from parent directory
docker build -t collab-go -f Dockerfile.prod ..
Note: Dockerfile.prod expects build context at parent directory (..) because it copies:
collab-go/sourceproto/for protobuf generation- Generates proto stubs inside the container
Multi-stage build:
- Builder stage: generates proto stubs, compiles binary
- Runner stage: copies binary to minimal Alpine image
Integration with Collab Stack
collab-go is part of a two-service collaboration stack:
- collab-core (Node.js): Maintains Yjs document state in memory + Redis
- collab-go (Go): WebSocket gateway, auth, proxy
The gateway pattern separates:
- Authentication and authorization (Go, fast)
- Document state management (Node.js, Yjs runtime)
- Horizontal scaling (multiple gateway instances, single core per doc)
Logging
All logs use prefix collab-gateway:
collab-gateway listening on :1234
collab-gateway unauthorized path=/ws/doc/abc remote=127.0.0.1:12345 err=...
collab-gateway doc check failed docID=abc err=...
Testing
No tests currently exist. Verify with:
go vet ./...
go build ./...
Related Documentation
- Parent project:
../AGENTS.md - Full system docs:
../../docs/README.md - Chinese version:
../../docs/README_zh.md