{"slug": "colloq-a-collaborative-python-classroom-you-can-self-host", "title": "Colloq – A collaborative Python classroom you can self-host", "summary": "Colloq, an open-source self-hosted classroom tool from the colloq-edu project, lets a teacher share one link so students join a single live Python notebook from a browser with only a name, with no accounts or installs. The tool installs via `pip install colloq` and requires Python 3.9 or newer plus Docker, with Node.js 22 or newer needed outside macOS 14+ and Linux x64/arm64, and it ships under the MIT license at a pre-1.0 status. Each notebook runs its own Python kernel with a visible run queue and shared outputs, and a council mode lets students submit private answers that the teacher reviews and can show to the class with or without names.", "body_md": "**One link. One live notebook. The whole room.**\n\n  A self-hosted classroom for teaching Python, data science and ML together.\n  Students join from a browser with just a name.\n\n[Quick start](#quick-start) ·\n  [How a class runs](#how-a-class-runs) ·\n  [Documentation](https://colloq.cc/docs/en/) ·\n  [Contributing](#contributing)\n\n<sub>Share one link; the class fills the room and everyone sees where everyone is.</sub>\n\n```\npip install colloq\ncolloq start          # a class on this computer, in your browser\ncolloq start --share  # …and one link to give your students\n```\n\nInstalling needs **Python 3.9 or newer**; running a class needs **Docker**\n(macOS and Linux; on Windows, WSL 2). On macOS 14+ and Linux x64/arm64 the\npackage brings its own Node.js; elsewhere it needs **Node.js 22 or newer**. The\nserver, the web app and the room image come with the package, and your classes\nlive in `~/.colloq`.\n\n**[Documentation](https://colloq.cc/docs/en/)** ·\n[Your first class](https://colloq.cc/docs/en/getting-started.html) ·\n[Releases](https://github.com/colloq-edu/colloq/releases) ·\n[From source](#from-source)\n\nA teacher creates a class and shares its link. Students type a name, get a mark, and they are in: the same notebook, the same files and the same running Python process as everyone else. There are no student accounts, no installs and no environment setup. When someone runs a cell, everyone sees the result.\n\nColloq runs on your own machine or server, under the MIT license. It is built for the class that happens together: a live coding exercise, a lecture that opens up for questions, or individual attempts the teacher brings back to the room.\n\n- **For teachers:** presets for how the class works, rules the server enforces, a\nlecture console for an iPad and pen, and a council console for reviewing\nindividual answers.\n- **For students:** a browser is all it takes, and the same link brings you back.\n- **For operators:** a laptop for one class, your own Kubernetes or one Linux VM\nfor a term, or a rented GPU box for a deep-learning course.\n\n**Status:** pre-1.0; the version badge above carries the current one. Colloq\nruns the author's own courses. Before 1.0 a release can still change how\nsomething works, so read [CHANGELOG.md](https://github.com/colloq-edu/colloq/blob/main/CHANGELOG.md) before upgrading an\ninstance that has classes on it.\n\nEveryone edits the same cells live and sees who is typing where. Each notebook\nhas **its own Python kernel**: runs enter a visible queue, outputs reach\neveryone, and a variable one student defines is there for the next. A lecture\nand a seminar open side by side in one class therefore do not share variables,\nand they can be computing at the same time. A late arrival sees the notebook as\nit stands. This is the **Standard** preset.\n\nOpen a cell as a council and every student writes on their own sheet; only the\nteacher sees the text. The teacher's council console, a separate window, lists the\nsubmitted work and the run queue, sets a time limit for each run, marks each\nanswer correct or for revision, and shows one answer to the class, **with or\nwithout names**. Attempts share that notebook's one kernel — or, in a class whose council\ncomputes on machines, run at the same time on a pool of workers, each on a snapshot of the\nnotebook — so council is a teaching tool, not an isolated grading sandbox\n([details below](#presets-and-cell-locks)).\n\nPresent a PDF from the **Lecture console**, built for an iPad and a pen: turn\npages, write in eight colours, highlight under the ink, write with the pointer\n(a word fades only once you stop), zoom in on a detail and add a sheet of grid\nor lined paper after any slide. The projector window (`/s/:id/screen`) and\nevery student's screen follow, zoom included, and ink lands at the same spot\non each of them. Students can press **Read on my own** to page through by\nthemselves, and **To lecture** brings them back to the presenter's page.\n\nPick a preset when you create the class, then adjust the room's rules before or\nduring the class. **Permissions are enforced by the server.**\n\n| Preset | What the class can do | \n|---|---|\n| **Standard** | Everyone edits the notebook, runs cells and adds files. Oracle access follows its settings. | \n| **Lecture** | The teacher edits and runs. Students read the notebook, and the teacher can open individual cells for them to work on. | \n| **Council** | Lecture rules, but opening a cell gives every student a separate sheet. The teacher reviews the attempts and shows chosen ones to the class. | \n\nThe preset sets the starting rules; a cell's lock lets a single exercise change how the room takes part. In any preset, a cell can be:\n\n- **closed** — the room's rules decide who edits and runs it.\n- **open to shared editing** — anyone in the room can edit and run that one cell, even under Lecture rules.\n- **council** — each cell runs**Only me** (teacher-only execution, the default),**Everyone in turn** (students run directly) or**On request** (each student run needs the teacher's approval). Requests work for drafts as well as submitted answers; editing the text invalidates the request. Approval queues the requested version; it does not submit the answer. All attempts run in that notebook's**one kernel** , one after another, so students can use the data the teacher prepared. In a class whose council computes on machines ([the council on machines](https://colloq.cc/docs/en/council.html#machines) ) they run instead at the same time on a pool of workers on a class machine, each worker loaded with a snapshot of the notebook's kernel, so the teacher's cells never hold them up; an attempt that uses what the snapshot cannot carry (a database connection, an open file) goes to the one kernel, its card saying why. Each attempt gets personal copies of that data — tables, arrays, containers, torch tensors, models and their optimisers, sparse matrices, and objects of classes defined in the notebook — and afterwards the server restores the namespace exactly as it was: it removes the names the attempt defines and undoes its rebindings, so`data = data.dropna()` ,`df.drop(..., inplace=True)` ,`del df` or`globals().clear()` in one attempt does not reach the next. The same entry also protects the room from the three ways one attempt could end the class for everybody:`exit()` and`quit()` are refused (in a Jupyter kernel they shut the process down and everyone loses their variables),`os._exit()` and`os.abort()` are refused too (they end the kernel process on the spot, without even a traceback to say why), and an attempt's address space is capped (`COUNCIL_MEMORY_GUARD` ) so`np.ones((40000, 40000))` fails with`MemoryError` on its own card instead of the OOM killer taking the notebook's kernel. Process state that would otherwise change everyone's results goes back too: random seeds,`sys.stdout` ,`sys.path` ,`os.environ` ,`builtins` , warning filters, numpy and pandas options, matplotlib figures. Everything else stays shared: files on disk, modules the attempt imported, objects of types that cannot be copied and objects above the`COUNCIL_COPY_MB` budget — an attempt is told in its own output which of its variables were left shared, and which threads it left running. A review mark reaches only the answer's author, and the class sees it only on an answer the teacher shows. Individual sheets are a teaching tool, not independent execution sandboxes or an isolated grading environment.\n\n## **Room rules and teacher access**\n\nWhatever the card sets, the room can change what opening a cell does; who edits a cell's text; who runs code; after how many seconds a cell stops itself; whether dangerous commands run at all; whether code cells show line numbers; who changes the structure; who can put a document on the room's screen; who may create and edit files; whether a student may start their own notebook; whether the Oracle may act on the room's files; actions per request; questions per hour; seconds between questions; who may read the history; who may restart the kernel; and who may wipe shared work. The source of these settings is [rule-rows.ts](https://github.com/colloq-edu/colloq/blob/main/web/src/lib/rule-rows.ts).\n\nA room holds several notebooks, and each one also has an access of its own —\n**as in the room**, **personal** (its author and the teacher work in it),\n**open to everyone**, or **teacher only** — set from the **Access** menu on the\nnotebook's tab. It overrides editing, running and structure for that notebook\nalone; files, the terminal and the shared screen stay shared by the room.\nWhether a student may start their own notebook at all is a separate rule, off by\ndefault — the teacher grants the rights, not the student — and an own notebook\nis personal from the moment it is created. A personal notebook is the one kind\nthat also runs in a **separate container**: never the class's GPU, its own\nmemory cgroup, so a greedy draft cannot pull the OOM killer onto the teacher's\nkernel — and its author may restart its kernel and clear its output without\nasking.\n\nWhether the oracle answers in this room is chosen on the creation form: **As set for the instance**, **Off**, **Hints only** or **Full answers**. It is not one of those rows and cannot be changed later in the room settings. Room limits can tighten the instance settings, never loosen them.\n\nExecution and editing permissions need to agree with the exercise. If students can edit a cell, they can change the source the teacher eventually runs. If they can execute Python, they can access the room's files through Python regardless of restrictions in the Files panel.\n\nThe owner manages classes, teachers, environments and Oracle settings at\n`/admin`. Teachers sign in through personal links; students join through class\nlinks. Teacher links and the owner's setup token grant staff access and must stay\nprivate. Adding a teacher does not send an email.\n\nGuides: [room rules](https://colloq.cc/docs/en/room-rules.html) ·\n[council](https://colloq.cc/docs/en/council.html) ·\n[lectures](https://colloq.cc/docs/en/lectures.html)\n\nThe Oracle lives in the room's shared thread: the whole room sees each question and each answer, so one student's question helps the next. It reads the notebook's context, code and outputs included.\n\n- **Per class, chosen at creation:** As set for the instance, Off, Hints only, or\nFull answers.\n- **Ask** explains code, discusses an error, or, from a cell, proposes an edit that\nshows up in the cell with**Accept** and**Discard** . Whoever accepts it is\nrecorded as the author.\n- **Act** uses tools: it reads and edits files, creates notebooks, changes and\nruns cells and runs scripts, with a visible record of its steps, and only within\nthe permissions of the person who asked. Its edits apply immediately; notebook\nhistory and file snapshots are the way back. One Act request runs at a time per\nclass.\n- **Any OpenAI-compatible endpoint and model** , including local models served by\nOllama or vLLM, set in the teaching panel or the environment. With no provider\nconfigured, the notebook works and there is no Oracle. When the Oracle is enabled, selected\nnotebook and file context is sent to the configured provider.\n\n## **Act mode limits**\n\n**Act mode stops after 24 actions by default.** The owner can change **Actions per\nrequest** in `/admin/oracle`; class rules expose the same setting when creating\nor editing a class. Reading, editing and running each consume one action,\nincluding failed attempts. `0` means unlimited; a blank class field inherits the\nserver setting. A class can tighten a finite server limit. A request also stops\nafter five minutes of wall-clock work, and when the same call repeats with the\nsame arguments three times; either way the completed actions stand and the reply\nsays what was done. Manual Stop remains available. Tool use requires provider\nsupport. Changes apply to the next request.\n\nGuide: [the Oracle](https://colloq.cc/docs/en/oracle.html)\n\n| Feature | What you get | \n|---|---|\n| **Shared files and terminal** | Upload datasets, organize folders, edit text files, preview images and PDFs. The shared terminal works in the room's filesystem and records who ran each command. | \n| **Cell output** | Text, tracebacks, `pandas` tables, Markdown, images and SVG, plus interactive`plotly` figures — drawn in a sandboxed frame with no access to the class and no network of its own. Scripts from a cell's output are never executed: the output is produced by anyone allowed to run code, and it is rendered in every browser in the room. | \n| **History** | Notebook versions, plus a filtered activity journal in the teacher's History panel: attendance, Oracle requests, run outcomes, submitted answers and editing participation. See [activity history](https://github.com/colloq-edu/colloq/blob/main/docs/activity-history.md) . | \n| **Notebooks in and out** | Several notebooks per room and `.ipynb` export. Start a class from a`.ipynb` file (outputs are not imported) or from a GitHub link to a notebook or folder in a public repository. | \n| **End class, resume later** | **End class** makes the room read-only for students and keeps its notebook, files and discussion.**Resume class** brings back the previous rules. | \n| **Courses and class pages** | After class, put the room's notebooks with their outputs, the slides and the data on a class page that keeps its link: a tab per notebook, PDFs in the browser's viewer, everything as one ZIP laid out like the room. **End class** refreshes it from the saved choice. The course page is a timetable by day. The room itself, with its edit history and oracle questions, stays open from the page only to those who were in it, unless the teacher opens it to everyone.[Class pages and courses](https://colloq.cc/docs/en/publishing.html) . | \n| **Russian or English** | The owner switches the interface for the whole instance from `/admin` . Open rooms switch live, keeping code, cursors and drafts.[Language settings](https://colloq.cc/docs/en/language.html) . | \n| **Python environments** | An environment is a requirements file; each class picks one at creation. GPU environments give each room an exclusive GPU slice. | \n\nA competition is a task with a closed evaluation: you hand the class the data and\nthe description, an entrant submits an `.ipynb` notebook as a file, and the server\nexecutes it **from scratch** in a throwaway container with no network, then scores\nthe result with your metric. It lives next to courses, under **Competitions** in\nthe admin panel, and the class opens it at an address like `/k/<name>`.\n\nWhat you set up: a title and an address, a Markdown description, the open data\nfiles, the **hidden answers**, a sample notebook with a baseline solution, the\nmetric code, the execution limits and a deadline. Until the sample notebook has\ngone the whole way to a number, the competition cannot be opened: a task that does\nnot even solve for its author means a hundred people hunting for a mistake of\ntheir own that isn't there.\n\nHow a result is computed. A submission takes two steps in two separate throwaway\ncontainers: first the notebook runs and `submission.csv` is taken out of it, then\nyour `score(solution, submission)` runs on its own. The metric is computed\n**twice** — on the public share of the rows and on the private one. The public\nleaderboard is always visible; the final one opens at the deadline. What counts is\nthe submission the entrant picked; if they picked none, their best public one.\n\nWhat an entrant sees: their own submissions and only their own, the stages moving\nlive (accepted → queued → running the notebook → checking the CSV → scoring),\ntheir own traceback verbatim if the notebook failed, and their place. They read\nthe text of a `ParticipantVisibleError` raised by your metric in full — everything\nelse it may print is yours alone to see.\n\nAn entrant's identity is instance-level, not per room: a person gets an **entry\nkey** shaped `K7Q-M2X-9FD` and a link carrying it, and the same key brings them\nback to their submissions from another device. The key itself is not in the\ndatabase: what is stored is its fingerprint and a copy encrypted with the instance\nsecret. A teacher can issue a new key — the old one stops working that same\nsecond, along with every tab still open on it.\n\nThe queue is one per instance, lives in SQLite and survives a server restart. By default one submission runs at a time, the queue is fair per person (a person's second submission queues behind everyone else's first), and the teacher can pause it or kill the run in progress.\n\nGuide: [competitions](https://colloq.cc/docs/en/competitions.html)\n\nA contest is a set of algorithmic problems with tests, as on Yandex.Contest or\nCodeforces: problems A, B, C… with a statement, examples and limits; an entrant\nsends **Python, C or C++** source from an editor on the problem's page (or a file),\nand a verdict comes back in seconds — **OK, WA, TL, ML, RE, OL, CE** with the number\nof the first test that failed. It lives under **Contests** in the admin panel, and\nthe class opens it at `/a/<name>`; the same entry key as competitions brings an\nentrant back, and the `/k` list shows both.\n\nWhat you set up per problem: a Markdown statement with formulas, time and memory\nlimits, the tests (an archive in the usual layouts — `01`/` 01.a`, `1.in`/` 1.out`,\n`input`/` output` — or typed by hand; the ones marked as examples are shown under\nthe statement), how answers are compared (word by word, line by line, numbers with\nan epsilon, or your own Python `check(input, output, answer)`), and a reference\nsolution. The reference is judged like a submission and can **write the answers**\nfor inputs that have none; a contest opens only when every reference passes.\n\nHow a submission is judged. One throwaway container without network per submission runs all its tests: the program runs as an unprivileged user under a root harness, the answers reach the harness through its stdin into memory the program cannot read, and every test has its own CPU time, memory and output limit. Verdicts and live progress («test 7 of 22») stream back as it goes.\n\nRules and the board. **ICPC** — solved problems, then penalty minutes (20 per\nrejected attempt by default, compilation errors free), with an optional **freeze**\nfor the last minutes that the teacher reveals after the end; **IOI** — points for\ntests or groups of tests, the best submission per problem counts. Places are always\ndistinct. The board has a projector view, the teacher's console shows every\ntest's output (hidden tests included), answers questions to one person or to\neveryone, rejudges after a test fix, and exports the board as CSV.\n\nGuide: [contests](https://colloq.cc/docs/en/contests.html)\n\nThe whole app ships as a Python package: the server, the built web app, the CLI\nand the room's Dockerfile, behind one `colloq` command. Python itself only\ncarries them — the class runs on Node and Docker.\n\n```\npip install colloq     # Python 3.9+, into a virtualenv or with pipx\ncolloq start           # a class on this computer, in your browser\n```\n\nThe machine needs **Docker** running. On macOS 14+ and on Linux x64/arm64\n(glibc 2.28+) pip installs a wheel with Node.js and the server's dependencies\ninside, and that is all. Elsewhere (macOS 13, Alpine, other architectures) pip\ntakes the universal wheel, which needs **Node.js 22 or newer**: `colloq` finds\nit on its own, including under nvm, fnm, volta and asdf, says so in words when\nit is missing, and installs the server's dependencies once on the first run.\nCtrl+C saves the notebook and stops the class.\n\n1. Open the link `colloq start` prints. Like Jupyter's, it carries the setup\ntoken (kept in`~/.colloq/data/setup-token` ): the first time it makes you\nthe owner of the instance, and after that it signs you in.\n2. Create a class and choose its preset and Python environment.\n3. Share the class link, `/s/<id>` , and never a link with`/admin/` in it.\n\nState lives in `~/.colloq`, never inside the package: `.env`, the database,\n`workspace/` and the environments you create. Reinstalling or upgrading leaves\nall of it alone, and `COLLOQ_HOME` moves it elsewhere. `colloq host`, `status`,\n`doctor`, `backup` and `env` cover the rest — `colloq --help` lists them, and\n[python/README.md](https://github.com/colloq-edu/colloq/blob/main/python/README.md) and [cli/README.md](https://github.com/colloq-edu/colloq/blob/main/cli/README.md) explain\nthem.\n\n## **One link for your students: `colloq start --share`**\n\n`colloq start --share`\n`colloq start --share` starts the class, opens a quick Cloudflare tunnel and\nprints one block: the link to hand out (`https://….trycloudflare.com/s/<id>`),\nor, with no class yet, where to create one. Ctrl+C closes the link together with\nthe class, and a quick tunnel takes a new address on every start, so the link is\na new one each time. The first `--share` downloads a pinned `cloudflared`,\nchecked against a pinned SHA-256 before it is ever run and kept in\n`~/.colloq/bin` (`COLLOQ_CLOUDFLARED=/path` uses your own,\n`COLLOQ_CLOUDFLARED_DOWNLOAD=0` forbids the download). macOS and Linux only; on\nWindows, use WSL 2.\n\n**The isolation gate.** A class goes online only when every room's Python runs\nin a container of its own. `--share` and `--host` are refused before the start\nif kernels are not one container per room, and again before the tunnel unless\nDocker answers and the server confirms it. A refusal leaves the class running\nlocally. What stays true either way: the link is a door, and whoever has it runs\ncode in a sandbox on this computer. Keep it for your class.\n\n**Cloudflare addresses do not open from Russia.** For students there, publish\nthrough a relay of your own: `colloq start --host <name>` with the `RELAY_*`\nlines in `~/.colloq/.env`; from a checkout, `make relay-setup` prepares the\nrelay ([Give the room an address](#give-the-room-an-address)).\n\nYou need **Node.js 22 or newer**, npm, **Docker** (running) and Make.\n\n```\ngit clone https://github.com/colloq-edu/colloq.git\ncd colloq\nnpm ci\nmake dev\n```\n\n`make dev` builds the room kernel image when it is missing or stale, then runs\nthe server on `:3000` (with reload) and Vite on `:5173` in this terminal and opens\nthe browser (`OPEN=0` skips that). The first launch writes a short local `.env`;\nevery setting is documented in [.env.example](https://github.com/colloq-edu/colloq/blob/main/.env.example).\n\n1. Claim the instance at `/admin` with the token from`data/setup-token` or the\nfirst server log.\n2. Create a class and choose its preset and Python environment.\n3. Share the class link. To let people outside your machine in, run `make host` in a second terminal for a temporary public address; for a permanent one, see[Give the room an address](#give-the-room-an-address) .\n\n## **More on local runs**\n\nCtrl+C saves the notebook and stops this local instance's application and\nPython kernels. Files, outputs and the database remain; Python variables do not.\nServer reloads preserve kernels until the development session ends. `make run`\nbuilds the optimized frontend and runs the server in the background instead\n(`make logs-run`, `make stop`); `make up` keeps the app inside Linux Docker and\nalso needs Docker Compose.\n\n`make host` owns only its tunnel: stopping it leaves the local session running.\nTunnel failure also preserves local work. Publishing a `make dev` session uses\nan expiring address and leaves `.env` unchanged. Staff sign-in links and student\nclass links serve different purposes.\n\nLocal kernels use Docker containers and are intended for trusted workstation\nuse. Production uses the private broker described under\n[Deploy for a real class](#deploy-for-a-real-class).\n\nA checkout also builds the package itself: `make wheel` writes\n`python/dist/colloq-*.whl` (after `npm ci`; it needs Python 3 with pip), and\ninstalling that wheel gives exactly what `pip install colloq` gives.\n\n`colloq-vast` is one ready image for a rented Vast.ai **VM**: the server, the\nbuilt web app, the tunnel clients and the kernel build context.\n`make vast-vm HOST=class1` rents the VM over the API and starts the image on it,\nand the machine comes up with a class address and an owner sign-in link. Each room\nstill gets its own kernel container.\n[deploy/vast/README.md](https://github.com/colloq-edu/colloq/blob/main/deploy/vast/README.md) covers renting, settings,\nbackups and building the image (`make vast-image`); read its *What has been\nverified* section before relying on it for a class. The k3s path in\n[docs/deployment-vast.md](https://github.com/colloq-edu/colloq/blob/main/docs/deployment-vast.md) isolates rooms more strictly\nbut is a preview: no release up to and including 0.9.0 has published its bundle.\n\nThere are two ways to run Colloq for an institution:\n\n- **In your own Kubernetes.** The Helm chart`oci://ghcr.io/colloq-edu/charts/colloq` goes into one namespace: no\ncluster-admin and no cluster-scoped objects, restricted Pod Security,\nnetwork policies for every flow, images pinned by digest and rewritten to\nyour registry mirror, and secrets taken from existing Secrets (Vault,\nExternal Secrets), so ArgoCD sees no drift. A non-root app calls a private\nruntime broker, the only component with Kubernetes credentials, which\ncreates a separate Jupyter Pod for each room. The guide[Colloq in your Kubernetes](https://colloq.cc/docs/en/kubernetes.html) is\nwritten for the platform team: assumptions, permissions, network flows,\nstorage, sizing, upgrades and backups.\n- **On one VM.** Run the server image`ghcr.io/colloq-edu/colloq-server` with`colloq-host` on one dedicated Linux amd64 VM behind your own HTTPS reverse\nproxy. Each room still gets its own kernel container; the app holds the\nDocker socket, so the VM belongs to Colloq alone. Sizing, inbound and\noutbound access, nginx and Caddy settings, backups and updates are in the\nguide[Your own server (university)](https://colloq.cc/docs/en/server.html) .\n\nThe single-node k3s path below is a **preview** for those without a cluster:\na script installs k3s on a bare VM by itself. No release up to and including\n0.9.0 carries its deployment bundle (`release.json`, `colloq-deploy.tar.gz`,\n`SHA256SUMS`), so it cannot be installed from published artifacts yet. It runs\non **one Linux amd64 VM with k3s/containerd**. A non-root web app\ncalls a private runtime broker, which creates a separate Jupyter Pod, token and\nworkspace mount for each class. The app has no Docker socket or Kubernetes\ncredentials. If a room's runtime cannot start, execution fails; there is no\nfallback to an instance-wide kernel.\n\nIt needs an explicitly published release and its matching deployment bundle.\nFrom the extracted bundle, with your configuration in `instance.env`:\n\n```\npython3 scripts/release.py validate --release release.json\nsudo scripts/cluster.sh install --release release.json --env-file instance.env\nsudo scripts/cluster.sh smoke\n```\n\nPrivate images also require `--registry-config /path/to/pull-only-config.json`.\nEach release records the `sourceCommit`, image digests, exact tooling versions\nand deployment-tool hashes. The installer rejects mismatched tooling. The host\nproxy reaches the app at `127.0.0.1:30080`; the broker and Kubernetes API stay\nprivate.\n\nRooms keep the Python environment image they were created with when the default changes. Publish custom packages through a new release catalog; images are built outside the web app. GPU environments request one exclusive NVIDIA GPU per room and require a real CUDA preflight.\n\n| Operator guide | What it covers | \n|---|---|\n| [Colloq in your Kubernetes](https://colloq.cc/docs/en/kubernetes.html) | The Helm chart in one namespace of your cluster: assumptions, permissions, network flows, storage, registry mirror, ArgoCD, sizing and operations. | \n| [Helm chart](https://github.com/colloq-edu/colloq/blob/main/deploy/helm/colloq/README.md) | Every value of the chart and the environment variable it sets. | \n| [Your own server (university)](https://colloq.cc/docs/en/server.html) | The server image with `colloq-host` on a dedicated VM: sizing, network access, reverse proxy, settings, backups, updates and personal data. | \n| [Single-node deployment](https://github.com/colloq-edu/colloq/blob/main/deploy/k3s/README.md) | The single-node k3s preview for those without a cluster: releases, installation, environments, storage, updates, rollback and GPU prerequisites. | \n| [Runtime boundary](https://github.com/colloq-edu/colloq/blob/main/runtime/README.md) | Broker API, credentials, room lifecycle and isolation limits. | \n| [Vast VM with k3s](https://github.com/colloq-edu/colloq/blob/main/docs/deployment-vast.md) | Renting a VM, registry credentials, named backups and recovery. Rental and disk destruction require confirmation. | \n| [Vast VM with one image](https://github.com/colloq-edu/colloq/blob/main/deploy/vast/README.md) | The `colloq-vast` image, its template and on-start script. | \n| [Guides at colloq.cc](https://colloq.cc/docs/en/) | Installing, networking, environments, backups and updates, for operators and teachers. | \n\nThe following Make targets require a full source checkout. The minimal deployment bundle contains the cluster and recovery tools.\n\n| Route | Command | Requirements | \n|---|---|---|\n| Temporary tunnel | `make host` | Cloudflare connectivity; the URL changes each run. | \n| Named tunnel | `make tunnel-setup HOST=seminar.example.edu` , then`make host HOST=seminar.example.edu` | A configured Cloudflare account and domain. | \n| Your own relay | `make relay-setup WHERE=root@your-relay` , then`make host HOST=seminar.example.edu` | A public relay, its domain, and relay settings in `.env` . | \n| Direct HTTPS | `sudo make host-direct HOST=seminar.example.edu` | Public Linux host, reachable ports 80/443, and a Cloudflare DNS token; Caddy serves the app. | \n\nFrom the pip package the first three rows are `colloq start --share` and\n`colloq start --host <name>` (or `colloq host <name>` beside a running class),\nwith the `RELAY_*` lines in `~/.colloq/.env`.\n\nTest access from the students' network before class. Use your own relay or direct hosting where the Cloudflare tunnel is unreachable — it does not open from Russia. Keep the relay sized for the connected audience: every room published through it depends on that machine.\n\nProduction state lives under `/var/lib/colloq`. Local volumes survive Pod\nreplacement; losing the VM disk requires an off-machine backup.\n\n```\nsudo env MODE=consistent scripts/backup.sh\nsudo scripts/cluster.sh start\n```\n\nA consistent backup stops the app, broker and every room writer; restarting is\nexplicit and Python memory is lost. A live backup is **not an atomic snapshot**\nof the database and workspace. Updates and rollbacks also interrupt active rooms.\nFollow the deployment guide for restore, interrupted-operation recovery and\nschema-compatible rollback.\n\n## **Operator notes**\n\n`kernel/environments/<name>.txt` is a pip requirements list, and three header\nlines are directives rather than comments: `# colloq: gpu` claims an exclusive GPU\nslice for every room on it, `# colloq: from <name>` builds this image on another\nenvironment's image instead of the base, and `# colloq: python 3.12` chooses the\ninterpreter — 3.10 to 3.13, defaulting to the `ARG PARENT` version in\n`kernel/Dockerfile`. pip treats all three as comments, so they install nothing and\ndo not mark a built image stale. Only the **root** of a `from` chain decides the\nPython version: a layer on top of a built image installs wheels for the\ninterpreter it inherited and cannot replace it, so a child that asks for a\ndifferent version is refused by name before Docker is started.\n`make env-new NAME=cv PYTHON=3.12` writes the directive, `make env-list` and\n`make env-show` print the resolved version, and the Environments screen shows the\nversion of the **built** image beside its size — falling back to the version the\nfile asks for, with the usual rebuild mark, once the two disagree.\n\nBoth build modes produce a minified production frontend with lazy screens, hashed asset URLs and no source maps. The server's development mode does not turn the built frontend into a development bundle.\n\n| Command | Use | \n|---|---|\n| `make dev` /`npm run dev` | Supervised server watcher and Vite hot reload; kernels survive reloads. | \n| `make run` | Background server on the optimized build. | \n| `make run FAST=1` | Skip precompression for the edit-build loop; use the default for a class. | \n| `npm run build:optimized` | Build all artifacts without starting or restarting the server. | \n\nPrecompression adds Brotli quality 11 and gzip level 9 files alongside\nJavaScript, CSS, the PDF worker and plotly.js. The server selects an accepted\nencoding at the original URL and sends the stored bytes, avoiding compression\nwork on each download. Unsupported clients and `FAST=1` builds use the original\ndelivery path, where the server compresses each asset per request: 11.6 ms of\nCPU and 25 KB more on the wire for the largest chunk, once per student.\n`make host` warns when it is about to publish a build with no precompressed\nfiles.\nHTML retains revalidation; hashed assets retain their one-year cache.\nProduction Docker images and release CI use the precompressed build by default.\n`npm run perf` checks the bundle budgets for either mode. For complete cold entry\nthrough a working notebook, see [the entry benchmark](https://github.com/colloq-edu/colloq/blob/main/docs/entry-performance.md).\n\nYour own relay also serves the frontend. `make host` uploads `assets/`, `fonts/`\nand `pdf/` from the build to the relay, which serves those paths itself with the\nsame cache headers the app sends; only the live room still travels through the\ntunnel. A missing or stale file falls through to the tunnel, so the mirror never\nbreaks a class. `curl -sI https://<host>/assets/<file>` reports\n`X-Colloq-Mirror: hit` when the relay answered. Previous chunk names stay\navailable for 30 days, so tabs opened before a deploy still load.\n\nStart with [.env.example](https://github.com/colloq-edu/colloq/blob/main/.env.example). `make up` selects the explicit Docker\ndevelopment backend; the production installer supplies broker configuration.\nRoom memory and CPUs, the personal-notebook numbers, the process ceilings and\nthe upload limits are also instance settings: a value the owner saves through\n`/api/admin/resources` wins over `.env` until it is cleared, and takes effect\nwithout a restart.\n\n| Setting | Purpose | \n|---|---|\n| `BIND_ADDR` | Development server / host publication address. Unset means every interface; the example uses `127.0.0.1` . Compose applies it to the host port, keeping its container listener reachable. | \n| `PORT` /`PUBLIC_URL` | Local port and the public origin used to generate class links. | \n| `OPENAI_API_KEY` /`OPENAI_BASE_URL` /`OPENAI_MODEL` | Oracle credentials, endpoint and model; also configurable in the teaching panel. | \n| `SESSION_SECRET` | Signing key. When empty, a persistent key is generated in `DATA_DIR` ; preserve it in backups. | \n| `AUTH_JWT_JWKS_URL` /`AUTH_JWT_JWKS_FILE` | Sign-in by a proxy that vouches for the person with a signed JWT (Teleport application access, oauth2-proxy in front of an OIDC provider): the proxy's public keys, fetched over https or read from a file. Unset: off, and only links sign anyone in. A staff email signs in as that teacher; a student joins a room under the token's name, as one participant on every device. A request without a verified token is served as before. | \n| `AUTH_JWT_HEADER` /`AUTH_JWT_ISSUER` /`AUTH_JWT_AUDIENCE` | The header with the token ( `teleport-jwt-assertion` by default; never`Authorization` , which is the room API's own), and the`iss` and`aud` it must carry. The audience is required (`*` accepts any): one proxy signs every application's tokens. | \n| `AUTH_JWT_SUBJECT_CLAIM` /`AUTH_JWT_NAME_CLAIM` /`AUTH_JWT_EMAIL_CLAIM` /`AUTH_JWT_ROLES_CLAIM` /`AUTH_JWT_EMAIL_DOMAIN` | Claim paths tried in order, comma-separated ( `traits.email` reaches into an object); the defaults read Teleport and OIDC tokens. The domain turns a bare login into the email staff are matched by. | \n| `AUTH_JWT_TEACHER_ROLES` /`AUTH_JWT_OWNER_ROLES` | The proxy's roles that add a person to the staff on their first visit; the roles then decide, so someone removed in the panel comes back while the proxy still gives the role. Unset: nobody is added, and the owner adds colleagues by email. Only an owner role claims an unclaimed instance. | \n| `UI_LANGUAGE` | Interface language ( `ru` , the default, or`en` ) until the owner chooses one in`/admin` . | \n| `KERNEL_ENV` | Default Python environment for the Docker development backend. | \n| `KERNEL_PIDS` /`KERNEL_ROOM_SUBNET` | Process ceiling of a room container (512 by default) and the subnet of the `colloq-rooms` network (`10.213.0.0/22` ). | \n| `KERNEL_OWN_MAX` /`KERNEL_OWN_PIDS` | Students' personal notebooks run in a second container per class, one that never receives the class's GPU: how many kernels may live in it at once (60 by default) and its process ceiling (2048, because each kernel is about fifteen threads). Over the first number, a run in a personal notebook is refused in words. | \n| `KERNEL_OWN_IDLE_MIN` | Minutes of idling after which one personal notebook's kernel is stopped (30 by default; `0` never stops them). The class keeps running; the notebook is told in the kernel log, and the next Run brings its kernel back. The container goes with the last kernel in it. | \n| `KERNEL_ROOM_IDLE_MIN` | Minutes a class with nobody inside and nothing computing keeps its kernels (120 by default; `0` keeps them). A running container holds its whole memory limit whatever it uses, so idle classes can starve new ones; the owner also sees and stops kernels by hand under «Ресурсы» → «Работают сейчас». | \n| `COLLOQ_ROOM_NETWORK` | Unset: rooms cannot reach local addresses (LAN, router, the host, cloud metadata), and they refuse to start if that block cannot be installed. `open` lifts the block for a trusted setup;`colloq doctor` then says so. | \n| `MAX_UPLOAD_MB` /`MAX_SESSION_MB` | Application upload limits; these do not limit arbitrary writes from Python. | \n| `COUNCIL_COPY_MB` | How much memory one council attempt may spend on personal copies of the room's data (512 by default). Anything above the budget stays shared, and the attempt is told so in its own output. Copy-on-write pandas copies cost nothing and are not counted. | \n| `COUNCIL_MEMORY_GUARD` | Cap the address space of a council attempt at the container's memory limit minus what is already in use ( `1` by default;`0` turns it off). A greedy attempt then fails with`MemoryError` on its own card instead of the OOM killer taking the notebook's kernel and everybody's variables. Skipped automatically where it cannot work: outside Linux, without a cgroup limit, or with CUDA nearby. | \n| `COMPETITION_BACKEND` | `broker` executes competition notebooks, metrics and package preparation in isolated Kubernetes Pods with pinned catalog images (the Helm chart or the k3s preview); it is selected automatically by the production broker deployment.`docker` uses throwaway containers on the local Docker (`colloq start` ,`make up` , the server image).`test` is a non-executing stand-in and requires`NODE_ENV=test` . The app refuses submissions when the selected executor or preparation capability is unavailable;`/api/health` reports each capability separately. Production does not need a Docker socket in the app Pod. | \n| `DATA_HOST_DIR` | The competition directory as the Docker daemon sees it — what `WORKSPACE_HOST_DIR` is to rooms. Leave it unset on a host-native server. Under`make up` the server itself lives in a container, and without this a submission silently receives an empty directory instead of its data. | \n\nIsolation is **between classes**, not inside one: participants in a class share\nPython, files and a terminal, and anyone allowed to run code reaches the class's\nfiles through Python whatever the Files panel allows.\n\nEach room's Python runs in a hardened container of its own, on every path and\nwhether or not the class is published. It runs as uid 1000 with every Linux\ncapability dropped and no way to regain them, under a process ceiling\n(`KERNEL_PIDS`, 512 by default). The room keeps the internet, so pip, datasets\nand APIs work; it loses the local network — the LAN, the router, the host\nitself, a neighbouring room, cloud metadata. Port 53 is the deliberate\nexception, because Docker's resolver forwards from addresses that differ per\nmachine. A room refuses to start when that block cannot be installed, rather\nthan opening quietly; `COLLOQ_ROOM_NETWORK=open` lifts it for a trusted machine,\nand the server and `colloq doctor` say so out loud.\n\nWhat the boundary does not do: containers share the host Linux kernel, so this\nis not VM isolation; on the Docker-based paths (`colloq start`, `make dev`,\n`make up` and the Vast image) the server controls the Docker daemon, which on a\nLinux host amounts to root; standard Kubernetes NetworkPolicy has a local-node\ntraffic exception; one node provides no high availability; and PVC capacity is\nnot an enforced per-room disk quota. Review the [runtime boundary](https://github.com/colloq-edu/colloq/blob/main/runtime/README.md)\nbefore admitting untrusted workloads, and report vulnerabilities privately as\ndescribed in [SECURITY.md](https://github.com/colloq-edu/colloq/blob/main/SECURITY.md).\n\nThe guides at **[colloq.cc/docs](https://colloq.cc/docs/en/)** cover installing,\nrunning a class, room rules, lectures, council, the Oracle, competitions,\nenvironments, publishing, networking, backups and updates, in\n[English](https://colloq.cc/docs/en/) and [Russian](https://colloq.cc/docs/).\nOperator and developer references live next to the code:\n[deploy/helm/colloq](https://github.com/colloq-edu/colloq/blob/main/deploy/helm/colloq/README.md), [deploy/k3s](https://github.com/colloq-edu/colloq/blob/main/deploy/k3s/README.md),\n[deploy/vast](https://github.com/colloq-edu/colloq/blob/main/deploy/vast/README.md), [runtime](https://github.com/colloq-edu/colloq/blob/main/runtime/README.md),\n[tests](https://github.com/colloq-edu/colloq/blob/main/tests/README.md), [cli](https://github.com/colloq-edu/colloq/blob/main/cli/README.md) and [python](https://github.com/colloq-edu/colloq/blob/main/python/README.md).\nThis page in Russian: [README.ru.md](https://github.com/colloq-edu/colloq/blob/main/README.ru.md).\n\nThe version's single source is `\"version\"` in the root `package.json` (the\nbadge at the top shows it). The other copies, including\n`python/colloq/_version.py`, are kept in step, and `make version` checks that\nthey agree. Versions are never bumped by hand. Commits on `main` follow\n[Conventional Commits](https://www.conventionalcommits.org/), and\n[release-please](https://github.com/googleapis/release-please) keeps a release\npull request open that bumps every copy and adds the changelog section. Merging\nthat pull request tags `vX.Y.Z`, creates the GitHub Release and attaches the pip\nwheel; a separate workflow publishes that wheel to PyPI, pushes the images to\nGHCR and the Helm chart, with the release's digests in its values, to\n`oci://ghcr.io/colloq-edu/charts/colloq`; the k3s release bundle is a separate\nmanual workflow, and no release up to and including 0.9.0 has published one. A\nrunning server reports its version at `/api/health`.\n\nWhat changed is in [CHANGELOG.md](https://github.com/colloq-edu/colloq/blob/main/CHANGELOG.md); how a release is cut is in\n[RELEASING.md](https://github.com/colloq-edu/colloq/blob/main/RELEASING.md).\n\nColloq is maintained by one person, and help is welcome: a clear bug report, a reproduction from a real class, a translation fix or a small, well-tested pull request all go a long way. For anything larger, such as a new room mode, a new deployment path or a change to the permission model, open an issue first.\n\n- [CONTRIBUTING.md](https://github.com/colloq-edu/colloq/blob/main/CONTRIBUTING.md) : setting up a checkout, the checks, the conventions that matter here, and commits and pull requests.\n- [CODE_OF_CONDUCT.md](https://github.com/colloq-edu/colloq/blob/main/CODE_OF_CONDUCT.md) : how we treat each other.\n- [SUPPORT.md](https://github.com/colloq-edu/colloq/blob/main/SUPPORT.md) : where to ask questions.\n- [SECURITY.md](https://github.com/colloq-edu/colloq/blob/main/SECURITY.md) : reporting vulnerabilities privately.\n- [Issue templates](https://github.com/colloq-edu/colloq/blob/main/.github/ISSUE_TEMPLATE) : bug reports and feature requests.\n\nAfter `npm ci` and `make dev` (see [Quick start](#quick-start)), the server\nlistens on `:3000`; Vite serves the development UI on `:5173` and proxies\nAPI/WebSocket traffic to the server. `make dev` prepares the room kernel image\nwhen needed; there is no shared Jupyter service. Linux production requires secure\nfilesystem access through `/proc/self/fd` and refuses to start without it. Leave\n`WORKSPACE_HOST_DIR` unset when running the app directly on the host.\n\n```\nnpm run typecheck\nnpm test\nnpm run build\n```\n\nTests run without a live server or browser. End-to-end, performance and load\nharnesses (`npm run e2e`, `npm run perf`, `make load`) need a running disposable\ninstance, never a real class; the header of each script in `scripts/` gives its\ntarget and settings. Operations such as releases, the relay,\nenvironments and backups are Make targets: `make help` lists them, with help text\nin Russian.\n\n| Directory | Responsibility | \n|---|---|\n| [`web/`](https://github.com/colloq-edu/colloq/blob/main/web) | Svelte 5 UI, CodeMirror 6 editors and Yjs collaboration. | \n| [`server/`](https://github.com/colloq-edu/colloq/blob/main/server) | Express API, WebSockets, SQLite persistence, execution queues and the Oracle. | \n| [`shared/`](https://github.com/colloq-edu/colloq/blob/main/shared) | Notebook schema, room rules, protocols and UI strings shared by server and web. | \n| [`runtime/`](https://github.com/colloq-edu/colloq/blob/main/runtime) | Private broker and fixed Kubernetes room templates. | \n| [`kernel/`](https://github.com/colloq-edu/colloq/blob/main/kernel) | Python kernel image and environment definitions. | \n| [`cli/`](https://github.com/colloq-edu/colloq/blob/main/cli) | The `colloq` command and the supervisor behind`make dev` . | \n| [`python/`](https://github.com/colloq-edu/colloq/blob/main/python) | The pip package that carries the built app and the `colloq` command. | \n| [`deploy/`](https://github.com/colloq-edu/colloq/blob/main/deploy) ·[` scripts/`](https://github.com/colloq-edu/colloq/blob/main/scripts) | Release tooling, the Helm chart, the k3s and Vast deployments, hosting and operations. | \n| [`site/`](https://github.com/colloq-edu/colloq/blob/main/site) ·[` docs/`](https://github.com/colloq-edu/colloq/blob/main/docs) | The colloq.ru landing page and guides (sources in `docs/pages` , built by`docs/build.py` ), plus operator notes in`docs/` . | \n| [`tests/`](https://github.com/colloq-edu/colloq/blob/main/tests) | The `node --test` unit suite; its README explains how the run is set up. | \n\nThe server participates in the Yjs document and writes execution results into it, so every client receives the same output. Notebook state is periodically snapshotted to SQLite; graceful shutdown flushes pending changes, while abrupt power loss can lose the latest edits. Each class gets its own kernel container (Docker locally, a Jupyter Pod behind the broker in production). Browsers talk only to the app and never receive Jupyter credentials.\n\nThe animations in this README are generated by `make readme-art`\n([scripts/readme-art.mts](https://github.com/colloq-edu/colloq/blob/main/scripts/readme-art.mts)).\n\nColloq focuses on the live class. It is not an LMS: there is no course progress tracking, automated grading, SSO or video conferencing. Pair it with the call tool you already use.\n\n  MIT © 2026 Aleksandr Kalashnikov ·\n  [Documentation](https://colloq.cc/docs/en/) ·\n  [colloq.ru](https://colloq.ru)", "url": "https://wpnews.pro/news/colloq-a-collaborative-python-classroom-you-can-self-host", "canonical_source": "https://github.com/colloq-edu/colloq", "published_at": "2026-11-04 10:03:56+00:00", "updated_at": "2026-11-04 10:24:37.405559+00:00", "lang": "en", "topics": ["ai-tools", "developer-tools"], "entities": ["Colloq", "colloq-edu", "Python", "Docker", "Node.js"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/colloq-a-collaborative-python-classroom-you-can-self-host", "markdown": "https://wpnews.pro/news/colloq-a-collaborative-python-classroom-you-can-self-host.md", "text": "https://wpnews.pro/news/colloq-a-collaborative-python-classroom-you-can-self-host.txt", "jsonld": "https://wpnews.pro/news/colloq-a-collaborative-python-classroom-you-can-self-host.jsonld"}}