Skip to main content

Correxit

GitHub Actions status PyPI version npm version Documentation Launch JupyterLite

Correxit is a kernel-agnostic Jupyter extension for grading Jupyter notebooks. It is also the third-person singular perfect active indicative conjugation of the Latin verb corrigere, to correct.

The Python package delivers the extension to JupyterLab and bundles the schemas that register settings and toolbar contributions. All grading logic runs in the browser; there is no server component.

Documentation

Document Audience Content
AUTHORING Assignment author How to create, configure, test, and distribute a workbook
DESIGN Developer Architecture, data model, async patterns
MOODLE Teacher, integrator Browser-only Moodle workflow and constraints
SECURITY Developer, auditor Threat model, encryption, key management
PLUGINS Integrator Plugin token interfaces for LMS and transport
RELEASE Maintainer Release process and versioning
CHANGELOG Everyone Version history

What does it do? How does it work?

With Correxit, a user can convert a Jupyter notebook using any language kernel into a gradable workbook.

  • All of the workbook creation and grading logic happens in the Jupyter frontend (JupyterLab / Jupyter Notebook / JupyterLite). There is no backend.
  • All workbook data for both a grader and a student is contained within the document itself, which is a Jupyter notebook (.ipynb) file with a correxit field in its metadata. That object carries a required cxtformat discriminator independent of the package version. There is no other data store.
  • The author converts a Jupyter notebook into a workbook by configuring which cells to grade, optionally setting a local late policy for backendless workflows, and encrypting the grading logic with a passphrase or a deployment-managed key. A custom Unlocker can provide credentials from an authenticated service throughout the author and student workflows.
  • A grader uses the Correxit Corrector panel to scan a directory of submitted workbooks, optionally unlock them, and batch-grade them, with configurable concurrency and a per-workbook timeout to handle hung kernels.

New assignments use cxtformat: 2, authenticating fixed cell sources and the issued cells' types and relative order. Grading, cached-score reuse, CSV scores, and collection require authentication. Format 1 was a draft with no usage in the wild; format 2 supersedes it and is the only supported format. See SECURITY for the contract.

This video shows an example, a SQL (xeus-sql) Jupyter notebook that loads the Chinook database in SQLite and runs some queries and renders a Vega graph of the genres and media types tables. The user converts the notebook into a workbook and demonstrates the basic features of Correxit.

https://github.com/user-attachments/assets/638b0fde-0168-499b-8ff4-d0ef0a25f031

Requirements

  • JupyterLab >= 4.0.0 or Jupyter Notebook >= 7.0.0
  • Node.js >= 20.0.0 for the optional Node runtime assignment entrypoint

Node Runtime Assignment

Correxit also publishes a narrow Node runtime entrypoint for issuing one already-authored workbook without reopening the Jupyter UI:

import { Assignment } from 'correxit/node';

const assigned = await Assignment.assign({
  notebook,
  assignee: 'foo@example.com',
  key: null,
  passphrase: secret
});

The host application supplies the notebook JSON, passphrase or derived key, and final distribution step. Correxit keeps those secrets in memory for this pure notebook transformation; it does not add a backend authority or trusted third party. See examples/assign-one.mjs for a minimal script.

Every cell must have a unique, non-empty string ID before issuance. Missing or invalid IDs are rejected; the caller must establish stable cell identities before calling Assignment.assign.

Install

To install the extension, execute:

pip install correxit

Uninstall

To remove the extension, execute:

pip uninstall correxit

Troubleshoot

If you cannot see the Correxit sidebar in Jupyter, check that the frontend extension is installed:

jupyter labextension list

License

Correxit is open source under the GNU Affero General Public License version 3 only (AGPL-3.0-only). Version 2.0 marks its first open-source release.

Scheduled transition to BSD 3-Clause

Correxit will switch to the BSD 3-Clause License at 00:00 UTC on 1 January 2028. At that time, Correxit will also become available under BSD-3-Clause; releases made from that time forward will use BSD-3-Clause.

The transition does not withdraw or replace rights already granted under AGPL-3.0-only. Anyone who receives Correxit under the AGPL may continue to use that license permanently, provided its conditions are met.

Contribution terms

Until 00:00 UTC on 1 January 2028, external contributions are accepted under the Correxit Contributor License Agreement. Contributors retain ownership of their work. The CLA grants the public an AGPL license immediately and an irrevocable BSD 3-Clause license taking effect at the transition. It also grants QuantStack an immediate, non-exclusive right to use and license accepted contributions under commercial or proprietary terms. Any contribution used under that additional license remains available in public Correxit under the applicable public license.

The CLA requirement ends at the transition. Contributions submitted at or after 00:00 UTC on 1 January 2028 require no CLA and are accepted under BSD-3-Clause alone. Rights already granted for earlier contributions remain in effect.

Before the transition, do not submit a contribution unless you have read and accepted the CLA and have the authority to grant its rights. Contributions whose necessary rights are already held under another applicable agreement do not require a separate acceptance.

Contributing

Development install

Install Pixi first. Pixi provides the Python, NodeJS, JupyterLab, JupyterLite, and build tools used by this checkout.

The jlpm command is JupyterLab's pinned version of yarn that is installed with JupyterLab. Correxit keeps the normal JupyterLab extension workflow: prefix commands with pixi run, or enter pixi shell and run them directly.

# Clone the repo to your local environment
# Change directory to the correxit directory
# Create the locked development environment
pixi install
# Link your development version of the extension with JupyterLab
pixi run jupyter labextension develop . --overwrite
# Rebuild extension Typescript source after making changes
pixi run jlpm build

You can watch the source directory and run JupyterLab at the same time in different terminals to watch for changes in the extension's source and automatically rebuild the extension.

# Watch the source directory in one terminal and automatically rebuild
pixi run jlpm watch
# Run JupyterLab in another terminal
pixi run jupyter lab

With the watch command running, every saved change will immediately be built locally and available in your running JupyterLab. Refresh JupyterLab to load the change in your browser (you may need to wait several seconds for the extension to be rebuilt).

JupyterLite development

Correxit runs entirely in the browser, so you can develop against JupyterLite instead of a full Jupyter Server. The workflow uses two watch processes and a symlink so that every saved TypeScript change is immediately available after a browser refresh.

1. Build the JupyterLite testbed and website once:

pixi run jlpm build:lite

This pre-compiles the xeus Wasm kernels, copies example content into the static site, mounts content files into the kernel virtual filesystem, and runs jlpm link:lite to symlink the built extension back to correxit/labextension. It then assembles the MkDocs website from its landing page, the canonical repository documentation, a generated reference for the public TypeScript APIs, and that same JupyterLite build. Because the development site links the demo rather than copying it, subsequent TypeScript rebuilds are picked up without re-running build:lite.

2. Start two terminals:

# Terminal 1: rebuild on every save
pixi run jlpm watch

# Terminal 2: serve the website at http://localhost:8888
pixi run jlpm serve

3. Open http://localhost:8888/demo/notebooks/?path=chinook.ipynb and refresh after each rebuild. JupyterLab remains available at http://localhost:8888/demo/lab/.

The landing page is at http://localhost:8888/. Its guides are rendered directly from README.md, CHANGELOG.md, and docs/, while its API reference is generated from the package's public TypeScript entry points. Run pixi run jlpm build:site to refresh both without rebuilding JupyterLite.

If you rebuild the labextension outside of build:lite (e.g. after a clean), run pixi run jlpm link:lite to re-establish the symlink and patch the manifest hash.

The lite/ directory contains:

File Purpose
jupyter_lite_config.json Build configuration: contents directory, Service Worker toggle
environment.yml Wasm kernel environment (xeus-python, xeus-sqlite) resolved from emscripten-forge
link.mjs Post-build script that symlinks the dev extension and patches the manifest hash

The site/ directory contains the landing page, small Correxit-specific overrides for MkDocs Material, the documentation manifest, and the staging hook that assembles the published artifact. mkdocs.yml defines the navigation and mike versioning. site/_api/, site/_docs/, site/_output/, and lite/_output/ are generated and are never checked in.

By default, the jlpm build command generates the source maps for this extension to make it easier to debug using the browser dev tools. To also generate source maps for the JupyterLab core extensions, you can run the following command:

pixi run jupyter lab build --minimize=False

Development uninstall

The Pixi development environment lives in .pixi/ and can be deleted when you no longer need the local workbench.

In development mode, you will also need to remove the symlink created by jupyter labextension develop command. To find its location, you can run pixi run jupyter labextension list to figure out where the labextensions folder is located. Then you can remove the symlink named correxit within that folder.

Testing the extension

Frontend tests

This extension is using Jest for JavaScript code testing.

To execute them, execute:

pixi run jlpm
pixi run jlpm test

Integration tests

This extension uses Playwright for the integration tests (aka user level tests). More precisely, the JupyterLab helper Galata is used to handle testing the extension in JupyterLab.

More information is provided within the ui-tests README

Packaging the extension

See RELEASE

Metadata

Release files for correxit 2.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for correxit 2.2.0
File Size Uploaded
correxit-2.2.0.tar.gz 1.2 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for correxit 2.2.0
File Interpreter ABI Platform
correxit-2.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.5 MB

Release files / correxit-2.2.0.tar.gz

Download URL correxit-2.2.0.tar.gz
Size 1.2 MB
Tags Source
SHA-256 checksum
How to use checksums
7ef04f23e270d676c39316bb9a98f05c50a4bca70707bffdb87ba5bdfb3bc83d
BLAKE2b-256 checksum
How to use checksums
ab9482b2931dd49ca8a001c47eb2a7ef4604134a922a9d00e728a9fb03941da0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.

Transparency log

Release files / correxit-2.2.0-py3-none-any.whl

Download URL correxit-2.2.0-py3-none-any.whl
Size 288.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ee4bf9df06288d23a75b2ee43bebcc885d3f301daf9e3e10b3ebb033af537dde
BLAKE2b-256 checksum
How to use checksums
c185a15fc74dc33c541b45d1b0db27b8bad366146dc548a4ed881bbc052a6875
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

2.2.0 This release

2 release files

2.1.0

2 release files

2.0.2

2 release files

2.0.1

2 release files

2.0.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page