# 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 ``` 1. Browser connects to `/ws/doc/{docId}` with JWT in query parameter 2. `collab-go` validates the JWT token 3. `collab-go` calls backend gRPC `DocumentService.GetDocumentSettings()` to verify document exists 4. On success, upgrades HTTP to WebSocket 5. Dials `collab-core` at `{COLLAB_CORE_URL}/doc-{docId}` 6. 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: ```bash # 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@latest` - `protoc-gen-go-grpc`: `go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest` ### Go Go 1.24 or later. ## Build & Run ```bash # Build go build -o collab-go main.go # Run ./collab-go ``` Or directly: ```bash 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 Unauthorized` if JWT is invalid - Returns `404 Not Found` if document doesn't exist - Returns `503 Service Unavailable` if backend gRPC is unreachable ### Health Probes ``` GET /health GET /readyz GET /livez ``` All return JSON: ```json { "status": "ok", "detail": "", "backend": "ok" } ``` - `/health`: Always returns `200 OK`. Includes backend status if available. - `/readyz`: Returns `503 Service Unavailable` during shutdown draining or if backend is unhealthy. - `/livez`: Always returns `200 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/** - `Authenticator` validates JWT tokens - Supports HS256, HS384, HS512 signing methods - Falls back to unverified parsing when secret is empty **config/** - Uses `caarlos0/env/v11` for environment variable parsing - All configuration via environment variables **handler/** - `WSHandler` implements `http.Handler` for 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/** - `Proxy` manages WebSocket connection to `collab-core` - `ProxyWS` runs two goroutines for bidirectional message forwarding - Connection path: `/doc-{docId}` on `collab-core` ## Graceful Shutdown On receiving `SIGINT` or `SIGTERM`: 1. Sets health checker to draining mode (readiness returns `not_ready`) 2. Stops accepting new connections 3. Waits up to 15 seconds for existing connections to close 4. Exits Load balancers should stop routing traffic when `/readyz` returns non-200. ## Docker ### Development ```bash 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 ```bash # 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/` source - `proto/` for protobuf generation - Generates proto stubs inside the container Multi-stage build: 1. Builder stage: generates proto stubs, compiles binary 2. Runner stage: copies binary to minimal Alpine image ## Integration with Collab Stack `collab-go` is part of a two-service collaboration stack: 1. **collab-core** (Node.js): Maintains Yjs document state in memory + Redis 2. **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: ```bash go vet ./... go build ./... ``` ## Related Documentation - Parent project: `../AGENTS.md` - Full system docs: `../../docs/README.md` - Chinese version: `../../docs/README_zh.md`