Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

197 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Classroom J

A self-hosted GitHub Classroom replacement, built around student identity as a first-class concern.

Live: https://classroom.jayenashar.org/ · Licence: MIT · Stack: static SPA + Firestore + Cloud Functions + a GitHub App

Classroom J does the part most courses actually depend on: distributing private assignment repositories from a template to students in a GitHub organisation, individually or in groups, and letting you find any student's repositories afterwards.

It was not built as a response to GitHub Classroom retiring. It was built after a semester in which students kept hitting 500 errors trying to provision their repositories, GitHub support was no help, and there was nothing on my side to fix. The announcement that Classroom shuts down on 28 August 2026 arrived afterwards.

The problem it solves

Every GitHub-based classroom tool has to answer one question: which GitHub account belongs to which enrolled student? Get it wrong and a student's work lands in a repository attributed to someone else, and you find out at marking time.

GitHub Classroom answered it by showing the student a list of everyone in the class and asking them to pick their own name. That fails in three ways every term:

  • A student picks the wrong name, and their repository is now linked to another student's identity.
  • A student has more than one GitHub account (a personal one and a course one is extremely common) and accepts with the wrong one, then has no way to undo it.
  • Nothing maps a student number or an institutional email to the repositories that student owns, so answering "whose repo is this?" is manual work.

The longer version of this argument, and why the fix is an inversion rather than a better roster picker, is at The wrong-account problem.

Classroom J answers it differently:

  • The student proves who they are. They sign in with their institutional address (Google, or a passwordless email link), so their identity comes from your enrolment data, never from a dropdown.
  • The student proves which GitHub account is theirs. They link it through GitHub's own OAuth flow, so the account is verified rather than typed.
  • More than one GitHub account per student is normal, not a mistake. A student who provisions from the wrong account simply provisions again from the right one. Both repositories exist, both are attributed to that student, and you can see both.
  • Lookup is a first-class view. Search by name, student number, email or GitHub login and see that person's linked accounts, groups, and every repository they own.

The class roster: every student with their linked GitHub accounts, student number, and per-assignment provisioning state, filterable and searchable.

The class view. Names and addresses above are invented, not a real cohort.

What it does

  • Classes for a term offering of a course, optionally split per tutor, with enrolment imported from an LMS CSV or edited row by row.
  • Assignments created by deploying a template repository into a class, individual or group, with per-assignment group size limits.
  • Student self-service provisioning. Students provision their own repository whenever they like, with no tutor in the loop. Repositories are private, owned by your org, have forking disabled, and add the student as an outside collaborator, so no org seat is consumed and no student can see another's work.
  • Groups defined by staff per assignment, by CSV round trip or in the browser, many-to-many, with one shared repository per group.
  • Provisioning that survives failure. Provisioning is a queued job, not something a student waits on. The worker claims the repository before it calls GitHub, so a failure leaves a partial record rather than a mystery, and a re-run resumes from it instead of creating a second repository. A student can provision again; staff can repair one repo or re-run a whole class.
  • Bulk provisioning and repair for the students who never got around to it.
  • Lookup across the whole org for staff, with a read-only role derived from GitHub org permissions.
  • Self-service org onboarding. An admin installs the GitHub App on an org they own and the org connects itself. Nobody exchanges a personal access token.

What it deliberately does not do

Stated plainly so you can rule it out fast:

  • No autograding and no score collection. Your template repository can carry whatever GitHub Actions workflow you like and it runs normally on every student repository, but Classroom J does not gather the results or produce a gradebook.
  • No feedback pull requests. Review happens in the repository itself.
  • No LMS grade passback and no LTI. Enrolment comes in as CSV.
  • No deadline enforcement or automatic repository lockdown.

If you need autograding or grade passback, Classroom 50 and Codio both do it and are the right answer for you.

How it compares

Classroom J Classroom 50 Codio GitHub Classroom
Cost Free, you run it Free Free for instructors, institution pays for students Free, retiring 28 Aug 2026
Licence MIT GPL-3.0 Proprietary n/a
Where it runs Your own Firebase project No servers; state lives in GitHub Codio's platform n/a
Student to GitHub mapping Student signs in institutionally, then links their own account by OAuth Teacher enters a GitHub username, or the student completes email onboarding LMS SSO Student picks their own name from a class list
More than one GitHub account per student Yes, by design One per student n/a No
Lookup by name, student number or email Yes Roster view Yes No
Group assignments Yes Yes Yes Yes
Autograding No, bring your own Actions Yes Yes Yes
LMS grade passback No No Yes LTI roster import
Student repositories Private, in your org, forking disabled GitHub repositories Codio plus GitHub GitHub repositories

Other alternatives worth your time, all raised by their authors in the GitHub sunset thread: Classmoji (Dartmouth, template repos plus roster management), QuickFeed (self-hosted, runs tests locally in Docker), Pupilfirst (full LMS with GitHub Actions evaluation), and github-course-repo-assistant (scripts, no platform).

How it works

Browser (static SPA, no build step)
    |
    |-- Firestore, rules-gated reads for org config and job watching
    |-- Cloud Functions callables, one per action, for every write
                |
                |-- GitHub App, per-org installation tokens
  • All writes go through Cloud Functions. A static SPA cannot safely hold credentials that create repositories, and student self-service needs a service that is always on.
  • The GitHub App installation is the root of trust. Roles are derived from real GitHub org permissions: create-repositories means admin, see-all-private-repos means staff. Classroom J maintains no permission grants of its own.
  • Provisioning is a queued job. provisionRepo writes a job document, a Firestore trigger does the GitHub work, and the student watches it to a terminal state. Retries are idempotent, so repair is safe.
  • No dependency on classroom.github.com. Nothing is scraped or imported from GitHub Classroom.

Design detail lives in CONTRACTS.md (schema, callable API, auth model, GitHub wrapper interface) and the SPA notes.

Run it yourself

See docs/DEPLOY.md. In outline: create a Firebase project on the Blaze plan, enable Google and email-link sign-in, register your own GitHub App, put its private key in Secret Manager, set your own superadmin GitHub user id, and deploy. Expect about an hour the first time, most of it in the Firebase and GitHub consoles rather than in code.

Cost for a normal course is dominated by Cloud Functions invocations and Firestore reads, both of which sit inside the Firebase free allowances at classroom scale. The Blaze plan is required because Cloud Functions needs it, not because the usage is expensive.

If you would rather not run it, open an issue and say which organisation you teach in. Hosting another institution is a manual step on my side, so this is a conversation rather than a signup button.

Development

npm install                # also installs functions/
npm run test:unit          # pure unit tests
npm run test:backend       # black-box tests against the Firebase Emulator Suite
npm run test:e2e           # Playwright walkthroughs in Firefox, with video

The offline suites need no GitHub credential. Tests that touch real GitHub are gated behind GITHUB_LIVE=1 and skip otherwise. Firefox is the target browser.

Status

Classroom J is in production for a real cohort: one university course running in 2026 session 2, with 114 enrolled students, 86 of whom have linked a GitHub account, and 83 provisioned assignment repositories.

It has not yet been deployed by anyone other than its author, so the parts most likely to bite you are the ones only a second deployment can find. Treat your first deployment as a pilot, point it at a throwaway GitHub organisation first, and please open an issue when something is wrong or unclear.

Licence

MIT. See LICENSE.

About

A self-hosted GitHub Classroom replacement built around verified student identity. Distributes private assignment repos from a template, individually or in groups.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages