# Collaboration By default with Docs, collaboration is enabled. To allow the collaboration between users, a connection to a websocket server is made (the yhub service), you only have to configure the Django backend URL and the allowed origin in your yhub service: ```yaml COLLABORATION_BACKEND_BASE_URL: https://{yourdocsdomain.tld} COLLABORATION_SERVER_ORIGIN: https://{yourdocsdomain.tld} ``` The collaboration server keeps the live state of a document in Redis and persists it to a PostgreSQL database of its own, so it needs both: ```yaml REDIS: redis://{redis-host}:6379/0 POSTGRES: postgres://{user}:{password}@{postgres-host}:5432/yhub ``` Nothing creates that schema at startup: the server never runs DDL. Run the script yhub ships (`npm run init-db`, which the helm chart runs as a job) once before starting it, and again after every upgrade that adds a table. It creates the database when it is missing, it is idempotent, and until it has run every document read fails with `relation "..." does not exist`. The Django backend reads and writes document content there too, so point it at the service: ```yaml YHUB_API_BASE_URL: http://{yhub-service}:443 ``` Prefer the internal service url: the routes the backend calls are not meant to be reachable from the outside. Route `/collaboration/ws/` to the service publicly — that is the one the browsers open — plus the document routes (`/collaboration/ydoc/`, `rollback`, `prune`, `changeset`, `activity`) and `/collaboration/jwks/`, which carries public keys and nothing else. Keep `reset-connections`, `migrate`, `restore-ydoc`, `reset-ydoc` and `create-ydoc` in-cluster. Both directions are authenticated with short-lived RS256 JWTs rather than a shared secret, and each side verifies the other against the JWKS it publishes — so both need a signing key of their own, and neither needs a copy of the other's: ```yaml # Django JWT_PRIVATE_KEY_FILE: /path/to/backend-private.pem # yhub YHUB_JWT_PRIVATE_KEY_FILE: /path/to/yhub-private.pem ``` They are ordinary PKCS#8 RSA keys (`openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048`), and rolling one needs no change on the other side. Without them the documents still open and edit, but the backend cannot create, delete or restore a document's content, and yhub cannot tell it that a document changed — its `updated_at` stops following the edits. ### Generating them on the cluster The helm chart generates both for you, so that no key has to be created by hand, put in a values file or in a secret: ```yaml jwtKeys: enabled: true ``` A job then creates the two keys, once, in a secret every service mounts read-only, and points the backend and yhub at them. It generates them with `openssl` in a pod-local volume and hands them to `kubectl create secret`, so they never touch a disk, a manifest or a values file. The secret is left alone when it is already there, so the job is safe to re-run — it runs on every sync — and rolling the keys is deleting the secret and letting the next run create it again. Both sides follow: they pick the verification key by its `kid` and fetch the set again when they meet one they do not know. The job is the only thing allowed near that secret: the chart gives it a service account whose role can `create` a secret and read whether that one exists, nothing more. The services never call the kubernetes API — they read a mounted file. The secret is not part of the release either, so uninstalling keeps the same identities; delete the secret to start over. Deployments already holding their keys in a secret of their own point the chart at it instead, and the job and its rights are not created at all: ```yaml jwtKeys: enabled: true existingSecret: my-jwt-keys # holding private.pem and yhub-private.pem ``` Setting `JWT_PRIVATE_KEY_FILE` or `YHUB_JWT_PRIVATE_KEY_FILE` yourself keeps priority over what the job provides, so a deployment holding its keys in a secret of its own can leave `jwtKeys` disabled and mount them where it wants. Several replicas can serve the same document: they exchange updates through Redis, so no sticky routing is needed on the websocket ingress. ## What happens when connection to the websocket is not allowed? When multiple users access a Docs and the connection to the websocket is not allowed, then they will be in a situation where they can lose data. They will lose data because they will erase each other modifications. You can also have a scenario with a mix of users connected to the websocket and some other not.