Hub — Multi-User Server¶
The hub provides a cloud-hosted alternative to the local mograder student workflow. Each student gets a persistent notebook directory and on-demand marimo edit sessions, all accessible through a web browser.
Quick Start¶
# Set the secret (required, unless --dev)
export MOGRADER_HUB_SECRET=$(python -c "import secrets; print(secrets.token_hex(32))")
# Start the hub
mograder hub -C /path/to/course --port 8080 --headless
# Or in development mode (no auth required)
mograder hub -C /path/to/course --dev --headless
Student Workflow¶
Students access the hub via a browser (no local install required):
Assignments¶
- Navigate to the hub URL provided by your instructor
- Log in via university SSO (automatic via reverse proxy)
- Click Download to fetch an assignment
- Click Edit to open the marimo editor in a new tab
- Work on the assignment — changes are saved automatically
- Click Validate to run checks and see results
- Click Export to download the completed
.pyfile - Upload the exported file to Moodle for submission
- Click Reset to restore the original release version (your work is archived)
Active edit sessions appear in an Active editors panel with links to reopen and a Stop button.
Note
The hub does not submit to Moodle directly. Students must export their notebook and upload to Moodle for grading.
Lectures¶
If the instructor has published lectures to the hub, they appear in a Lectures table below the assignments:
- Click Run to open the lecture in a new tab
- The lecture runs in read-only mode with code visible (
marimo run --include-code) - Each student gets their own isolated session (widget state is not shared)
- Cross-notebook links within lectures navigate directly to other published lectures
Lecture sessions are per-user and subject to the same idle timeout as assignment edit sessions.
Deep Links¶
Both assignments and lectures support shareable deep links that can be embedded in lectures, Moodle pages, or shared directly:
| Type | Deep link URL | What it does |
|---|---|---|
| Assignment | /edit/{assignment} |
Auto-downloads release (if needed), starts editor |
| Lecture | /run/{lecture} |
Starts read-only run session |
Deep links show a spinner page while the session starts, then redirect to the per-user editor or viewer. If the student already has a copy of the assignment, their edits are preserved — the release is not re-downloaded.
Try it on the live demo: edit demo-assignment | run demo-lecture
These can be used in:
- Lecture notebooks —
mograder generate --lectureautomatically rewrites inter-notebook links to/run/{lecture}/format - Moodle — paste the deep link as an activity URL or in assignment instructions
- Direct sharing — students can bookmark or share the link
If a session is already running for the current user, the deep link reconnects to it rather than starting a new one.
Commands¶
mograder hub¶
Start the hub server. Options:
| Option | Default | Description |
|---|---|---|
-C, --course-dir |
. |
Course directory |
--port |
8080 |
Server port |
--host |
0.0.0.0 |
Bind address |
--notebooks-dir |
from config | Student notebooks directory |
--session-ttl |
3600 |
Session idle timeout (seconds) |
--trusted-header |
X-Remote-User |
Trusted proxy header name |
--dev |
off | Dev mode (no auth required) |
--headless |
off | Don't open browser on startup |
mograder hub check¶
Preflight check to verify hub requirements:
Checks:
- MOGRADER_HUB_SECRET is set
- marimo is available
- bwrap (bubblewrap) is available (optional)
- Hub port is free
- Directories are configured
mograder hub publish¶
Publish a release assignment or lecture to the hub:
# Publish assignment (verifies files match Moodle first)
mograder hub publish A1 --url $HUB_URL --token $TOKEN
# Skip Moodle verification
mograder hub publish A1 --url $HUB_URL --token $TOKEN --force
# Preview what will be published
mograder hub publish A1 --force --dry-run
# Explicit Moodle assignment name (if different from directory name)
mograder hub publish A1 --moodle-assignment "A1. Introduction" --url $HUB_URL --token $TOKEN
# Publish a lecture (auto-detected from mograder-type metadata, or use --lecture)
mograder hub publish L01-Intro --url $HUB_URL --token $TOKEN
mograder hub publish L01-Intro --lecture --url $HUB_URL --token $TOKEN
The ASSIGNMENT argument is a name (e.g. A1-Intro-to-SciML or prefix A1) resolved from the release/ directory, or an explicit directory path.
For assignments, files are verified against Moodle before publishing by default — Moodle is the authoritative source for assignment content.
For lectures, Moodle verification is skipped automatically (lectures aren't posted to Moodle). The lecture type is auto-detected from mograder-type = "lecture" in the notebook's PEP 723 block (injected by mograder generate --lecture), or can be forced with --lecture.
Publishing a lecture:
1. Uploads the notebook and auxiliary files to the hub
2. Stores "type": "lecture" in the files.json manifest
3. Warms the uv cache / creates a shared .venv from PEP 723 dependencies
| Option | Env var | Description |
|---|---|---|
--url |
MOGRADER_HUB_URL |
Hub base URL |
--token |
MOGRADER_HUB_INSTRUCTOR_TOKEN |
Instructor token |
--moodle-assignment |
Moodle assignment name (default: same as ASSIGNMENT) | |
--force |
Skip Moodle verification | |
--lecture |
Publish as lecture (implies --force) |
|
--dry-run |
Preview only, don't publish |
mograder hub warm-cache¶
Pre-populate the uv cache with notebook dependencies:
# Warm cache for specific notebooks
mograder hub warm-cache release/hw1/hw1.py release/hw2/hw2.py
# Warm cache for all release notebooks
mograder hub warm-cache --all
# Dry run (show deps without installing)
mograder hub warm-cache --dry-run release/hw1/hw1.py
# Remote mode: warm cache on the hub server
mograder hub warm-cache --url $HUB_URL --token $TOKEN
This parses PEP 723 inline script metadata (# /// script blocks) to find
dependencies and runs uv run --with <deps> python -c pass to populate the cache.
With --url, sends a POST to the hub's /warm-cache endpoint instead of running locally.
mograder hub generate-token¶
Generate HMAC authentication tokens:
# Generate a student token
mograder hub generate-token alice
# Generate an instructor token
mograder hub generate-token --role instructor admin
Requires MOGRADER_HUB_SECRET to be set.
Configuration¶
Add a [hub] section to mograder.toml:
[hub]
port = 8080
notebooks_dir = "hub-notebooks"
release_dir = "hub-release"
session_ttl = 3600
trusted_header = "X-Remote-User"
uv_cache_dir = "" # empty = default ~/.cache/uv
Instructor Workflow¶
Assignments¶
- Generate release notebooks:
mograder generate A1 - Upload to Moodle:
mograder moodle upload A1 - Publish to hub:
mograder hub publish A1 --url $HUB_URL --token $TOKEN
Lectures¶
- Generate lecture release:
mograder generate --lecture source/L01-Intro/L01-Intro.py - Publish to hub:
mograder hub publish L01-Intro --url $HUB_URL --token $TOKEN
The lecture type is auto-detected from PEP 723 metadata — no --lecture flag needed on publish if generate --lecture was used. Cache warming happens automatically during publish.
Environment Variables¶
| Variable | Description |
|---|---|
MOGRADER_HUB_SECRET |
HMAC secret for session/token signing (required unless --dev) |
MOGRADER_HUB_URL |
Hub base URL (for publish and warm-cache commands) |
MOGRADER_HUB_INSTRUCTOR_TOKEN |
Instructor token (for publish and warm-cache commands) |
API Endpoints¶
Deep Links (shareable)¶
| Method | Path | Description |
|---|---|---|
| GET | /edit/{assignment} |
Spinner → auto-download + start edit session |
| GET | /run/{lecture} |
Spinner → start run session |
Assignments (per-user)¶
| Method | Path | Description |
|---|---|---|
| POST | /start-edit-deep/{assignment} |
Auto-download release + start edit (used by spinner) |
| POST | /start-edit/{user}/{assignment} |
Start edit session (notebook must exist) |
| POST | /stop-edit/{user}/{assignment} |
Stop edit session |
| POST | /upload/{user}/{assignment} |
Upload a notebook |
| GET | /export/{user}/{assignment} |
Download a notebook |
| POST | /validate/{user}/{assignment} |
Run validation checks |
| POST | /reset/{user}/{assignment} |
Reset to release version |
| GET | /status/{user}/{assignment} |
Get assignment status |
| POST | /mark-exported/{user}/{assignment} |
Mark as exported |
* |
/edit/user/{user}/{assignment}/... |
Proxy to marimo editor |
Lectures (per-user)¶
| Method | Path | Description |
|---|---|---|
| POST | /start-run/{lecture} |
Start per-user marimo run session |
* |
/run/user/{user}/{lecture}/... |
Proxy to marimo run session |
Shared¶
| Method | Path | Description |
|---|---|---|
| GET | /assignments |
List assignments and lectures (with type field) |
| GET | /sessions |
List active sessions (with type field) |
| GET | /release/{name}/{filename} |
Download release file |
| POST | /publish/{name} |
Publish release (instructor); ?type=lecture for lectures |
| POST | /warm-cache |
Warm uv cache (instructor) |
Authentication¶
The hub supports multiple authentication methods (checked in order):
- Session cookies — Set after first successful authentication
- Trusted proxy header —
X-Remote-Userfrom trusted proxy IPs (e.g., university SSO) - Bearer tokens — HMAC-SHA256 tokens for API access
- Dev mode — No authentication (for local development)
Security¶
- Path traversal hardening — All file paths are validated against base directories
- AST safety scanner — Uploaded code is checked for dangerous patterns (denied imports, builtins)
- Bubblewrap (optional) — Filesystem isolation for marimo edit sessions
- Session isolation — Each student can only access their own sessions
- Instructor bypass — Instructors can access any student's resources