instructure/canvas-lms
The open LMS by Instructure, Inc.
Healthy across the board
worst of 4 axescopyleft license (AGPL-3.0) — review compatibility
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 1w ago
- ✓50+ active contributors
- ✓Distributed ownership (top contributor 13% of recent commits)
Show 4 more →Show less
- ✓AGPL-3.0 licensed
- ✓CI configured
- ✓Tests present
- ⚠AGPL-3.0 is copyleft — check downstream compatibility
What would change the summary?
- →Use as dependency Concerns → Mixed if: relicense under MIT/Apache-2.0 (rare for established libs)
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/instructure/canvas-lms)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/instructure/canvas-lms on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: instructure/canvas-lms
Generated by RepoPilot · 2026-05-10 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/instructure/canvas-lms shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across the board
- Last commit 1w ago
- 50+ active contributors
- Distributed ownership (top contributor 13% of recent commits)
- AGPL-3.0 licensed
- CI configured
- Tests present
- ⚠ AGPL-3.0 is copyleft — check downstream compatibility
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live instructure/canvas-lms
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/instructure/canvas-lms.
What it runs against: a local clone of instructure/canvas-lms — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in instructure/canvas-lms | Confirms the artifact applies here, not a fork |
| 2 | License is still AGPL-3.0 | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 39 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of instructure/canvas-lms. If you don't
# have one yet, run these first:
#
# git clone https://github.com/instructure/canvas-lms.git
# cd canvas-lms
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of instructure/canvas-lms and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "instructure/canvas-lms(\\.git)?\\b" \\
&& ok "origin remote is instructure/canvas-lms" \\
|| miss "origin remote is not instructure/canvas-lms (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(AGPL-3\\.0)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"AGPL-3\\.0\"" package.json 2>/dev/null) \\
&& ok "license is AGPL-3.0" \\
|| miss "license drift — was AGPL-3.0 at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "Gemfile" \\
&& ok "Gemfile" \\
|| miss "missing critical file: Gemfile"
test -f "Rakefile" \\
&& ok "Rakefile" \\
|| miss "missing critical file: Rakefile"
test -f ".github/workflows/claude-code-review.yml" \\
&& ok ".github/workflows/claude-code-review.yml" \\
|| miss "missing critical file: .github/workflows/claude-code-review.yml"
test -f "config/routes.rb" \\
&& ok "config/routes.rb" \\
|| miss "missing critical file: config/routes.rb"
test -f "app/models" \\
&& ok "app/models" \\
|| miss "missing critical file: app/models"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 39 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~9d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/instructure/canvas-lms"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Canvas is an open-source Learning Management System (LMS) built primarily in Ruby on Rails with a modern TypeScript/JavaScript frontend. It provides institutions with a complete platform for course delivery, assignment management, grading, and student engagement, handling the full lifecycle of academic institution operations. Traditional Rails monolith: app/ contains the API and backend (models, controllers, views), config/ holds settings (wt.toml for database config), public/javascripts and app/javascript hold frontend bundles (TypeScript compiled to JS). API-first architecture serves both server-rendered views and the modern TypeScript SPA frontend; migrations in db/migrate/ manage schema evolution.
👥Who it's for
Higher education institutions, K-12 schools, and training organizations who need a self-hosted or cloud-deployed LMS; developers and sysadmins maintaining Canvas deployments; and Instructure partners building integrations via Canvas's LTI and REST API ecosystem.
🌱Maturity & risk
Highly mature production system: 59M lines of Ruby, active CI/CD pipelines (.github/workflows/), comprehensive test infrastructure (.rspec config), and maintained by Instructure Inc. The presence of DevContainer setup (.devcontainer.json) and multi-stage deployment docs (Quick Start, Production Start on wiki) indicates battle-tested, enterprise-grade readiness.
Large monorepo (80M+ lines of code across 6+ languages) creates maintenance complexity and slow test suites; AGPLv3 license requires derivative works be open-sourced; the single primary maintainer (Instructure Inc.) concentration risk means feature velocity and bug fixes depend on corporate resources. Check CODEOWNERS for governance structure.
Active areas of work
Active development visible via .github/workflows/ (claude.yml, frontend.yml for automated review), .claude/ directory for AI-assisted code review agents, ongoing TypeScript migration (28M lines), and RSpec-driven test expansion. Skills in .claude/skills/ (ketchup, squash-migrations, update-gems) show active tooling development for contributor ergonomics.
🚀Get running
Clone with git clone https://github.com/instructure/canvas-lms.git && cd canvas-lms. Dependencies: Ruby (check .nvmrc for version, likely 3.x), Node.js (.nvmrc), PostgreSQL (wt.toml references database config). Install with bundle install && yarn install. For dev setup, see .devcontainer.json for containerized environment or wiki's Quick Start for bare-metal setup.
Daily commands:
Development: rails server (starts on port 3000 by default). Frontend dev: yarn dev or webpack watch (infer from presence of .yarnrc.yml, no Vite config visible). Run tests: rspec or use .claude/skills/rspec/SKILL.md for optimized test runs. Check .github/workflows/frontend.yml for CI test commands (likely yarn test + linting).
🗺️Map of the codebase
Gemfile— Declares all Ruby dependencies and is the primary manifest for the LMS runtime environment.Rakefile— Entry point for build, test, and deployment tasks; defines how the codebase is built and run..github/workflows/claude-code-review.yml— Defines CI/CD pipeline for automated code review and testing on every commit.config/routes.rb— Core Rails routing configuration that maps all HTTP requests to controllers; foundational for request handling.app/models— ORM layer containing all data models that represent Canvas entities (courses, users, assignments, submissions).app/controllers— Request handlers that orchestrate business logic and responses for the LMS API and web interface.db/migrate— Database schema evolution; essential for understanding data structure and deployment prerequisites.
🛠️How to make changes
Add a New API Endpoint
- Create or extend a controller in app/controllers/ to handle the new endpoint logic (
app/controllers/api/v1/example_controller.rb) - Define the route mapping in config/routes.rb under the appropriate namespace (
config/routes.rb) - Add database model and associations in app/models/ if new data is required (
app/models/example.rb) - Create a database migration if schema changes are needed (
db/migrate/[timestamp]_create_examples.rb) - Write RSpec tests in spec/controllers/ or spec/requests/ following test.md guidelines (
spec/requests/api/v1/examples_spec.rb)
Add a Background Job
- Create a new Active Job class in app/jobs/ with perform method (
app/jobs/example_job.rb) - Enqueue the job from controllers or models using ExampleJob.perform_later (
app/controllers/api/v1/example_controller.rb) - Configure job-specific settings in Gemfile.d/jobs_server.rb if queue or retry parameters needed (
Gemfile.d/jobs_server.rb) - Write RSpec tests to verify job behavior in spec/jobs/ (
spec/jobs/example_job_spec.rb)
Add a New Data Model
- Create migration file to add table schema with db:migrate expectations (
db/migrate/[timestamp]_create_examples.rb) - Define ActiveRecord model with associations, validations, and scopes (
app/models/example.rb) - Add relationships and inverse associations to related models in app/models/ (
app/models/course.rb) - Create serializer for API responses in app/serializers/ (
app/serializers/example_serializer.rb) - Write model specs covering validations and associations (
spec/models/example_spec.rb)
Add Frontend UI Component
- Create React component file in app/jsx/components/ or appropriate subdirectory (
app/jsx/components/ExampleComponent.jsx) - Add corresponding ERB template in app/views/ that renders the component (
app/views/examples/show.html.erb) - Style using SCSS following Canvas conventions in app/stylesheets/ (
app/stylesheets/components/example.scss) - Write Jest tests in tests/ alongside the component (
app/jsx/components/__tests__/ExampleComponent.test.jsx)
🔧Why these technologies
- Ruby on Rails — Provides rapid development with convention-over-configuration; mature ecosystem for educational platforms; built-in ORM (ActiveRecord), routing, and test support.
- PostgreSQL (Gemfile.d/postgres.rb) — Reliable, ACID-compliant relational database required for LMS data consistency; supports complex queries for reporting and enrollment management.
- Redis (Gemfile.d/redis.rb) — In-memory cache for session storage, real-time features, and job queue backend; reduces database load for high-traffic read patterns.
- Active Job + Background Processing — Decouples long-running tasks (email notifications, grade calculations, exports) from request-response cycle; improves UI responsiveness.
- React + ERB Templates — Hybrid rendering: server-rendered views for traditional pages and React components for interactive UI; balances SEO and rich interactivity.
- Docker Containerization — Ensures reproducible deployments across development, testing, and production; simplifies dependency management and scaling.
⚖️Trade-offs already made
-
Monolithic Rails application vs. microservices
- Why: Monolith reduces operational complexity for educational institutions; easier to deploy, monitor, and maintain small-to-medium installations.
- Consequence: Scaling requires vertical scaling or careful service extraction; tight coupling between features can slow large feature development.
-
Mixed server-side rendering (ERB) + client-side (React)
- Why: Supports both traditional page-based workflows (admin dashboards) and modern interactive UIs (student interfaces); gradual migration path.
- Consequence: Increased JavaScript bundle size; dual state management in some pages; complexity in keeping server and client views in sync.
-
PostgreSQL as single source of truth
- Why: Simplifies data consistency and ACID guarantees required for grades, enrollment, and financial records.
- Consequence: Database becomes bottleneck at extreme scale; requires careful indexing and query optimization; read replicas needed for analytics.
-
Open-source AGPLv3 licensing
- Why: Ensures community contributions flow back to project; transparent for educational users; aligns with academic values.
- Consequence: Restrictive for proprietary derivatives; can complicate commercial integrations; requires contributor awareness of license obligations.
🚫Non-goals (don't propose these)
- Real-time synchronization for simultaneous multi-user editing (eventual consistency model used)
- Video hosting or streaming (integrates with external services like Kaltura)
- Mobile-native applications (responsive web design is primary mobile strategy)
- Machine learning or AI-driven personalization (focus on core LMS functionality; integrations via plugins/APIs)
🪤Traps & gotchas
- Database: Canvas uses significant custom SQL and raw ActiveRecord queries; migrations must handle large data sets and include rollback logic. 2) Background jobs: likely using Delayed::Job (search for DJ) or Sidekiq; changes must account for queued/in-flight jobs. 3) Multi-tenancy: Canvas supports multiple institutions; changes must respect account isolation and permissions. 4) LTI integrations: breaking API changes will break partner tools; API versioning is critical. 5) i18n strings: changes to user-facing text must update .i18nrc translations; missing strings break in non-English locales. 6) Docker + container config: wt.toml and .devcontainer.json suggest required Docker setup; native development may need PostgreSQL, Redis manually configured.
🏗️Architecture
💡Concepts to learn
- LTI (Learning Tools Interoperability) 1.3 — Canvas's key integration standard; allows third-party tools (Zoom, Turnitin, etc.) to embed in courses securely. Understanding LTI flows is essential for API design decisions.
- Multi-tenancy with Account Isolation — Canvas hosts multiple institutions in one deployment; database queries and permissions must respect account boundaries. Critical for understanding data privacy and schema design.
- ActiveRecord Preloading (N+1 Query Optimization) — Large Rails codebases suffer N+1 query bugs; Canvas's 59M lines likely has many preload() and includes() calls. Essential for performance debugging.
- Delayed Job / Background Job Queuing — Grade calculations, email notifications, and SIS syncs run asynchronously; understanding job lifecycle prevents data loss and debugging issues.
- Rails Migrations and Schema Versioning — Canvas handles migrations for large production datasets (millions of enrollments); safe migration patterns (no locking DDL, data migration stages) are non-negotiable.
- REST API Versioning (Canvas API v1) — Canvas exposes /api/v1/ endpoints used by mobile apps and partners; API versioning strategy prevents breaking integrations and is visible in code organization.
- Role-Based Access Control (RBAC) with Permissions — Canvas has complex user roles (admin, teacher, student, observer) with granular permissions; authorization checks permeate the codebase and must be understood for any feature work.
🔗Related repos
moodle/moodle— Largest open-source LMS competitor; direct functional equivalent (Moodle is PHP-based, Canvas is Rails)openedx/edx-platform— Open EdX is alternative open-source LMS/MOOC platform backed by 2U; similar scale and complexitylti-advantage-gem/lti_advantage— Ruby gem for LTI 1.3 integration; Canvas embeds LTI support and would use or mirror this patterninstructure/instructure-ui— Component library by Instructure; Canvas frontend likely depends on or mirrors patterns from this UI libraryinstructure/sis-sync— Sister project for SIS (Student Information System) integration; Canvas users rely on SIS connectors for roster sync
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Create comprehensive test coverage for .claude/skills directory execution
The repo has a sophisticated Claude skills system (.claude/skills/) with multiple tools like squash-migrations, update-gems, ketchup, and create_commit_msg, but there's no visible test suite validating these scripts work correctly. New contributors could add integration tests (likely bash/shell tests) to ensure these critical developer tools don't break during refactoring.
- [ ] Create .claude/skills/tests/ directory with test scripts
- [ ] Add tests for each SKILL.md workflow (validate squash-migrations.sh, get-diffs.sh, etc.)
- [ ] Add CI workflow in .github/workflows/ to run skill tests on PRs
- [ ] Document test execution in CONTRIBUTING.md
Implement Claude code review workflow validation and documentation
The repo has .github/workflows/claude-code-review.yml and .github/workflows/claude.yml plus AGENTS.md and CLAUDE.md files, but there's no clear documentation on how the migration-reviewer.md agent works or how to test Claude automation changes locally. A new contributor could document the agent system and add validation scripts.
- [ ] Expand AGENTS.md with specific agent configuration examples and testing instructions
- [ ] Create .claude/agents/test-harness.sh to locally validate agent behavior before merging
- [ ] Document in CONTRIBUTING.md the process for safely modifying Claude workflows
- [ ] Add validation to .lintstagedrc.js or .github/workflows to check AGENTS.md syntax
Add missing linter configurations for Dockerfile consistency
The repo has 11+ Dockerfiles (.groovylintrc.json handles Groovy), but no Docker linting configuration. With multiple Jenkins-specific Dockerfiles (Dockerfile.jenkins*) that need consistency, a new contributor could add Hadolint configuration and a GitHub Action to catch Docker best-practice violations.
- [ ] Create .hadolint.yaml with Canvas-specific rules for Docker image builds
- [ ] Add hadolint GitHub Action in .github/workflows/ to lint all Dockerfile* files
- [ ] Document Docker build guidelines in CONTRIBUTING.md
- [ ] Fix any existing violations found by the linter in Dockerfile and Dockerfile.* files
🌿Good first issues
- Add RSpec test coverage for app/models/ files that lack test files (scan spec/ for gaps in model coverage): Core business logic with incomplete tests reduces safety; good way to learn the domain
- Audit TypeScript in app/javascript/ for missing JSDoc comments or type annotations (look for 'any' types and untyped functions): 28M lines of TS is large; improving type safety prevents frontend bugs and helps onboarding
- Document one complex API endpoint (pick a high-traffic one like /api/v1/courses/{id}) with request/response examples in the wiki or generated OpenAPI spec: LMS has powerful API but documentation gaps exist; chosen endpoint teaches integration patterns
⭐Top contributors
Click to expand
Top contributors
- @Jenkins — 13 commits
- @ericsaupe — 6 commits
- @Gazuru — 4 commits
- @aaronshaf — 4 commits
- @viktor-duka-inst — 4 commits
📝Recent commits
Click to expand
Recent commits
1c9f0bb— update ko translation (Jenkins)06213fa— update ko translation (Jenkins)b337fb4— link caption status text to language name via aria-describedby (patrikgerzsenyi)6c9b890— Fix text overlap in rubric InstructorScore label (hossamg-122)d7328a4— Fix courseProgression null in multi-course query (Gazuru)512ad62— spec: add e2e tests for low contrast a11y checker rule (AdamJelenszky)3c67cd1— Revert "use permission system + include[] in a11y APIs" (fmarcso)1ec91e5— add grade post policy tray to individual gradebook (hassuunna)436d7c3— use permission system + include[] in a11y APIs (fmarcso)318f2ad— Add size_bytes field to GraphQL File type (Ádám Máté)
🔒Security observations
The Canvas LMS codebase shows a moderate security posture based on the provided infrastructure configuration. Key strengths include a documented vulnerability disclosure policy and private bug bounty program. Primary concerns include: permissive upload size limits that could enable DoS attacks, absence of visible security headers configuration, reliance on auto-generated Dockerfiles which increases maintenance risk, and lack of pinned dependency versions. The application uses modern frameworks (Rails 8.0, Ruby 3.4) but requires validation of dependency management practices, input validation/sanitization patterns, and authentication mechanisms which are not visible in the provided file structure. Recommend comprehensive SAST scanning, dependency vulnerability auditing (Bundler Audit, Dependabot), and security-focused code review processes for database queries and user input handling.
- Medium · Docker Image Based on Older Ubuntu Release —
Dockerfile (line: FROM instructure/ruby-passenger:$RUBY-jammy). The Dockerfile uses 'jammy' (Ubuntu 22.04) as the base image. While not immediately critical, this should be regularly updated to ensure the latest security patches from the base image are included. The Ruby version (3.4) should also be monitored for EOL status. Fix: Establish a policy to regularly update base Docker images. Consider using a more recent Ubuntu LTS release or implement automated dependency scanning (e.g., Dependabot, Trivy) to detect vulnerabilities in base images. - Medium · Large Upload Size Configuration —
Dockerfile (ENV NGINX_MAX_UPLOAD_SIZE 10g). NGINX_MAX_UPLOAD_SIZE is set to 10GB, which is very permissive. This could potentially be abused for denial-of-service attacks or to upload large malicious files if upload validation is inadequate. Fix: Evaluate if a 10GB upload limit is necessary. Implement stricter limits based on actual requirements. Add validation on file type, size, and content at the application level. Monitor upload usage patterns. - Medium · Missing Security Headers Configuration —
Dockerfile / Application Configuration. No visible security headers configuration (Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, etc.) in the provided Dockerfile or configuration files. This leaves the application vulnerable to XSS and clickjacking attacks. Fix: Implement comprehensive security headers in the Nginx/Rails configuration. Ensure CSP, HSTS, X-Frame-Options, X-Content-Type-Options, and X-XSS-Protection headers are properly configured. - Low · Development Environment Configuration in Docker —
Dockerfile (ENV RAILS_ENV development). The Dockerfile explicitly sets RAILS_ENV=development, which may contain debug information and less strict error handling. Ensure this is not used in production deployments. Fix: Verify that production deployments use RAILS_ENV=production. Implement build-time validation to prevent development configurations in production Docker images. - Low · Generated Dockerfile Without Source Control Protection —
Dockerfile (header comment). The Dockerfile header states 'GENERATED FILE, DO NOT MODIFY' and references a generation script (build/dockerfile_writer.rb). Changes to the generator script could inadvertently introduce security issues. Fix: Implement code review requirements for the Dockerfile generation script. Maintain version control of the template and generation tool. Add security scanning to the generation process. - Low · Ruby Version Management —
Dockerfile (ARG RUBY=3.4). Ruby 3.4 is specified but no pinned patch version is used (e.g., 3.4.0). This means any 3.4.x patch could be pulled, potentially including those with unvetted security updates. Fix: Pin Ruby to a specific patch version (e.g., 3.4.0). Implement a process for regularly reviewing and updating Ruby versions with security patches.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.