C4C Campus is the public website and full Learning Management System (LMS) for Code for Compassion — a developer training program equipping engineers to build AI tools for animal advocacy, climate action, and AI safety. The same codebase powers the recruitment funnel and the enrolled-student experience. Based in Bengaluru with remote participation worldwide; hard launch target May 2026.
Note
This project is part of the Open Paws ecosystem — AI infrastructure for the animal liberation movement. Explore the full platform →
Browser → Vercel serverless (Astro 6 SSR)
│
├── .astro pages ← server data fetching, layout
├── React islands ← interactive UI (client:only / client:load)
│
└── Supabase (PostgreSQL + Auth + Storage + Realtime)
├── anon key — RLS enforced, client-safe
└── service key — RLS bypassed, server API routes only
git clone https://github.com/Open-Paws/c4c-campus-website.git
cd c4c-campus-website
npm install
cp .env.example .env # fill in SUPABASE and RESEND keys (see Configuration below)
npm run dev # http://localhost:4321Node.js ≥ 22.12.0 and npm 10.x are required (see engines in package.json).
Public recruitment funnel
- Home, About, Programs, Tracks, Framework, FAQ, Pricing pages
- Application form with Supabase-backed storage
- Blog
Authenticated LMS (enrolled students)
- Student dashboard: cohort progress, AI key widget, notifications
- Course browsing and lesson delivery — video via Plyr, rich text via Tiptap
- Assignments: submission, teacher grading, rubric display
- Quizzes: auto-graded, multiple question types (multiple choice, true/false, short answer, essay, multiple select)
- Discussion forums per lesson and per course
- PDF certificate generation
- OpenRouter AI key provisioning per student with configurable weekly spending cap
Teacher portal
- Course and module creation
- Cohort management and scheduling
- Grading workflow and analytics dashboards (D3 / Chart.js)
Admin portal
- Application review and user management
- Platform-wide statistics
Infrastructure
- SSR-only — every page renders on Vercel serverless; no static output
- Stripe payment integration (optional)
- Daily cron for module-unlock notifications (Vercel cron →
/api/cron/) - Husky pre-commit hooks enforcing schema-type sync
| Doc | Description |
|---|---|
| docs/architecture-overview.md | Request pipeline, islands architecture, auth flow |
| docs/DATABASE.md | Schema management, migrations, validation commands |
| docs/data-model.md | All 34 tables, ID types, relationships |
| docs/api-reference.md | 40+ API endpoint reference |
| docs/integrations.md | Supabase, OpenRouter, Resend, Stripe setup |
| .env.example | All environment variables documented inline |
Minimum required variables (copy .env.example → .env):
| Variable | Scope | Purpose |
|---|---|---|
PUBLIC_SUPABASE_URL |
Client + Server | Supabase project URL |
PUBLIC_SUPABASE_ANON_KEY |
Client + Server | Anon key — RLS enforced |
SUPABASE_SERVICE_ROLE_KEY |
Server only | Admin key — bypasses RLS; never expose to client |
RESEND_API_KEY |
Server only | Transactional email |
SITE_URL |
Server | Canonical URL for email links and auth redirects |
Optional integrations: OPENROUTER_MANAGEMENT_KEY (AI key provisioning), STRIPE_* (payments), CRON_SECRET (scheduled tasks).
Variables prefixed PUBLIC_ are embedded in client bundles at build time. All others are server-only.
npm run dev # Dev server with hot reload (port 4321)
npm run build # Production build
npm run build:production # Image optimization + type check + build
npm run preview # Preview production build locally
npm run check # astro check (TypeScript + template types)
npm run test # Vitest unit tests
npm run test:watch # Vitest in watch mode
npm run test:coverage # Unit test coverage report
npm run test:integration # Integration tests against real Supabase DB (sequential)
npm run db:types # Regenerate src/types/generated.ts from local schema
npm run db:validate:all # Schema + field name + case mismatch checks
npm run db:schema-apply # Backup → destructive reset → validate (DESTRUCTIVE)Architecture details
| Layer | Technology |
|---|---|
| Framework | Astro 6 — SSR only (output: 'server') |
| UI islands | React 19 (client:only="react" or client:load) |
| Styling | Tailwind CSS v4 with CSS custom property design system |
| Database | Supabase (PostgreSQL + Auth + Storage + Realtime) |
| Auth | Supabase Auth + independent JWT verification via jose / JWKS |
| Resend | |
| Hosting | Vercel (serverless SSR + cron jobs) |
| Payments | Stripe (optional) |
| AI features | OpenRouter Management API (per-student key provisioning) |
| Testing | Vitest (unit + integration) + Playwright (E2E, 6 configurations) |
- SSR-only — every page renders on Vercel serverless functions; no static output
- Islands architecture —
.astrofiles handle layout and server data fetching; React.tsxislands handle interactivity - Database-driven content — courses, modules, lessons, and cohort schedules live in Supabase, not in Astro content collections
- Schema is immutable —
schema.sqlis the single source of truth; TypeScript types must conform to it, never the reverse - Two Supabase client patterns — anon key (RLS enforced, client-safe) and service role key (RLS bypassed, server API routes only, always after JWT verification)
c4c-campus-website/
├── schema.sql # IMMUTABLE database schema — source of truth
├── astro.config.mjs # Astro + Vercel SSR config
├── vercel.json # Security headers + cron schedule
├── .env.example # All environment variables documented
├── src/
│ ├── middleware/ # Request pipeline: perf → CORS → auth → cache → security
│ ├── lib/ # Shared server/client utilities (auth, Supabase, rate limiting)
│ ├── types/
│ │ ├── generated.ts # AUTO-GENERATED — never edit by hand
│ │ └── index.ts # Application-level types extending generated
│ ├── pages/
│ │ ├── index.astro # Home
│ │ ├── apply.astro # Application form
│ │ ├── dashboard.astro # Student dashboard (auth required)
│ │ ├── courses/ # Course browsing + lesson delivery
│ │ ├── assignments/ # Assignment submission
│ │ ├── quizzes/ # Quiz delivery
│ │ ├── teacher/ # Teacher portal
│ │ ├── admin/ # Admin portal
│ │ └── api/ # 40+ API endpoints (Astro file-based routing)
│ ├── components/
│ │ ├── student/ # Student dashboard widgets (React islands)
│ │ ├── teacher/ # Teacher grading/creation tools
│ │ ├── course/ # Course display components
│ │ ├── analytics/ # D3/Chart.js data visualizations
│ │ ├── search/ # Search UI
│ │ ├── certificates/ # Certificate generation
│ │ └── payments/ # Stripe integration
│ ├── layouts/ # Astro layout templates
│ └── styles/ # Global CSS + Tailwind base
├── tests/
│ ├── unit/ # Vitest + jsdom
│ ├── integration/ # Vitest + real Supabase DB
│ ├── e2e/ # Playwright (6 browser/device configurations)
│ └── security/ # File upload validation, malware scanning
├── migrations/ # Incremental SQL migrations
├── scripts/ # DB validation, type generation, maintenance
└── docs/ # Extended documentation
schema.sql defines 34 tables across these domains:
- Auth & profiles:
applications,profiles,auth_logs - Course structure:
courses,modules,lessons - Cohort system:
cohorts,cohort_enrollments,cohort_schedules,enrollments,lesson_progress - Discussions:
lesson_discussions,course_forums,forum_replies - Assessments:
quizzes,quiz_questions,quiz_attempts,assignments,assignment_rubrics,assignment_submissions - Messaging:
message_threads,messages,notifications,announcements - AI assistant:
ai_conversations,ai_messages,ai_usage_logs - Certificates:
certificates,certificate_templates - Payments:
payments,subscriptions - Media & analytics:
media_library,analytics_events
ID types — confusing these causes silent runtime failures:
| ID type | Tables | TypeScript |
|---|---|---|
| UUID (string) | cohorts, quiz_attempts, assignment_submissions, applications, most junction tables |
string |
| BIGSERIAL (number) | courses, modules, lessons, enrollments, lesson_progress |
number |
Always check schema.sql before writing query code.
- Supabase Auth handles registration, login, password reset, and sessions
- Server-side JWT verification via
jose/ JWKS (src/lib/auth.ts) validates tokens independently on every privileged API route
Roles: admin (/admin/*), teacher (/teacher/*), student (/dashboard, /assignments/*). Stored in profiles.role, enforced by src/middleware/auth.ts.
- Connect the repo to a Vercel project
- Add all required environment variables in the Vercel dashboard
- Deploy — the
@astrojs/verceladapter handles SSR - After first deploy, update the Supabase project's Site URL to match the Vercel domain
vercel.json schedules /api/cron/module-unlock-notifications daily at 06:00 UTC. The endpoint requires CRON_SECRET in the Authorization header.
Read existing code before writing anything. The schema is immutable — schema.sql is the final source of truth and must never be modified; all code adapts to it, not the reverse.
Validation checklist before every commit:
npx astro check # TypeScript + template type check (zero errors required)
npm run db:types:check # Schema-types sync (blocking pre-commit hook)
npm run db:validate:all # All schema checks
npm run test:integration # Integration testsCritical rules:
- Never modify
schema.sql - Never edit
src/types/generated.tsby hand — regenerate vianpm run db:types - Never expose
SUPABASE_SERVICE_ROLE_KEYin client-side code - Always verify JWT before using the service role client in API routes
- Use
snake_casein all database queries —.select('user_id'), never.select('userId') - Don't refactor while fixing bugs — scope creep causes regressions
Language standards: This is software for the animal liberation movement. Use movement terminology precisely: campaign, investigation, activist, farmed animal, factory farm, slaughterhouse, direct action. Run semgrep --config semgrep-no-animal-violence.yaml on all code and docs edits. Avoid speciesist idioms — 60+ patterns are enforced by the no-animal-violence tooling suite.
Quality gate: desloppify scan --path . — minimum score ≥85.
Developers interested in contributing to animal advocacy infrastructure are especially welcome. Open an issue or reach out at info@codeforcompassion.com.
MIT © 2024–2026 Open Paws
See LICENSE for full text.
Acknowledgments: Built by Open Paws volunteers and contributors for the animal liberation movement. Code for Compassion is the developer training program this platform serves.
Donate · Discord · openpaws.ai · Volunteer
