# Project export: ConfusionLayer

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: OpenAI Build Week
- Tagline: Teachers unlock AI tutorials, doubt-chat & quizzes chapter-by-chapter as they teach or students self-start any topic solo. AI clears confusion; teachers stay in control of the classroom.
- Devpost: https://devpost.com/software/confusionlayer
- GitHub: https://github.com/AyushPatel2021/confusionlayer
- Demo: https://confusionlayer.znova.in/
- Video: https://www.youtube.com/embed/EOpJ7c6Jfz4?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — Ayush Patel (98 commits)

## Devpost submission (written by the team)

### Inspiration

ConfusionLayer was inspired by a simple problem we kept seeing in classrooms: teachers usually discover confusion too late. A quiz, test, or exam tells them what already went wrong, but by then the class has often moved on. We wanted to build something that helps teachers act earlier. Instead of only tracking marks, ConfusionLayer tracks concept mastery, prerequisite gaps, and confusion risk so a teacher can see what students may struggle with before the next lesson.

### What it does

ConfusionLayer is an AI-powered school learning and operations platform. For students, it provides: Unlocked lesson concepts based on the classroom pace AI-generated explanations, examples, visuals, doubt support, quizzes, and teach-back grading Mastery tracking by concept Confusion Map to show weak and risky areas Exam Outlook and Exam Practice for focused revision Timetable view based on the student’s classroom For teachers, it provides: Student Insights with strengths, weaknesses, mastery, and forecast risk Forecast Brief to predict which upcoming concepts may cause trouble Confusion Brief to summarize classroom-level misconceptions Classroom and curriculum tools For school owners, it adds workspace features like members, roles, classrooms, parent linking, attendance, fees, HR, admissions, and timetables. The goal is not to replace teachers. The goal is to give teachers earlier signals and clearer priorities.

### How we built it

We built ConfusionLayer as a full-stack web app. The frontend is built with Vue and a custom interface designed for schools, teachers, and learners. The backend handles authentication, role-based access, curriculum, mastery tracking, school operations, and AI workflows. We used a structured curriculum model with subjects, chapters, concepts, unlock states, and prerequisite relationships. AI is used on top of that structure for tutorials, doubt responses, quiz feedback, teach-back grading, forecast explanations, and curriculum cleanup from PDFs. The production demo runs with Docker on an Oracle VM, with HTTPS served through nginx and the app split into frontend, backend, and database services.

### Challenges we ran into

One major challenge was making the product feel like a real school system instead of just an AI chatbot. That meant handling roles carefully: student, teacher, parent, school owner, institute owner, and platform admin all needed different access. Another challenge was keeping curriculum behavior realistic. Students should not see everything at once in a classroom setup. Teachers need to unlock chapters and topics step by step, while individual learners need more freedom for self-study. We also had to improve the AI flow so it was not just generic answers. The app needed structured context, clear loading states, safer PDF import, better demo data, and useful visual outputs.

### Accomplishments we're proud of

We are proud that ConfusionLayer became more than a prototype screen. It has working authentication, role-based dashboards, classroom pacing, student progress, teacher insights, forecast briefs, confusion briefs, curriculum import, and school operations. The strongest part is the prediction layer. ConfusionLayer can connect mastery, prerequisite decay, and concept risk into a practical teacher briefing. That makes the product feel useful before failure happens, not only after. We are also proud of the demo experience. A judge can move from student learning to teacher analytics to school owner operations and see one connected product.

### What we learned

We learned that AI in education is much more useful when it is grounded in structure. A chatbot can answer questions, but a school product needs curriculum, roles, progress, history, permissions, and teacher control. We also learned that small product details matter a lot. Things like locked chapters, student timetables, parent linking, clean role names, loading states, and realistic demo data make the difference between a concept and a believable product. Most importantly, we learned that the best use of AI here is not replacing the teacher. It is helping the teacher see earlier, decide faster, and support students better.

### What's next

Next, we want to expand ConfusionLayer with deeper curriculum coverage, richer teacher planning tools, stronger parent reports, and better analytics across classrooms. We also want to improve the AI-generated visuals, add more interactive practice formats, and make the Forecast Brief even more useful for daily lesson planning. Long term, ConfusionLayer can become a complete early-warning layer for schools: helping every teacher know who needs help, what concept is causing confusion, and what to do next.

## README (from the GitHub repository)

# Slate

**Run your school. Clear the confusion before it starts.**

Slate is a multi-tenant school operating system with ConfusionLayer, an AI learning engine that helps teachers and learners catch gaps before they become exam problems. It supports three product shapes on one codebase:

- **Schools:** full workspace with owner, school admin, accountant, HR, teachers, students, parents, admissions, fees, payroll, timetable, attendance, curriculum and learning.
- **Institutes:** teaching and learning workspace with owners, teachers, students, classrooms, curriculum and learner insights.
- **Individual learners:** self-study workspace where a learner can create/import curriculum, unlock all self-study chapters, learn, practice and track progress.

Live demo: https://confusionlayer.znova.in

## Demo Access

Use the **Explore a demo workspace** section on the sign-in page:

- **School Owner** shows the full school ERP and learning stack.
- **Institute Owner** shows institute member/classroom/curriculum management.
- **Individual Student** shows self-study curriculum, learn, progress, confusion map and exam practice.
- **Platform Admin** shows app-owner usage, content, user and audit visibility.

No demo password is needed for those buttons.

## What Makes It Different

Most AI tutor demos generate explanations after a learner asks for help. Slate adds a persistent model around the learner:

- **Curriculum graph:** subjects, chapters, topics and prerequisite edges.
- **Mastery decay:** deterministic effective mastery from quiz, open answer, misconception recurrence and retention signals.
- **Forecast engine:** pure-code prediction of concepts likely to be difficult next, based on prerequisite gaps.
- **Teacher-gated learning:** school/institute students only access what a teacher unlocks for their classroom.
- **Individual self-study:** individual learners manage their own curriculum and all self-study chapters are available to them.
- **Structured AI contracts:** Codex CLI calls GPT-5.6 Luna for tutorials, Socratic doubt chat, quiz diagnosis, teach-back grading and curriculum cleanup.

The AI explains and diagnoses; the system calculates mastery and forecast numbers deterministically.

## Features

- Real email/password auth with JWT cookie sessions.
- Role and segment routing for platform admin, school owner, school admin, accountant, HR, teacher, student and parent.
- School/institute member invitation flow with optional SMTP delivery.
- Owner-only Connect action for demoing member accounts.
- Classroom CRUD, teacher assignment, student enrollment and subject assignment.
- Dynamic curriculum authoring: subject, chapter and topic manager.
- PDF curriculum import: parses in memory, allows Codex cleanup, then saves reviewed structure.
- Learning flow: syllabus, concept detail, tutorial, doubt chat, quiz grading and teach-back grading.
- Student progress, confusion map, exam outlook and exam practice.
- Teacher classroom view, student insights, forecast brief and confusion brief.
- School operations: admissions, fees, ledgers, receipts, HR, payroll, attendance and timetable.
- Parent portal with learner summary, attendance, fees and learning signals.
- Platform admin dashboard for app-wide usage, content, users and audit logs.

## Tech Stack

- **Frontend:** Vue 3, TypeScript, Vite, Pinia, Vue Router, Tailwind, Chart.js, Lucide icons.
- **Backend:** FastAPI, SQLAlchemy, Alembic, pypdf.
- **Database:** PostgreSQL.
- **AI runtime:** Codex CLI with `gpt-5.6-luna`; no OpenAI Platform API key path.
- **Deployment:** Docker Compose on an Oracle VM, public HTTPS handled by nginx reverse proxy.

## Repository Layout

```text
backend/
  app/
    main.py              FastAPI routes and response models
    models.py            SQLAlchemy tables
    auth.py              auth, invitations, demo users
    ai.py                Codex CLI structured-output adapter
    seed.py              rich demo data
    eval/                grader/tutor evaluation harness
  alembic/               database migrations
  tests/                 backend regression tests

frontend/
  src/
    layouts/             public/app/admin shells
    views/               marketing, auth, app, admin screens
    components/          UI, app shell, charts, marketing pieces
    stores/session.ts    Pinia store and API client
    router/index.ts      route guards and page titles
  public/                Slate SVG logo

scripts/
  redeploy.sh            VM redeploy helper

DESIGN.md                visual system and UI rules
PROJECT_OVERVIEW.md      full product and engineering overview
```

## Local Setup

Prerequisites:

- Docker and Docker Compose
- Codex CLI logged in on the host for live AI calls: `codex login`

```bash
cp .env.example .env
docker compose up -d --build
docker compose exec backend alembic upgrade head
docker compose exec backend python -m app.seed
```

Open:

```text
http://localhost
```

Health check:

```bash
curl http://localhost/api/health
```

Reset local demo data intentionally:

```bash
docker compose exec -e CONFUSIONLAYER_ALLOW_DB_RESET=1 backend python -m app.seed --reset-demo
```

## Environment Variables

Copy `.env.example` to `.env`.

| Variable | Purpose |
|---|---|
| `SITE_DOMAIN` | Frontend/Caddy site domain for local compose usage |
| `POSTGRES_DB` | PostgreSQL database name |
| `POSTGRES_USER` | PostgreSQL user |
| `POSTGRES_PASSWORD` | PostgreSQL password |
| `DATABASE_URL` | SQLAlchemy database URL used by backend and Alembic |
| `CODEX_MODEL` | Codex model string, currently `gpt-5.6-luna` |
| `CODEX_TIMEOUT_SECONDS` | Max seconds per Codex subprocess call |
| `AI_DAILY_CALL_LIMIT` | Per-user daily AI call cap |
| `JWT_SECRET` | Long random secret for cookie JWT signing |
| `JWT_EXPIRES_HOURS` | Session lifetime |
| `AUTH_COOKIE_SECURE` | `1` for HTTPS, `0` for local HTTP |
| `SMTP_HOST` | Optional SMTP host for invitation/reset emails |
| `SMTP_PORT` | SMTP port, usually `587` |
| `SMTP_STARTTLS` | `1` to use STARTTLS |
| `SMTP_USERNAME` | Optional SMTP username |
| `SMTP_PASSWORD` | Optional SMTP password |
| `SMTP_FROM` | Sender address for outgoing emails |
| `CONFUSIONLAYER_ALLOW_DB_RESET` | Set to `1` only when intentionally resetting demo data |

Without SMTP settings, emails are logged to backend container logs. This keeps local/demo setup simple while still allowing real invitation email delivery in production.

## Testing

Backend:

```bash
PYTHONPATH=backend python -m pytest backend/tests
```

Frontend:

```bash
cd frontend
npm run build
```

Current verified state before this cleanup:

- Backend tests: `148 passed`
- Frontend production build: passing
- Live smoke: school, institute, individual and platform admin demo paths return `200`

## Deployment

Production runs on the Oracle VM from the git checkout:

```bash
./scripts/redeploy.sh
```

Actual public architecture:

```text
Internet
  -> nginx on VM, HTTPS for confusionlayer.znova.in
  -> frontend container bound on 127.0.0.1:18080
  -> backend container on Docker network
  -> postgres container on Docker network only
```

Production secrets live in the VM `.env` file and are not committed.

## Documentation

- Read `PROJECT_OVERVIEW.md` for the full product, role, architecture, data and demo explanation.
- Read `DESIGN.md` for the visual system.

## Disclaimer

Slate is an independent educational product and is not affiliated with or endorsed by CBSE or NCERT. Curriculum references are used only for educational alignment. Official textbooks remain available through NCERT and ePathshala.


## Detected evidence (automated analysis)

Indexed codebase: 140 recognized source files, 895 KB.
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- Vue (technology) — detected in the code
- Docker (technology) — claimed on Devpost, not found in the code
- OpenAI (technology) — claimed on Devpost, not found in the code
- PostgreSQL (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (120 of 161)

```
.env.example
.gitignore
alembic.ini
backend/.dockerignore
backend/alembic.ini
backend/alembic/env.py
backend/alembic/script.py.mako
backend/alembic/versions/0001_initial_schema.py
backend/alembic/versions/0002_add_users.py
backend/alembic/versions/0003_add_ai_call_usage.py
backend/alembic/versions/0004_add_mastery_history.py
backend/alembic/versions/0005_add_multitenancy.py
backend/alembic/versions/0006_add_user_name.py
backend/alembic/versions/0007_add_subject_org.py
backend/alembic/versions/0008_add_admissions.py
backend/alembic/versions/0009_add_fees.py
backend/alembic/versions/0010_add_hr.py
backend/alembic/versions/0011_add_guardian_links.py
backend/alembic/versions/0012_add_school_operations.py
backend/alembic/versions/0013_add_member_departments.py
backend/alembic/versions/0014_add_core_record_fields.py
backend/alembic/versions/0015_add_invoice_line_items.py
backend/alembic/versions/0016_add_hr_reference_structures.py
backend/app/__init__.py
backend/app/ai.py
backend/app/auth.py
backend/app/briefs.py
backend/app/codex_schemas/confusion_brief.schema.json
backend/app/codex_schemas/curriculum_draft.schema.json
backend/app/codex_schemas/doubt_chat.schema.json
backend/app/codex_schemas/forecast_brief.schema.json
backend/app/codex_schemas/quiz_grade.schema.json
backend/app/codex_schemas/teach_back.schema.json
backend/app/codex_schemas/tutorial.schema.json
backend/app/curriculum.py
backend/app/db.py
backend/app/eval/__init__.py
backend/app/eval/dataset.py
backend/app/eval/run_eval.py
backend/app/forecast.py
backend/app/mail.py
backend/app/main.py
backend/app/make_platform_admin.py
backend/app/mastery.py
backend/app/models.py
backend/app/seed.py
backend/Dockerfile
backend/entrypoint.sh
backend/requirements.txt
backend/tests/test_admissions.py
backend/tests/test_ai.py
backend/tests/test_auth_onboarding.py
backend/tests/test_auth.py
backend/tests/test_briefs.py
backend/tests/test_curriculum.py
backend/tests/test_dashboards_classrooms.py
backend/tests/test_eval_harness.py
backend/tests/test_fees_hr.py
backend/tests/test_forecast.py
backend/tests/test_learning_api.py
backend/tests/test_mastery.py
backend/tests/test_member_departments.py
backend/tests/test_org_billing.py
backend/tests/test_parent_admin.py
backend/tests/test_school_operations.py
backend/tests/test_self_start_progress.py
backend/tests/test_tenancy.py
DESIGN.md
docker-compose.nginx.yml
docker-compose.yml
frontend/.dockerignore
frontend/Caddyfile
frontend/Dockerfile
frontend/index.html
frontend/package.json
frontend/postcss.config.js
frontend/src/App.vue
frontend/src/components/app/AppSidebar.vue
frontend/src/components/ConfusionBrief.vue
frontend/src/components/ForecastBrief.vue
frontend/src/components/marketing/MarketingCta.vue
frontend/src/components/marketing/Marquee.vue
frontend/src/components/marketing/Squiggle.vue
frontend/src/components/Progress.vue
frontend/src/components/SelfStart.vue
frontend/src/components/ui/SAIWorkingState.vue
frontend/src/components/ui/SBadge.vue
frontend/src/components/ui/SButton.vue
frontend/src/components/ui/SCard.vue
frontend/src/components/ui/SChart.vue
frontend/src/components/ui/SCombobox.vue
frontend/src/components/ui/SConfirmDialog.vue
frontend/src/components/ui/SDataTable.vue
frontend/src/components/ui/SDatePicker.vue
frontend/src/components/ui/SDialog.vue
frontend/src/components/ui/SEmptyState.vue
frontend/src/components/ui/SErrorState.vue
frontend/src/components/ui/SFormField.vue
frontend/src/components/ui/SKanbanBoard.vue
frontend/src/components/ui/SLoadingState.vue
frontend/src/components/ui/SMultiSelect.vue
frontend/src/components/ui/SPageHeader.vue
frontend/src/components/ui/SPagination.vue
frontend/src/components/ui/SStatCard.vue
frontend/src/components/ui/STabs.vue
frontend/src/components/ui/SToast.vue
frontend/src/components/ui/STutorialVisual.vue
frontend/src/directives/countup.ts
frontend/src/directives/reveal.ts
frontend/src/layouts/AdminLayout.vue
frontend/src/layouts/AppLayout.vue
frontend/src/layouts/PublicLayout.vue
frontend/src/main.ts
frontend/src/router/index.ts
frontend/src/stores/session.ts
frontend/src/styles.css
frontend/src/views/admin/AdminHomeView.vue
frontend/src/views/app/AccountView.vue
frontend/src/views/app/DashboardView.vue
frontend/src/views/app/family/FamilyView.vue
[41 more files omitted for size]
```

### Dependencies

- backend/requirements.txt: alembic@==1.16.4, fastapi@==0.116.1, psycopg[binary]@==3.2.9, pypdf@==6.5.0, python-multipart@==0.0.20, SQLAlchemy@==2.0.41, uvicorn[standard]@==0.35.0
- frontend/package.json: @lucide/vue@^1.25.0, @vitejs/plugin-vue@^5.2.4, @vue/tsconfig@^0.7.0, autoprefixer@^10.4.21, chart.js@^4.5.1, pinia@^3.0.3, postcss@^8.5.6, tailwindcss@^3.4.17, typescript@^5.8.3, vite@^6.3.5, vue@^3.5.17, vue-router@^4.5.1, vue-tsc@^2.2.12

### Recent commits (newest first)

- Show timetable to enrolled students
- Update Slate logo branding
- Remove codebase memory metadata from repo
- Polish release docs and platform admin
- Unlock self-study chapters for individual learners
- Allow individual learners to manage curriculum
- Fix progress chart render timing
- Improve AI waiting and tutorial visuals
- Enable institute member management
- Use icon-only Slate logo
- Record latest deployed UI commit
- Polish owner navigation and import assignment
- Record production demo reset
- Separate institute owner dashboard
- Align demo school student accounts
- Fix admin endpoints and reset demo seed
- Record latest deployed commit
- Complete school operations and role dashboards
- Complete UI hardening and audit coverage
- Add HR designation and salary structures

## Key source files (fetched from GitHub, selected and truncated for size)

### PROJECT_OVERVIEW.md

```markdown
# Slate Project Overview

This file is the compact source of truth for understanding the product and codebase. Pair it with `DESIGN.md` for visual direction.

## Product Summary

Slate combines a school operating system with ConfusionLayer, a learning engine that predicts and clears confusion before it derails a lesson.

The same codebase supports three paid-product shapes:

- **School:** complete school workspace with ERP modules and learning.
- **Institute:** teaching and learning workspace for tuition/coaching teams.
- **Individual:** self-study workspace for a single learner.

## Core Differentiator

ConfusionLayer is not only a tutorial generator. It stores a curriculum-anchored learner model:

1. Topics live in a subject/chapter/concept graph.
2. Mastery is computed deterministically from attempts, teach-back, misconceptions and retention.
3. Mastery decays over time.
4. Forecast records predict which upcoming concepts are likely to be difficult because prerequisites are weak.
5. AI generates explanations and diagnoses misconceptions, but the system owns the mastery and forecast math.

## User Segments And Roles

### Platform

- **Platform Admin**
  - Sees app-wide workspaces, users, content, usage, revenue signals and audit logs.
  - Does not use school classroom/ERP workflows.

### School

- **School Owner**
  - Full workspace access.
  - Manages members, classrooms, curriculum, admissions, fees, HR, payroll, timetable, attendance and settings.
  - Can connect into active member accounts for demo/support.
- **School Admin**
  - Operational admin access without owner-only billing/member ownership controls.
- **Teacher**
  - Manages assigned classroom learning.
  - Unlocks chapters, views student insights, forecast brief and confusion brief.
- **Student**
  - Sees only own learning workspace.
  - Can access only chapters unlocked for their classroom.
- **Parent**
  - Sees linked child summary, learning progress, attendance and fee signals.
- **Accountant**
  - Fees and account-related workspace.
- **HR**
  - HR/payroll workspace.

### Institute

- **Institute Owner**
  - Manages members, classrooms, curriculum and institute learning.
  - Can invite/connect teacher and student accounts.
- **Institute Teacher**
  - Uses classroom, curriculum and student-insight tools.
- **Institute Student**
  - Uses assigned classroom learning only.

### Individual

- **Individual Student**
  - Creates subjects, chapters and topics.
  - Imports PDFs into a reviewed curriculum tree.
  - All self-study chapters are available; there is no teacher unlock requirement.
  - Uses learn, explore, progress, confusion map, exam outlook and exam practice.

## Major Product Modules

- **Authentication:** email/password signup/login, secure cookie JWT, account profile and password management.
- **Demo access:** sign-in page offers school owner, institute owner, individual learner and platform admin demos.
- **Members:** invitations, role/department assignment, deactivatio
[truncated — 7157 more characters]
```

### DESIGN.md

```markdown
# DESIGN.md - Slate Design System

> **Status: current visual source of truth.**
>
> The palette, type scale, layout rhythm and interaction rules below are the public design
> reference for Slate. They are chosen to feel calm and trustworthy for a teacher/education product,
> deliberately *not* the generic purple-SaaS / default-shadcn look. Anyone can still
> revise; if you change a token here, it changes everywhere by design.
>
> Pair this file with `PROJECT_OVERVIEW.md` for product structure, roles and architecture.

---

## 0. Design intent

**Feeling:** calm, grounded, trustworthy — like a good textbook or a well-run classroom,
not a hype-y AI dashboard. Teachers are the gatekeepers; the product should read as a
serious pedagogical instrument they'd trust with their students.

**Explicitly avoid:**
- Default `indigo-500` / violet primary buttons (the "every AI app" look)
- Inter used flat for everything with no hierarchy
- Cookie-cutter equal card grids with drop shadows everywhere
- Neon gradients, glassmorphism, dark-mode-first "developer tool" vibes

**Lean into:** warm paper-like neutrals, a single confident calm-teal primary, a warm
clay accent used sparingly, generous whitespace, and clear typographic hierarchy over
decoration.

---

## 1. Color palette

Not framework defaults. Warm-neutral "paper & slate" base + calm teal primary + warm
clay accent. Semantic colors are muted, not fire-alarm bright.

### Neutrals — "Paper / Slate" (warm-tinted, not pure gray)
| Token | Hex | Use |
|---|---|---|
| `--paper` | `#FAF8F3` | App background (warm off-white, not `#fff`) |
| `--surface` | `#FFFFFF` | Cards / raised surfaces |
| `--surface-sunken` | `#F1EDE4` | Insets, code blocks, sunken panels |
| `--border` | `#E4DED2` | Hairline borders / dividers |
| `--ink-900` | `#1F2A2E` | Primary text (deep warm slate, not black) |
| `--ink-700` | `#3C4A4F` | Body text |
| `--ink-500` | `#6B7880` | Secondary / muted text |
| `--ink-300` | `#A6AFB4` | Disabled / placeholder |

### Primary — "Calm Teal" (trust, focus)
| Token | Hex | Use |
|---|---|---|
| `--primary-600` | `#0F6E6E` | Primary buttons, active nav, key actions |
| `--primary-500` | `#178A8A` | Hover / lighter primary |
| `--primary-100` | `#D6EAE9` | Primary-tinted backgrounds, selected rows |
| `--primary-050` | `#EBF4F3` | Subtle wash / focus ring bg |

### Accent — "Clay" (warmth, human touch — use sparingly)
| Token | Hex | Use |
|---|---|---|
| `--accent-600` | `#C1592E` | Highlights, teach-back / human moments, small CTAs |
| `--accent-100` | `#F6E3D8` | Accent-tinted backgrounds |

### Semantic (muted, education-appropriate)
| Token | Hex | Meaning |
|---|---|---|
| `--success` | `#2E7D5B` | Mastered / correct (calm green, not lime) |
| `--success-bg` | `#E2EFE8` | |
| `--warning` | `#B8862B` | Predicted confusion / at-risk (amber-gold) |
| `--warning-bg` | `#F6ECD6` | |
| `--danger` | `#B23A3A` | Errors / misconceptions (muted brick, not `#ef4444`) |
| `--danger-bg` | `#F3DEDE` | |

[truncated — 12305 more characters]
```

### docker-compose.yml

```yaml
services:
  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 10s
      timeout: 5s
      retries: 5

  backend:
    build:
      context: ./backend
    restart: unless-stopped
    environment:
      DATABASE_URL: ${DATABASE_URL}
      CODEX_HOME: /root/.codex
      CODEX_MODEL: ${CODEX_MODEL:-gpt-5.6-luna}
      CODEX_TIMEOUT_SECONDS: ${CODEX_TIMEOUT_SECONDS:-90}
      AI_DAILY_CALL_LIMIT: ${AI_DAILY_CALL_LIMIT:-50}
      JWT_SECRET: ${JWT_SECRET}
      JWT_EXPIRES_HOURS: ${JWT_EXPIRES_HOURS:-24}
      AUTH_COOKIE_SECURE: ${AUTH_COOKIE_SECURE:-1}
      SMTP_HOST: ${SMTP_HOST:-}
      SMTP_PORT: ${SMTP_PORT:-587}
      SMTP_STARTTLS: ${SMTP_STARTTLS:-1}
      SMTP_USERNAME: ${SMTP_USERNAME:-}
      SMTP_PASSWORD: ${SMTP_PASSWORD:-}
      SMTP_FROM: ${SMTP_FROM:-no-reply@slate.local}
    volumes:
      - ${HOME}/.codex:/root/.codex
    depends_on:
      postgres:
        condition: service_healthy

  frontend:
    build:
      context: ./frontend
    restart: unless-stopped
    environment:
      SITE_DOMAIN: ${SITE_DOMAIN:-localhost}
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - caddy_data:/data
      - caddy_config:/config
    depends_on:
      - backend

volumes:
  postgres_data:
  caddy_data:
  caddy_config:

```

### backend/requirements.txt

```
fastapi==0.116.1
uvicorn[standard]==0.35.0
psycopg[binary]==3.2.9
SQLAlchemy==2.0.41
alembic==1.16.4
python-multipart==0.0.20
pypdf==6.5.0

```

### frontend/Dockerfile

```
FROM node:22-alpine AS build

WORKDIR /app

COPY package.json package-lock.json* ./
RUN npm install

COPY . .
RUN npm run build

FROM caddy:2.8-alpine

COPY Caddyfile /etc/caddy/Caddyfile
COPY --from=build /app/dist /srv

EXPOSE 80 443


```

### backend/Dockerfile

```
FROM python:3.12-slim

ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1

WORKDIR /app

RUN apt-get update \
    && apt-get install -y --no-install-recommends ca-certificates nodejs npm \
    && npm install -g @openai/codex@0.144.4 \
    && rm -rf /var/lib/apt/lists/*

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY alembic.ini .
COPY alembic ./alembic
COPY app ./app
COPY entrypoint.sh .
RUN chmod +x entrypoint.sh

EXPOSE 8000

# Runs `alembic upgrade head` then uvicorn — migrations apply automatically on deploy.
CMD ["./entrypoint.sh"]

```

### frontend/package.json

```
{
  "name": "confusionlayer-frontend",
  "private": true,
  "version": "0.1.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vue-tsc -b && vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "@lucide/vue": "^1.25.0",
    "@vitejs/plugin-vue": "^5.2.4",
    "chart.js": "^4.5.1",
    "pinia": "^3.0.3",
    "vue": "^3.5.17",
    "vue-router": "^4.5.1"
  },
  "devDependencies": {
    "@vue/tsconfig": "^0.7.0",
    "autoprefixer": "^10.4.21",
    "postcss": "^8.5.6",
    "tailwindcss": "^3.4.17",
    "typescript": "^5.8.3",
    "vite": "^6.3.5",
    "vue-tsc": "^2.2.12"
  }
}

```

### frontend/src/main.ts

```typescript
import { createPinia } from "pinia";
import { createApp } from "vue";

import App from "./App.vue";
import { countup } from "./directives/countup";
import { reveal } from "./directives/reveal";
import { router } from "./router";
import "./styles.css";

createApp(App).use(createPinia()).use(router).directive("reveal", reveal).directive("countup", countup).mount("#app");

```

### frontend/src/router/index.ts

```typescript
import { createRouter, createWebHistory, type RouteRecordRaw } from "vue-router";

import { useSessionStore } from "../stores/session";

const routes: RouteRecordRaw[] = [
  {
    path: "/",
    component: () => import("../layouts/PublicLayout.vue"),
    children: [
      { path: "", name: "landing", component: () => import("../views/marketing/LandingView.vue"), meta: { title: "Home" } },
      { path: "schools", name: "schools", component: () => import("../views/marketing/SchoolsView.vue"), meta: { title: "Schools" } },
      { path: "institutes", name: "institutes", component: () => import("../views/marketing/InstitutesView.vue"), meta: { title: "Institutes" } },
      { path: "students", name: "students", component: () => import("../views/marketing/StudentsView.vue"), meta: { title: "Students" } },
      { path: "pricing", name: "pricing", component: () => import("../views/marketing/PricingView.vue"), meta: { title: "Pricing" } },
      { path: "about", name: "about", component: () => import("../views/marketing/AboutView.vue"), meta: { title: "About" } },
      { path: "login", name: "login", component: () => import("../views/auth/LoginView.vue"), meta: { title: "Sign in", guestOnly: true } },
      { path: "signup", name: "signup", component: () => import("../views/auth/SignupView.vue"), meta: { title: "Get started", guestOnly: true } },
      { path: "forgot-password", name: "forgot-password", component: () => import("../views/auth/ForgotPasswordView.vue"), meta: { title: "Reset password" } },
      { path: "reset-password/:token", name: "reset-password", component: () => import("../views/auth/ResetPasswordView.vue"), meta: { title: "Set password" } },
      { path: "accept-invite/:token", name: "accept-invite", component: () => import("../views/auth/AcceptInviteView.vue"), meta: { title: "Accept invite" } },
    ],
  },
  {
    path: "/app",
    component: () => import("../layouts/AppLayout.vue"),
    meta: { requiresAuth: true, title: "Workspace" },
    children: [
      { path: "", redirect: () => useSessionStore().roleHome },
      { path: "dashboard", name: "dashboard", component: () => import("../views/app/DashboardView.vue"), meta: { title: "Overview", roles: ["owner", "school_admin", "accountant", "hr", "teacher", "student", "parent"] } },
      { path: "account", name: "account", component: () => import("../views/app/AccountView.vue"), meta: { title: "Account", roles: ["owner", "school_admin", "accountant", "hr", "teacher", "student", "parent"] } },
      // Student
      { path: "learn", name: "learn", component: () => import("../views/app/student/LearnView.vue"), meta: { title: "Learn", roles: ["student"] } },
      { path: "learn/:conceptId", name: "concept", component: () => import("../views/app/student/ConceptView.vue"), meta: { title: "Concept", roles: ["student"] } },
      { path: "explore", name: "explore", component: () => import("../views/app/student/ExploreView.vue"), meta: { title: "Explore", roles: ["student"] } },
      { path: "progress", name: "progress", component: () => import("../views/app/student/ProgressView.vue"), meta: { title: "My progress", roles: ["student"] } },
      { path: "timetable", name: "student-timetable", component: () => import("../views/app/teacher/OperationsView.vue"), meta: { title: "Timetable", roles: ["student"], studentSegments: ["school", "institute"] } },
      { path: "map", name: "confusion-map", component: () => import("../views/app/student/ConfusionMapView.vue"), meta: { title: "Confusion map", roles: ["student"] } },
      { path: "exam-outcome", name: "exam-outcome", component: () => import("../views/app/student/ExamOutcomeView.vue"), meta: { title: "Exam outlook", roles: ["student"] } },
      { path: "exam-practice", name: "exam-practice", component: () => import("../views/app/student/ExamPracticeView.vue"), meta: { title: "Exam practice", roles: ["student"] } },
      // Parent
      { path: "family", name: "family", component: () => import("../views/app/family/FamilyView.vue"), meta: { title: "Family", roles: ["parent"] } },
      // Teacher
      { path: "teacher", name: "teacher-classroom", component: () => import("../views/app/teacher/ClassroomView.vue"), meta: { title: "Classroom", roles: ["teacher", "owner", "school_admin"] } },
      { path: "teacher/students", name: "teacher-students", component: () => import("../views/app/teacher/StudentInsightsView.vue"), meta: { title: "Student insights", roles: ["teacher", "owner", "school_admin"] } },
      { path: "students/:studentId", name: "student-report", component: () => import("../views/app/teacher/StudentReportView.vue"), meta: { title: "Student record", roles: ["teacher", "owner", "school_admin", "parent", "student"] } },
      { path: "attendance", name: "attendance", component: () => import("../views/app/teacher/AttendanceView.vue"), meta: { title: "Attendance", roles: ["teacher", "owner", "school_admin"], segments: ["school"] } },
      { path: "operations", name: "operations", component: () => import("../views/app/teacher/OperationsView.vue"), meta: { title: "Timetable", roles: ["owner", "school_admin"], segments: ["school"] } },
      { path: "classrooms", name: "classrooms", component: () => import("../views/app/teacher/ClassroomsView.vue"), meta: { title: "Classrooms", roles: ["owner", "school_admin"] } },
      { path: "curriculum", name: "curriculum", component: () => import("../views/app/teacher/CurriculumView.vue"), meta: { title: "Curriculum", roles: ["teacher", "owner", "school_admin", "student"], studentSegments: ["individual"] } },
      { path: "curriculum/import", name: "curriculum-import", component: () => import("../views/app/teacher/CurriculumImportView.vue"), meta: { title: "Import curriculum", roles: ["teacher", "owner", "school_admin", "student"], studentSegments: ["individual"] } },
      { path: "admissions", name: "admissions", component: () => import("../views/app/teacher/AdmissionsView.vue"), meta: { title: "Admissions", roles: ["owner
[truncated — 3304 more characters]
```

### docker-compose.nginx.yml

```yaml
services:
  frontend:
    ports: !override
      - "127.0.0.1:18080:80"

```

[130 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]