---
title: Campus
type: project
description: A self-hosted study assistant for my courses: Brightspace sync, hybrid retrieval and an agent that logs every write.
tags: [local-ai, rag, agents, education-tooling]
related: [index.md, homelab.md, aware.md, ../skills.md, ../local-ai.md]
id: campus
dates: "Aug 2026 to present"
role: solo
status: active
rank: flagship
updated: 2026-10-16
---
Dates: Aug 2026 to present
Role: solo
Rank: flagship project
Stack: Python · FastAPI · SQLite · embeddings · cross-encoder reranking · React
courses, in daily use (I'm the only user): 6
tests in CI on every push: 308
agent tools: 20
Links: GitHub https://github.com/HasNate618/Campus
Summary: My courses are spread across six Brightspace pages, so I built one place to check deadlines, study from slides and test a draft against the brief. Campus syncs everything into SQLite and answers from the course files with hybrid retrieval: a lexical prefilter, embeddings and a cross-encoder reranker. Its 20-tool agent logs every change it makes to my notes or due dates, and it runs on any OpenAI-compatible endpoint, local or hosted.
Limits: The agent edits notes and due dates directly and logs every write with the before and after. Campus has no approval gate and no rollback; the NixOS homelab is the project that gates changes and rolls them back.

# Campus

## tl;dr

- Syncs my Brightspace courses into local SQLite and answers questions from the course files.
- Hybrid retrieval over my course files: just over 200 of them as of early October 2026.
- A 20-tool agent that can also edit my notes and due dates. Every write it makes is logged.
- 308 tests run in CI on every push.
- Works with any OpenAI-compatible endpoint, local or hosted.

## Why I built it

My course material lives in Brightspace, spread across 6 courses. I wanted one place to look up deadlines, study from the slides, and check a draft against the assignment brief, running on hardware I control.

I also wanted it to update my notes and due dates. That is only useful if I can see exactly what it changed, so every AI write is logged with the before and after.

## What I built

It's a solo project, so everything below is mine.

- LMS sync with Playwright, MFA login included, into SQLite and a local file store.
- sha256 change detection, so unchanged files are not downloaded again.
- Hybrid retrieval: embedding search, cross-encoder reranking, and a lexical SQL prefilter.
- A fix for exact phrases that scored below the semantic cutoff. They now go through the lexical path.
- A 20-tool agent that answers questions, reads files, and edits notes or due dates, in a bounded tool loop.
- An append-only audit log with the before and after state of every AI write.
- A React, TypeScript and Vite PWA. <!-- one-source-ok -->
- CI on GitHub Actions: pytest, frontend type-check and a production build on every push.

## How it works

```diagram
alt: Brightspace syncs into SQLite and a local file store. Those files are split into an index of chunks. Retrieval reads the index and feeds an agent with twenty tools, which answers questions and writes every change to an audit log with the before and after state.
Brightspace ─► sync ─► SQLite + files ─► index
                                          │
     answer ◄── agent (20 tools) ◄── retrieve
        │
        └──► audit log (every write, before and after)
```

1. Sync. Playwright signs in and pulls course content. Files with an unchanged sha256 are skipped.
2. Index. Course files are split into chunks.
3. Retrieve. A SQL prefilter catches exact phrases, embedding search finds related chunks, and a cross-encoder reranks them.
4. Answer. The agent picks from its tools to search, read files, or change notes and due dates.
5. Audit. Every write is logged with its before and after state.

## Models and cost

- The agent talks to any OpenAI-compatible endpoint. Same code for a local server or a hosted API, set in config.

## Results

- I use it across 6 courses for deadline lookups, studying from slides, and checking drafts against briefs.
- As of early October 2026 it had synced just over 200 course files. I'm the only user, so that number is my course load, not adoption.
- 308 tests pass in CI on every push.
- Retrieval and answer quality: evaluation in progress.

## Next

Finish the retrieval and answer evaluation, then post the numbers here.
