Real-time collaborative editing for Redmine using Yjs CRDTs and Hocuspocus WebSocket server.
This plugin addresses a long-standing feature request for real-time collaborative editing in Redmine, as discussed in Feature #10568. Multiple users can now edit wiki pages, issue descriptions, and notes simultaneously with live synchronization—similar to Google Docs.
- Real-time sync with conflict-free merging (CRDT)
- User presence indicators with colored cursors
- Works with CKEditor and plain text editors
- Offline support with auto-sync on reconnect
- Ephemeral collaboration (Redmine stores final documents)
| Redmine Version | Plugin Version | Status |
|---|---|---|
| 6.0.x | 1.0.0+ | ✅ Supported (tested) |
| 6.1.x | 1.0.0+ | |
| 5.1.x | 1.0.0+ | |
| 5.0.x | 1.0.0+ | |
| 4.x and earlier | - | ❌ Not supported |
The plugin itself requires no additional prerequisites beyond your existing Redmine installation:
- Redmine: 5.0+ (tested on 6.0.x)
- Ruby: 3.0+ (comes with Redmine 5.x/6.x)
- Rails: 6.1+ (comes with Redmine 5.x/6.x)
Assets are pre-built and included in the repository, so Node.js is not required for installation.
To use collaborative editing, you also need:
- Hocuspocus WebSocket Server: A separate Node.js service (see Hocuspocus Deployment)
- Node.js: 18+ (only needed for running the Hocuspocus server, not for the plugin itself)
- Browser: Modern browsers with WebSocket support (Chrome, Firefox, Safari, Edge)
If you want to modify and rebuild assets:
- Node.js: 18+
- npm: Comes with Node.js
Simply checkout the plugin into your Redmine plugins directory:
cd /path/to/redmine/plugins
git clone https://github.com/your-org/redmine_yjs.gitThe plugin will automatically copy its assets on first load. Then:
# Run migrations (if any)
cd /path/to/redmine
bundle exec rake redmine:plugins:migrate RAILS_ENV=production
# Restart RedmineThat's it! The plugin is now installed and ready to use.
Important: This plugin requires a separate Hocuspocus WebSocket server. You must deploy and maintain this backend service.
Security: Use an authenticating proxy (e.g., OAuth2 Proxy) in front of both Redmine and Hocuspocus. WebSocket connections inherit the authenticated session. See auth-example/oauth2proxy/ for a complete setup.
docker run -p 8081:8081 ghcr.io/d-led/redmine_yjs-hocuspocus:latestOr in Docker Compose:
services:
hocuspocus:
image: ghcr.io/d-led/redmine_yjs-hocuspocus:latest
ports:
- "8081:8081"- Build locally:
cd redmine_yjs/hocuspocus && docker build -t hocuspocus . && docker run -p 8081:8081 hocuspocus - Standalone Node.js:
cd redmine_yjs/hocuspocus && npm install && npm start - Fly.io:
cd redmine_yjs/hocuspocus && ./fly-deploy.sh
See Hocuspocus Deployment for detailed instructions.
Set the Hocuspocus WebSocket URL:
- Administration → Plugins → Redmine Yjs → Configure
- Or via environment variable:
HOCUSPOCUS_URL=wss://your-hocuspocus.example.com
| Variable | Default | Description |
|---|---|---|
HOCUSPOCUS_URL |
Auto-detected | Browser-facing WebSocket URL (direct or proxied) |
YJS_ENABLED |
1 |
Enable/disable collaborative editing (1/0) |
YJS_TOKEN_SECRET |
unset | Shared HMAC secret for signing Hocuspocus auth tokens (recommended) |
The plugin auto-detects the Hocuspocus URL based on environment:
- Docker:
ws://localhost:3000/ws(via Traefik) - Production:
wss://hocuspocus.fly.dev - Development:
ws://localhost:8081
- Go to Administration → Plugins
- Click Configure on "Redmine Yjs Collaborative Editing"
- Set the Hocuspocus WebSocket URL (browser-facing)
- Optionally enable Proxy WebSocket through Redmine (/ws)
- Enable/disable the plugin
Two authentication layers protect collaborative editing:
- OAuth2 Proxy (optional, recommended): Authenticates users before accessing Redmine. WebSocket connections inherit the same authenticated session. See
auth-example/oauth2proxy/for setup. - YJS Token Authentication: HMAC-signed tokens verify document access. Set
YJS_TOKEN_SECRETin both Redmine and Hocuspocus.
When YJS_TOKEN_SECRET is set:
- Redmine generates short-lived, HMAC-signed tokens per user + document
- Browser passes token to Hocuspocus via
HocuspocusProvider - Hocuspocus verifies signature, expiry, and document match
Without YJS_TOKEN_SECRET, Hocuspocus falls back to development-only mode (not for production).
A minimal, reasonably secure Docker-based deployment looks like this:
-
Run Hocuspocus with a shared secret (example):
services: hocuspocus: image: ghcr.io/d-led/redmine_yjs-hocuspocus:latest ports: - "8081:8081" environment: PORT: 8081 YJS_TOKEN_SECRET: "your-32-byte-random-secret-here"
-
Configure Redmine environment (e.g. in your Redmine container or systemd service):
export HOCUSPOCUS_URL=ws://your-redmine-host:8081/ws # or proxied /ws URL export YJS_TOKEN_SECRET="your-32-byte-random-secret-here"
-
Configure the plugin in Redmine:
- Go to Administration → Plugins → Redmine Yjs → Configure.
- Set Hocuspocus WebSocket URL to match
HOCUSPOCUS_URL(browser-facing). - Optionally set Hocuspocus Token Secret (overrides
YJS_TOKEN_SECRETin Redmine). - Click Apply.
With this setup:
- Only clients that obtained a page from Redmine (and thus a signed token) can join a document’s session.
- Hocuspocus will reject connections with invalid/expired/mismatched tokens.
Once enabled, collaborative editing works automatically for:
- Issue descriptions and notes
- Wiki pages
Visual indicators:
- Fixed status badge (bottom-right corner): Colored text badge showing connection status
- Green badge: "Collaboration active" - Connected and ready
- Orange badge: "Syncing..." - Connecting to server
- Red badge: "Disconnected" - Click to reconnect (changes saved locally)
- Form widget (below editor, when focused): Shows colored circle indicator (● green, ◐ orange, ○ red) with "Collaborative Editing" label and list of other active editors
The hocuspocus/ directory contains a ready-to-deploy Hocuspocus server.
cd hocuspocus
docker build -t redmine-hocuspocus .
docker run -p 8081:8081 redmine-hocuspocusservices:
hocuspocus:
build: ./plugins/redmine_yjs/hocuspocus
ports:
- "8081:8081"
environment:
PORT: 8081cd hocuspocus
flyctl apps create my-hocuspocus --org personal
flyctl deploy --config fly.toml
# Use: wss://my-hocuspocus.fly.devcurl http://localhost:8081/health # → OKRun the setup script to ensure everything is ready:
cd plugins/redmine_yjs
./scripts/setup.shThis will:
- Check for Node.js/npm
- Install Node.js dependencies
- Build assets automatically
- Verify the setup
Option 1: Using build script (recommended)
cd plugins/redmine_yjs
./scripts/build-js.shOption 2: Using npm directly
cd plugins/redmine_yjs
npm install
npm run build:depsOption 3: Using Rake (from Redmine root)
# Build assets
bundle exec rake redmine_yjs:build_assets
# Copy assets to public directory
bundle exec rake redmine_yjs:copy_assets
# Or do both
bundle exec rake redmine_yjs:setupOption 4: Automatic (on plugin load) Assets are automatically built if missing when the plugin loads (requires Node.js).
./scripts/bump_version.sh patch # 1.0.0 -> 1.0.1 (updates files, commits, tags)
./scripts/bump_version.sh minor # 1.0.0 -> 1.1.0
./scripts/bump_version.sh major # 1.0.0 -> 2.0.0
./scripts/bump_version.sh rc # 1.0.0 -> 1.0.1-rc.0
./scripts/bump_version.sh release # 1.0.0-rc.0 -> 1.0.0
./scripts/bump_version.sh patch --dry-run # Preview changes without applyingThe script automatically:
- Updates version in
init.rb,package.json, and related files - Updates
CHANGELOG.mdif present - Updates all
package-lock.jsonfiles - Commits changes
- Creates a git tag
- Does NOT push (you push manually:
git push && git push --tags)
# From Redmine root directory
cd /path/to/redmine
# Setup test database
RAILS_ENV=test bundle exec rake db:drop db:create db:migrate redmine:plugins:migrate
# Run all plugin tests
RAILS_ENV=test bundle exec rake test TEST="plugins/redmine_yjs/test/**/*_test.rb"
# Run specific test
RAILS_ENV=test bundle exec rake test TEST=plugins/redmine_yjs/test/unit/redmine_yjs_test.rbTests concurrent editing with two browser sessions:
cd plugins/redmine_yjs/test/e2e
# Run all tests (starts Docker, runs tests, cleans up)
./scripts/run-tests.sh
# Run with visible browser
./scripts/run-tests.sh --visible
# See test/e2e/README.md for details# Start development stack
docker-compose up -d
# Open same issue in multiple browser windows
# Changes should sync in real-timeWebSocket Connection Issues
- Check Hocuspocus is running:
curl http://localhost:8081/health - Check browser console for WebSocket errors
- Verify
HOCUSPOCUS_URLmatches your deployment
Plugin Not Loading
- Check Redmine logs for errors
- Verify plugin is in
plugins/directory - Run
bundle exec rake redmine:plugins:migrate - Restart Redmine
Debugging
- Hocuspocus logs:
docker logs redmine_hocuspocus - Browser DevTools → Network → WS to check WebSocket connection status
redmine_yjs/
├── app/
│ ├── assets/ # Additional assets (if needed)
│ ├── channels/ # ActionCable channels (if used)
│ └── views/ # ERB templates for settings
│ ├── layouts/ # Layout partials
│ └── settings/ # Settings views
├── assets/
│ ├── javascripts/ # Yjs client-side code
│ └── stylesheets/ # Collaboration UI styles
├── config/
│ └── locales/ # I18n translations
├── hocuspocus/ # Hocuspocus WebSocket server
│ ├── Dockerfile
│ ├── Dockerfile.fly
│ ├── fly-deploy.sh
│ ├── server.js
│ └── package.json
├── lib/
│ └── redmine_yjs/ # Ruby modules and patches
├── scripts/
│ ├── bump_version.sh # Version bump, commit, and tag script
│ ├── run_all_tests.sh # Run all tests
│ ├── start_test_services.sh
│ └── stop_test_services.sh
├── src/
│ └── deps-entry.js # Entry point for bundled dependencies
├── test/
│ ├── unit/ # Ruby unit tests
│ ├── integration/ # Ruby integration tests
│ └── e2e/ # Playwright/Cucumber tests
├── init.rb # Plugin registration
├── package.json # Node.js dependencies
└── README.md
MIT License - See LICENSE for details.