Documentation
FairMark Documentation
Human-in-the-loop AI pre-evaluation for
Canvas,
Moodle, and
Blackboard via LTI 1.3.
Overview
FairMark is a FastAPI service that integrates with your LMS as an LTI 1.3 external tool. When an instructor launches FairMark from within a course, it fetches the student's submission, runs a two-stage GPT-4o evaluation, and presents the AI pre-evaluation for instructor review. The instructor can edit and approve the result, which is then posted back to the LMS gradebook via AGS.
Students never see the AI comment until the instructor approves it. The full evaluation chain — evidence pass, scoring pass, verifier output — is stored and exportable as a PDF audit report.
Quick start
- Copy
.env.production.example to .env.production and fill in your keys
- Generate RSA keys:
bash scripts/generate_keys.sh
- Start the stack:
cd docker && docker compose -f docker-compose.prod.yml up -d
- Register your LMS:
bash scripts/register_platform.sh canvas
- Configure FairMark as an external tool in your LMS (see LTI 1.3 section)
Environment variables
| Variable | Required | Description |
OPENAI_API_KEY | Conditional | OpenAI API key (required unless OPENAI_BASE_URL set) |
OPENAI_BASE_URL | Conditional | OpenAI-compatible endpoint (e.g. Ollama) |
OPENAI_MODEL | — | Default: gpt-4o |
FAIRMARK_ADMIN_SECRET | ✅ | Protects /admin/* endpoints |
FAIRMARK_BASE_URL | ✅ | Public URL of this server |
FAIRMARK_DB_PATH | — | Default: /data/fairmark.db |
CANVAS_BASE_URL | — | Canvas instance URL |
CANVAS_TOKEN | — | Canvas API token |
FAIRMARK_PRIVATE_KEY | — | RSA PEM private key (for Deep Linking JWTs) |
FAIRMARK_RSA_N | — | RSA public key N component (for /lti/jwks) |
LTI 1.3 auth flow
1. LMS → GET/POST /lti/login (OIDC login init with nonce + redirect_uri)
2. FairMark → redirect to LMS auth_login_url
3. LMS → POST /lti/launch (signed id_token JWT)
4. FairMark validates JWT signature against LMS JWKS
5. Instructor sees dashboard / Student sees feedback view
Register FairMark in your LMS using these URLs:
| Setting | Value |
| OIDC Login URL | /lti/login |
| Launch URL | /lti/launch |
| JWKS URL | /lti/jwks |
| Redirect URI | /lti/launch |
| Deep Link URL | /lti/launch |
Required LTI services: Names and Role Provisioning, Assignment and Grade Services, Deep Linking.
Canvas setup
- Admin → Developer Keys → + LTI Key → Method: Manual Entry
- Fill all URLs from the table above
- Enable scopes:
lti-ags/scope/score and lti-nrps/scope/contextmembership.readonly
- Copy the Client ID, then run:
CANVAS_CLIENT_ID=<id> bash scripts/register_platform.sh canvas
Moodle setup
- Site Admin → Plugins → External Tool → Manage Tools → Configure manually
- LTI Version: LTI 1.3, Tool URL:
/lti/launch
- Copy the Client ID, then run:
MOODLE_CLIENT_ID=<id> bash scripts/register_platform.sh moodle
Blackboard setup
- System Admin → LTI Tool Providers → Register LTI 1.3/Advantage Tool
- Note the Application ID provided by Blackboard
- Run:
BB_CLIENT_ID=<id> BB_APP_ID=<app_id> bash scripts/register_platform.sh blackboard
System endpoints
| Method | Path | Description |
| GET | /health | System status, version, registered platforms |
| GET | /api-docs | Interactive Swagger UI |
| GET | /lti/jwks | Tool public JWKS (LMS fetches once on registration) |
Admin endpoints
All admin endpoints require the X-Admin-Secret header matching your FAIRMARK_ADMIN_SECRET env variable.
| Method | Path | Description |
| GET | /admin/platforms | List all registered LMS platforms |
| POST | /admin/platforms | Register a new LMS platform |
| DELETE | /admin/platforms/{id} | Deactivate a platform |
Register platform body
{
"issuer": "https://canvas.instructure.com",
"client_id": "YOUR_CLIENT_ID",
"auth_login_url": "https://canvas.instructure.com/api/lti/authorize_redirect",
"auth_token_url": "https://canvas.instructure.com/login/oauth2/token",
"key_set_url": "https://canvas.instructure.com/api/lti/security/jwks",
"platform_name": "Canvas Production",
"lms_type": "canvas",
"deployment_ids": []
}
Policy endpoints
| Method | Path | Description |
| GET | /admin/policy/{platform_id} | List all policies for a platform |
| POST | /admin/policy/{platform_id} | Set university-level policy |
| POST | /admin/policy/{platform_id}/course/{course_id} | Set course-level policy |
| DELETE | /admin/policy/{policy_id} | Delete a policy |
Assignment config endpoints
| Method | Path | Description |
| POST | /config/assignments | Create assignment config |
| GET | /config/assignments/{id} | Get assignment config |
| POST | /config/assignments/{id}/versions | Create config version |
| GET | /config/assignments/{id}/materials | List material selections |
| POST | /config/assignments/{id}/materials/select | Save material selections |
| POST | /config/assignments/{id}/materials/sync | Sync Canvas materials to snapshots |
Evaluation endpoints
| Method | Path | Description |
| POST | /evaluate | Generate AI pre-evaluation |
| POST | /evaluate/finalize | Instructor approves → post grade to LMS |
| GET | /evaluation/audit | Retrieve persisted stage outputs + lineage |
| GET | /evaluation/report.pdf | Export evaluation PDF with audit appendix |
| POST | /instructor/note | Save instructor note on an evaluation |
| POST | /instructor/reevaluate | Re-run AI evaluation for a submission |
| GET | /instructor/roster | Get course roster with submission status |
LTI endpoints
| Method | Path | Description |
| GET POST | /lti/login | OIDC login initiation (LMS calls this first) |
| POST | /lti/launch | JWT validation + UI render (LMS calls this second) |
| GET | /lti/jwks | Tool public key set |
| POST | /lti/deep-link-response | Build signed Deep Linking response JWT |
Docker deployment
cd docker
docker compose -f docker-compose.prod.yml up -d --build
# View logs
docker compose -f docker-compose.prod.yml logs -f fairmark
# Health check
curl http://localhost:8010/health
The container binds to 127.0.0.1:8010 and expects Caddy or Nginx to proxy HTTPS traffic from the outside.
RSA keys
# Generate key pair
bash scripts/generate_keys.sh
# Extract the N component for JWKS
python3 scripts/extract_jwk_n.py
# Copy FAIRMARK_PRIVATE_KEY and FAIRMARK_RSA_N into .env.production
Keys are stored in the fairmark_keys Docker volume and auto-generated on first startup if not provided.