§ Self-hosting

Run it yourself. The software is free and the docs are the real ones.

An honest overview of the work, not a sales page with the hard parts removed. Comfortable with Cloud Run, Firestore, Canvas Developer Keys, and MDM certificate deployment? You can run this without us.
Open source at release

The shape of a deployment

One container, one database, and a boundary you keep

System of record

Canvas tenant

Browsers navigate hereLTI 1.3 launchConstrained /api/v1 calls
HTTPS ⇄
The service you run

Canvas SEB · Cloud Run

Stateless Node container. Scales horizontally; all state is external.

Firestore — all runtime stateSecret Manager — secretsArtifact Registry — images
Out of band · you keep this

Your MDM & device fleet

Private identity (.p12) — installed here, never extractableApproved SEB client, version-pinned
→ public certificate only

The one line that matters: the private identity is installed by your MDM and never touches the service — the service is given the public certificate and nothing more.

The work

Four things stand between you and a working deployment

Roughly in this order. Step two is the one teams underestimate — it is a device-management project, not a deployment task.

Provision the project

01

Everything is environment-neutral. Choose names, URLs, and database IDs unique to this tenant and environment.

Cloud Run · Build · Artifact RegistrySecret Manager · FirestoreDedicated runtime service accountPublic HTTPS URL

Create the certificate identity

02

Plan this first — it involves your MDM, not your cloud console. The service gets the public cert; the private identity never touches it.

Generate, move private PEM/.p12 to a vaultDeploy .p12 by MDM — non-extractableInject only the public certVerify the published pk-hash

Register with Canvas

03

Two separate Developer Keys — the single most common place a first deployment goes wrong.

LTI 1.3 key from /lti/configAPI OAuth key with the right scopesApp installed at account scopeDetector loader in the theme JS

Verify before you trust it

04

Use separate admin, instructor, and student accounts, and finish on a real SEB client. Answering /health proves very little.

Enable one Classic + one New QuizDetector loads without console errorsProof + code fill on an approved deviceCancel vs submit exit behaviour

The stack

Nothing exotic

One containerised Node.js process and a document store. No queue to operate, no cache to warm, no sticky-session requirement — runtime state lives in Firestore, so the service scales horizontally on its own.

Backend

NestJS on Express

Frontend

React, built with Vite

Runtime

Node.js 24 · Cloud Run Gen2

Store

Google Cloud Firestore

Secrets

Secret Manager

CI/CD

Cloud Build · Dockerfile

Documentation

What ships alongside the code

These are the guides the team actually deploys from — not a marketing artefact written after the fact.

01

Architecture

Runtime design, trust boundaries, data model, route contracts, controls.

02

Canvas setup

Both Developer Keys, installing the app, API access, loading the detector.

03

Configuration

Environment variables, secrets, validation rules, local setup.

04

Deployment

Cloud Run + Firestore provisioning, Build, IAM, TTL, releases, verification.

05

Certificate management

Creating, distributing, rotating, and validating encryption identities.

06

Testing

Automated checks, browser smoke tests, and the manual acceptance sequence.

A note on the boundary. Use a separate service, database, secret set, and LTI registration for every environment that must stay isolated. Pointing a test deployment at production’s database is the failure mode this design exists to prevent — the service refuses USE_IN_MEMORY_STORE=true on Cloud Run for the same reason.

Read all of that and thought “not our team”?

A reasonable conclusion, and exactly what the managed tier is for. Same software, same certificate boundary — we do the provisioning, the upgrades, the rotation planning, and we pick up the phone during an exam window.