A JupyterLab extension that enables real-time collaboration between teachers a };
3. **Request Sync** (when ready):
```javascript
ws.send(JSON.stringify({
type: 'request_sync',
cell_id: 'cell_001'
}));
The extension now supports a simplified hash-based approach for direct teacher-to-student cell synchronization without session management.
When teacher and students are on the same network:
-
Teacher Setup:
# Start Redis server accessible from network redis-server --bind 0.0.0.0 --port 6379 # Set Redis URL (optional, defaults to localhost) export REDIS_URL="redis://0.0.0.0:6379"
-
Student Setup:
# Point to teacher's Redis server export REDIS_URL="redis://192.168.1.42:6379" # Replace with teacher's IP
POST /notebook-sync/hash/push-cell
Content-Type: application/json
{
"cell_id": "cell_001",
"created_at": "2025-01-15T10:30:00.000Z",
"content": "print('Hello, students!')",
"ttl_seconds": 86400
}
# Response:
{
"type": "push_confirmed_hash",
"cell_id": "cell_001",
"created_at": "2025-01-15T10:30:00.000Z",
"hash_key": "a1b2c3d4",
"teacher_id": "teacher123"
}POST /notebook-sync/hash/request-sync
Content-Type: application/json
{
"cell_id": "cell_001",
"created_at": "2025-01-15T10:30:00.000Z"
}
# Response:
{
"type": "cell_sync_hash",
"cell_id": "cell_001",
"content": "print('Hello, students!')",
"created_at": "2025-01-15T10:30:00.000Z",
"student_id": "student456"
}- Keys are generated using SHA256 hash of
cell_id:created_at - Same cell_id + created_at always produces the same hash key
- Enables deterministic retrieval without complex session management
- All existing session-based APIs remain functional
- Hash-based methods are additive, not replacement
- Can use both approaches simultaneouslys with request-based synchronization.
- Teacher-Student Sessions: Teachers create sessions, students join with session codes
- Request-Based Sync: Students receive notifications but only sync when they choose to
- Cell-Level Control: Teachers can toggle sync permissions per cell
- Redis Pub-Sub: Scalable architecture using Redis for real-time messaging
- Persistent Updates: Updates stored in Redis until students request them
- Backend: Tornado WebSocket handlers with Redis pub-sub
- Session Management: In-memory connection tracking with Redis persistence
- Notifications: Students get notified of available updates, not automatic syncs
- Security: Role-based permissions and input validation
Start Redis using Docker:
# From the project root directory
docker-compose up -dOr install Redis manually:
# On macOS
brew install redis
brew services start redis
# On Ubuntu
sudo apt update
sudo apt install redis-server
sudo systemctl start redis# Clone the repository
git clone https://github.com/your-username/jupyter-notebook-sync.git
cd jupyter-notebook-sync
# Install in development mode
pip install -e .
# Enable the server extension
jupyter server extension enable jupyter_notebook_sync
# Verify installation
jupyter server extension listjupyter lab --autoreload-
Create Session:
// Frontend JavaScript (connect to WebSocket) const ws = new WebSocket('ws://localhost:8888/notebook-sync/ws'); // Create session ws.send(JSON.stringify({ type: 'create_session' }));
-
Push Cell Updates:
ws.send(JSON.stringify({ type: 'push_cell', cell_id: 'cell_001', content: { source: 'print("Hello, students!")' }, metadata: { sync_allowed: true } }));
-
Toggle Sync Permissions:
ws.send(JSON.stringify({ type: 'toggle_sync', cell_id: 'cell_001', sync_allowed: false }));
-
Join Session:
ws.send(JSON.stringify({ type: 'join_session', session_code: 'ABC123' }));
-
Listen for Notifications:
ws.onmessage = (event) => { const data = JSON.parse(event.data); if (data.type === 'update_available') { // Show UI indicator that update is available showUpdateNotification(data.cell_id, data.timestamp); } };
-
Request Sync (when student chooses):
ws.send(JSON.stringify({ type: 'request_sync', cell_id: 'cell_001' }));
create_session: Create new sessionpush_cell: Push cell content (stores in Redis, notifies students)toggle_sync: Enable/disable sync for specific cellsend_session: End the session
join_session: Join existing session with coderequest_sync: Request specific cell content
session_created: Session creation confirmationsession_joined: Session join confirmationupdate_available: Notification of available update (NOT content)cell_content_update: Actual cell content (only after request)sync_allowed_update: Sync permission changeserror: Error messages
Set Redis URL in environment variables:
export REDIS_URL="redis://localhost:6379"The extension can be configured via Jupyter configuration:
# jupyter_lab_config.py
c.NotebookSyncExtensionApp.redis_url = "redis://localhost:6379"# Install in development mode
pip install -e .
# Install development dependencies
pip install -e ".[test]"
# Run tests
pytest# Run all tests
pytest
# Run with coverage
pytest --cov=jupyter_notebook_sync
# Run specific test
pytest tests/test_handlers.py::test_websocket_connectionjupyter_notebook_sync/
├── __init__.py # Extension entry point
├── handlers.py # WebSocket and HTTP handlers
├── session_manager.py # Session and connection management
└── redis_client.py # Redis client and operations
- Origin Checking: Implement proper origin validation for production
- Authentication: Integrate with JupyterLab's authentication system
- Input Validation: All message content is validated and sanitized
- Rate Limiting: Consider adding rate limiting for WebSocket messages
- Redis Security: Use Redis AUTH and TLS in production
# Check Redis status
redis-cli ping
# Check Redis logs
docker logs jupyter-sync-redis# Check if extension is enabled
jupyter server extension list
# Check JupyterLab logs
jupyter lab --debug- Check if extension is properly registered
- Verify Redis is running
- Check browser console for errors
- Verify WebSocket URL in client code
GET /notebook-sync/status: Extension status and health check
ws://localhost:8888/notebook-sync/ws: Main WebSocket connection
session:{session_code}: Session metadatapending_update:{session_code}:{cell_id}: Stored cell updates- Channel:
sync_session_{session_code}: Pub-sub notifications
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Submit a pull request
BSD 3-Clause License