Skip to main content

Course Harness

Course Harness is a local-first application for creating source-grounded Courses in a human-readable Course Workspace.

Install and launch

Course Harness requires Python 3.14. Run the published package without a permanent installation:

uvx --python 3.14 --torch-backend cpu course-harness

Or install it as a command-line tool:

uv tool install --python 3.14 --torch-backend cpu course-harness
course-harness

--torch-backend cpu installs the CPU build of PyTorch, which local document conversion uses. Without it, uv installs the default CUDA build on Linux, which downloads several gigabytes of GPU libraries.

The package contains the production web interface. Course Authors do not need Node.js, Bun, Nix, Docker, a database, or a paid model provider to launch the Workspace Launcher. LibreOffice is optional and enables high-fidelity slide thumbnails when its libreoffice or soffice executable is available on PATH; semantic previews remain available without it.

To try an unreleased revision directly from GitHub:

uvx --python 3.14 --torch-backend cpu --from git+https://github.com/martinkozle/course-harness.git course-harness

The command starts one loopback-only process, opens http://127.0.0.1:8765, and shows the Workspace Launcher. Use the operating-system folder chooser to create or open a Course Workspace. Pass an explicit path (including .) to bypass the launcher, --no-browser to avoid opening the application automatically, or --port <number> to choose another local port:

course-harness /path/to/course-workspace

Run course-harness --version to check the installed build.

Course Harness stores canonical Course state in readable Workspace files. Runtime caches, application state, and provider credentials stay in platform-native user locations outside Course Workspaces. Provider credentials use a safe operating-system keyring when one is available and a private application file otherwise; the in-app runtime diagnostics show the active storage mode, exact runtime locations, and available capabilities.

For an OpenAI-compatible provider on another local machine, enter its API base URL (for example, http://blaze.home:8081/v1) and select Allow HTTP to another host. HTTPS and loopback HTTP work without this opt-in. Remote HTTP sends the API key and Course requests without encryption; use it only on a network you trust.

After you open a Course, the workspace has three parts: a navigator on the left (Course Plan, Sources, the numbered Lectures, Conversations, History, and Settings), the conversation with the Course Agent, and a canvas that shows whatever you open. A new Course starts with your material: drop papers, notes, or slides onto the start screen, discover papers, or reuse files from your Library, then tell the agent what the Course should teach. You can also write the Course Plan yourself.

Sources accepts PDF, .pptx, and .docx Resources. Files added from the start screen or the This course tab are saved to your Library and included in the Course; the Library tab keeps files without including them, and Discover searches public paper indexes (arXiv, Crossref, Semantic Scholar, and OpenAlex) and code indexes. Web pages are accepted as well. Docling extracts text, tables, and document structure for search and Source use, including text recognition for scanned PDFs. On the first PDF upload, Course Harness asks before downloading layout, table, and OCR models to the application cache outside the Course Workspace. Conversion runs locally and stores a structured Docling result alongside the searchable Markdown. The original files remain immutable Snapshots. Settings → Diagnostics also has a Download PDF models control and Rebuild search index. If models are missing when you process or reprocess an existing PDF, the same prompt appears. Cancel leaves the Resource unchanged. The download shows progress across its layout, table, and OCR stages; stage progress is not a byte percentage.

The Course Agent can research too. Ask it to find sources for a topic and it searches the same paper indexes. Its results appear under its reply; they are not Sources until they are added to the course, and only added Sources can be cited. For an open-ended request the agent adds the most relevant results itself and tells you which; for a narrow one it lists them and asks. Either way, Add to course and Remove from course on each result let you decide. The agent can only add an address that a search returned or that you gave it. A Course Plan must exist before Sources can be added.

Paper search works without keys. Semantic Scholar's keyless requests share a public rate limit that is often busy; add your own key under Settings → Research → Paper search and it is sent as x-api-key on every Semantic Scholar request, from both Discover and the Course Agent. The key is stored like provider keys, never in a Course Workspace.

Settings → Research adds web search through Connectors. A Connector is a remote MCP server whose tools the Course Agent may use; Exa is set up by default and works without a key at Exa's free, rate-limited tier. Put an Exa API key in its x-api-key header to raise the limits, or delete Exa and add another search service with its own URL and headers. Header values and URLs marked secret are stored like provider keys, in the operating-system keyring or a private file, never in a Course Workspace. Test lists a Connector's tools so you can choose which the agent may use and which one reads pages. Your requests, including search queries, are sent to each enabled Connector. When a site blocks a direct download or renders its text with scripts, Course Harness reads the page through that page reader instead and records which Connector read it. Only HTTPS Connectors, or HTTP to this computer, are accepted.

Each Workspace can keep multiple Conversations in that private application state. The navigator lists them; each row's menu renames, archives, or deletes one. A new Conversation stays an unsaved draft until you send its first message, and an unsent message is kept per Conversation while you switch. Summarize earlier context in the Conversation menu shows an editable summary for review before using it as the earlier context for future model runs. The full transcript remains visible. Resolve any pending agent approval before summarizing, archiving, or deleting its Conversation. An existing single transcript is migrated automatically when that Workspace's Conversations are first opened.

History lists changes since the last Course Revision, lets you save a named revision or restore an earlier one, and holds published Course Releases. Publish release in the canvas bar prepares a new Release; Export PowerPoint on a Lecture downloads a file from the current work without publishing anything.

Development requirements

  • Nix with flakes enabled

No Docker daemon, database server, or external service is needed for the initial Workspace experience.

Enter the development environment

nix develop

The checked-in flake pins Python, uv, Bun, Node.js, Chromium, and Git. Python packages remain pinned by uv.lock, while frontend packages remain pinned by bun.lock.

If you use direnv with nix-direnv, install and hook direnv into your login shell first, then allow the checked-in .envrc once:

direnv allow

After that, entering the repository activates the flake automatically. Restart terminal tools such as Codex after activation so they inherit the development environment:

codex

Without direnv, launch Codex directly through the shell with nix develop -c codex.

Entering the development shell also installs the repository's generated pre-commit and pre-push Git hooks. They cover Biome, Ruff linting and formatting, ty, nixfmt, uv lock freshness, syntax checks, and common repository hygiene checks. Vendored agent skills and generated frontend bundles are excluded. Run the same hooks across the full repository with:

pre-commit run --all-files

The hook configuration lives in flake.nix; .pre-commit-config.yaml is generated and ignored.

Launch from a development checkout

uv sync --locked
bun install --frozen-lockfile
bun run build
uv run course-harness

The last command launches the checkout. Pass a Workspace path to bypass the launcher:

uv run course-harness /path/to/course-workspace

Course Harness rejects missing paths, files, symbolic links, and the filesystem root as unsafe Workspace choices. It does not expose a general filesystem API.

Development

For frontend hot reload, start the API and Vite in separate terminals:

uv run course-harness /path/to/course-workspace --no-browser
bun run dev

Vite serves the development UI at http://127.0.0.1:5173 and proxies /api to Course Harness.

Run focused and full checks with:

pre-commit run --all-files
nix flake check
uv run pytest tests/test_app.py
uv run pytest tests/test_cli.py
uv run ruff check .
uv run ruff format --check .
uv run ty check
bun run lint
bun run typecheck
bun run build
bun run test:e2e
uv run pytest

Build and smoke-test the wheel in an isolated temporary environment with:

uv run python scripts/verify_distribution.py

This verifies the wheel metadata and bundled React assets, launches the wheel through uvx, installs and launches that same wheel with uv tool install in a temporary isolated tool home, and launches the current committed revision through a direct uvx git+file:// URL. Each launch checks both the health endpoint and production interface. Release publication is a separate explicit step; building and verifying locally never uploads an artifact or changes your real uv tool installation.

bun run test:e2e builds the production frontend, launches Course Harness on a verified-empty smoke Workspace under the ignored local cache, and runs the Playwright journey. Inside Nix, the test uses the flake-pinned Chromium. Outside Nix, install the Playwright browser once with bunx playwright install --with-deps chromium, or set PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH to a compatible Chromium executable.

Without Nix

Nix is the supported development environment, but the application remains usable without it. Install Python 3.14, uv, Bun 1.3 or newer, Node.js, and Chromium, then follow the same uv and Bun commands above. No Nix store paths are recorded in package.json or application configuration.

Metadata

Release files for course-harness 0.1.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 course-harness 0.1.0
File Size Uploaded Provenance
course_harness-0.1.0.tar.gz 766.0 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for course-harness 0.1.0
File Interpreter ABI Platform
course_harness-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.6 MB

Release files / course_harness-0.1.0.tar.gz

Download URL course_harness-0.1.0.tar.gz
Size 766.0 kB
Tags Source
SHA-256 checksum
How to use checksums
08c868dc710ba3d2172585795a5da329004a3e917120356ac57d617686730d23
BLAKE2b-256 checksum
How to use checksums
a679349bd5b132479871c3052d94d0899f42c02f83d581dbdc1bbfde44b840d3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"NixOS","version":"26.05","id":"yarara","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / course_harness-0.1.0-py3-none-any.whl

Download URL course_harness-0.1.0-py3-none-any.whl
Size 790.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c8f839260fbd6fa672034bae8fe3d7c98dd59f27da86b5d5960e6fa15e30b686
BLAKE2b-256 checksum
How to use checksums
0191f90924483bca355ffe80638cd4457e6a0eaeeb4ceb7c752b89878a8f104e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"NixOS","version":"26.05","id":"yarara","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for course-harness 0.1.0

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

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