Developer Documentation

WIN Student Goal Tracker

April 2026

1. Project Description

The WIN Student Goal Tracker is a web-based case management system designed to support organizations working with adults with special needs. The platform enables staff to track student goals, document services, and log critical incidents while maintaining strong privacy, auditability, and compliance with FERPA, IDEA, and FAPE.

The system is designed with sustainability in mind, ensuring that future developers can easily maintain, extend, and redeploy the application.

2. Architecture

Technology Stack & Infrastructure

Frontend Angular 20.1.5
Backend .NET 9.0 (C#) with Dapper ORM
Authentication JWT + Refresh Tokens
Database MySQL
Infrastructure Docker + VPS + Traefik

Architecture Description

  1. Presentation Layer (Angular)
  2. Application Layer (.NET API)
  3. Data Layer (MySQL)

Traefik manages routing and HTTPS traffic.

3. Data Flow

Each scenario lists the UI file that initiates the action, the Angular service method called, the API endpoint hit, the .NET controller and repository method invoked, and the stored procedure that reads or writes the database.

1 — User Login

login.html (submit) → Auth.login() [auth.ts] → POST /api/Auth/Login → AuthController.Login() → UserRepository.GetByEmailAsync() + GetProgramsForUserIdAsync() → sp_User_GetByEmail, sp_UserPrograms_GetByUserId → session token + program list → login.html renders program selector

2 — View My Students (Home Dashboard)

home.ts (constructor) → StudentService.getMyStudents() [student.service.ts] → GET /api/Student/my → StudentController.GetMyStudents() → StudentRepository.GetMyStudentsAsync() → sp_Student_GetWithAssignments → student card list → home.html renders student cards

3 — Open Student Workspace (Full Profile)

home.html (card click → router) → workspace.ts (route param change) → StudentService.getFullProfile() [student.service.ts] → GET /api/Student/{id}/full → StudentController.GetFullProfile() → StudentRepository.GetFullProfileAsync() → sp_Student_GetFullProfile → goals + benchmarks + progress events → workspace.html renders detail view

4 — Create Goal

workspace.html (Add Goal button) → goal-modal.ts (onSubmit) → StudentService.createGoal() [student.service.ts] → POST /api/Student/{id}/goals → StudentController.CreateGoal() → PermissionService.IsAllowed() → StudentRepository.InsertGoalAsync() → sp_Goal_Insert → created goal returned → goal-modal closes, workspace.html refreshes

5 — Log Progress Event

workspace.html (Add Event) → edit-event-modal.ts (onSave) → StudentService.addProgressEvent() [student.service.ts] → POST /api/Student/{id}/progress-event → StudentController.AddProgressEvent() → StudentRepository.SaveProgressEventAsync() → sp_ProgressEvent_Save → new event ID returned → workspace.html progress list refreshes

4. Recommended Hosting

  • The entire application is currently hosted online with Hetzner (www.hetzner.com), on a plan donated by team members. The team has committed to hosting for at least the next two years, and if/when that changes, will help the partner transition to the hosting platform of their choice. The team recommends staying with Hetzner based on their reliability and low cost.

5. Application Installation

Prerequisites

Node.js, .NET 9 SDK, MySQL, Docker

Frontend

cd frontend
npm install
npm start

Backend

cd backend
dotnet restore
dotnet run

Database

Create DB, import schema, update .env.

Docker

docker-compose up --build

6. Authentication & Authorization

JWT-based authentication with refresh tokens.

Key files: AuthController.cs, JwtService.cs, middleware

7. Database Backup

Backup

mysqldump -u username -p dbname > backup.sql

Restore

mysql -u username -p dbname < backup.sql

From the UI

Administrators may back up the database via the "Backup" button in the Administrator panel

8. Environment Variables

DB_CONNECTION=...
JWT_SECRET=...
JWT_EXPIRATION=3600

Ensure .env is in .gitignore.

9. Partner Statement

The partner representatives Polly Balsillie and Fred Winter have reviewed the developer documentation and understands its scope and purpose.

Date: April 16, 2026

10. Installation Walkthrough Statement

N/A. An installation walkthrough was not appropriate for the partner, as they are non-technical. They are primarily users of the application, and at least two of our team members will continue to make ourselves available to support the application.

Date: April 16, 2026

11. Performance & UX Analysis

Lighthouse Results

Lighthouse Desktop Results
Desktop
Lighthouse Mobile Results
Mobile

Form Factor Analysis

  • Mobile Portrait: Fully responsive and functional - minimal UI and streamlined functionality for robust field use.
  • Mobile Landscape: Improved readability and layout - same features and user story as mobile portrait
  • Desktop: Full-featured user experience, with application configuration, administrator and edit/delete functionality

UX Observations

Clean navigation with consistent user workflows across devices.

Responsive Accessible High Performance SEO Ready

12. Known Liabilities & Improvements

Issues: Potential performance scaling and mobile optimization opportunities

Improvements: Lazy loading, bundle optimization, responsive enhancements

13. Sustainability Considerations

Docker deployment, very inexpensive hosting, and modular design support long-term maintainability. Addcitionally, the use of free, off-the-shelf technology choices (Angular, C#, MySQL) contribute a sustainable project tech stack.

Appendix A – ERD

The system includes a structured relational database supporting users, roles, permissions, programs, students, goals, progress tracking, and incident logging. The interactive diagram below shows all 17 tables and their relationships. Drag tables to rearrange, scroll to zoom, and hover a table to highlight its connections.

Open full-page ERD

⬡ ERD

Tables 17
Relations 0
Primary Key
Foreign Key
Relationship

Appendix B – Repository Structure

WinStudentGoalTracker/
├── api/                    .NET 9 backend — controllers, services, repositories, Dockerfile
├── db/                     MySQL schema and stored procedures; docker-init scripts for container startup
├── docs/                   HTML documentation (technical spec, user manual, ERD, architecture, API)
├── ui/                     Angular 20 frontend SPA
├── prototype/              Early role-assignment prototypes (not production code)
├── docker-compose.yml      Orchestrates API, Angular/Nginx, MySQL, and Traefik containers
└── WinStudentGoalTracker.sln  Visual Studio solution file

Appendix C – Architecture Overview

High-level diagrams of the system tiers, two-phase auth flow, data model, role hierarchy, deployment, and key design decisions.

Open full-page Architecture Overview
🏗 System Architecture
Client
Angular SPA v20
  • TypeScript 5.8 + SCSS
  • Angular Signals (state)
  • Lazy-loaded routes
  • JWT auth interceptor
  • Proactive token refresh
Desktop Layout
  • Home / Workspace
  • Student detail view
  • Goal & Benchmark modals
  • Progress event modals
  • Report generation UI
  • Admin panel
Mobile Layout
  • Student card list
  • Add progress event
  • Toggle benchmark
Shared Services
  • AuthService (signals)
  • ApiService (HTTP)
  • StudentService
  • AdminService
  • ReportPromptService
  • PlatformService
HTTPS / REST + JWT Bearer — win.opelly.me → winapi.opelly.me
Proxy
Traefik Reverse Proxy
  • win.opelly.me → Angular (Nginx :8006)
  • winapi.opelly.me → API (:8005)
  • Let's Encrypt TLS
  • Gzip compression
  • Security headers
HTTP (internal Docker network)
API
ASP.NET Core 9 C#
  • JWT authentication middleware
  • Swagger / OpenAPI
  • CORS policy
  • Dependency injection
Controllers
  • /api/Auth — login & tokens
  • /api/Student — CRUD
  • /api/Admin — district/users
  • /api/ReportPrompt — prompts
Services
  • TokenService (JWT)
  • PermissionService
  • PasswordHasher (PBKDF2)
  • RecommendationService
  • TranscriptionService
  • ProgressReportBuilder
Repositories Dapper
  • StudentRepository
  • UserRepository
  • AuthRepository
  • AdminRepository
  • ReportPromptRepository
Stored procedures via Dapper — TCP :3309 (Docker internal)
Data
MySQL 8 Docker
  • winstudentgoaltracker DB
  • 17 tables
  • 51 stored procedures
  • Initialised from db/docker-init/
Core Tables
  • user, school_district, program
  • user_program (roles junction)
  • student, user_student
  • goal (recursive), benchmark
  • progress_event + junction
  • refresh_token
Procedure Groups
  • sp_Student_* (CRUD)
  • sp_Goal_* / sp_Benchmark_*
  • sp_ProgressEvent_*
  • sp_RefreshToken_*
  • sp_Program_* / sp_User_*
  • sp_ProgressReport_*
Outbound HTTP from API tier only
External
Ollama LLM AI
  • llm.opelly.me
  • Model: gemma4:e2b
  • Benchmark recommendations
  • 5-min timeout
STT Service AI
  • stt.opelly.me
  • Speech-to-text transcription
  • Progress event dictation
  • 5-min timeout
🔐 Two-Phase Authentication Flow
1
User Submits Credentials email + password
POST /api/Auth/Login
2
Phase 1 Response Session token (5 min)
+ program list
Stored in localStorage
3
User Selects Program session token + program ID
POST /api/Auth/SelectProgram
4
Phase 2 Response JWT (1 min) +
Refresh token (30 days)
JWT includes role + program_id
5
Authenticated Requests Bearer JWT on all API calls
Auth interceptor injects token
6
Proactive Refresh 10 s before JWT expiry:
POST /api/Auth/RefreshToken
Rotated refresh token returned
Token storage: auth_jwt, auth_refresh_token, auth_session_token — all in localStorage.  |  401 interceptor: attempts one silent refresh; if that fails, redirects to /login.  |  Refresh token rotation: each use replaces the old token (tracked via replaced_by_token_id).
🗄 Core Data Model
school_district
PK id_school_district
name
contact_email
Top-level tenant
program
PK id_program
FK id_school_district
name, description
Scope boundary for data
user
PK id_user
email, name
password_hash, password_salt
locked_until
user_program (junction)
PK id_user_program
FK id_user
FK id_program
FK id_role
is_primary, status
student
PK id_student
FK id_program
identifier, program_year
enrollment_date
next_iep_date
user_student (junction)
PK id_user_student
FK id_user
FK id_student
is_primary
goal
PK id_goal
FK id_goal_parent (self-ref)
FK id_student
description, category
baseline, target_completion_date
close_date, achieved
benchmark
PK id_benchmark
FK id_goal
benchmark (description)
short_name
progress_event
PK id_progress_event
FK id_goal
FK id_user_created
content (rich text)
is_sensitive
progress_event_benchmark (junction)
FK id_progress_event
FK id_benchmark
Links events to benchmarks
refresh_token
PK id_refresh_token
FK id_user, id_program
token_hash, token_salt
expires_at, revoked_at
replaced_by_token_id
report_prompt
PK id_report_prompt
FK program_id
report_name
prompt_template (LLM)
Key relationships:  school_district 1→N program  ·  program 1→N student  ·  student 1→N goal (recursive parent/child)  ·  goal 1→N benchmark  ·  goal 1→N progress_event  ·  progress_event M↔N benchmark (junction)  ·  user M↔N program (via user_program + role)  ·  user M↔N student (via user_student)
👥 Role Hierarchy & Permissions
Role Tiers (highest → lowest)
super_admin  ·  full platform access
district_admin  ·  manages programs
program_admin  ·  manages users
teacher  ·  full student CRUD
paraeducator  ·  log events
Sample Permission Matrix
Entity / Action super_admin district_admin program_admin teacher paraeducator
Student — Create Allow Allow Allow Allow Deny
Student — Update Allow Allow Allow Mine Deny
Goal — Create Allow Allow Allow Mine Deny
Goal — Update Allow Allow Allow Mine Deny
ProgressEvent — Create Allow Allow Allow Allow Mine
ProgressEvent — Delete Allow Allow Allow Mine Deny
Program — Create Allow Mine Deny Deny Deny
User — Create Allow Mine Mine Deny Deny
Allow = full access   Mine = own records only   Deny = not permitted
🐳 Deployment & Infrastructure

Docker Containers

  • mysql:8 (port 3309)
  • .NET API (port 8005)
  • Angular / Nginx (port 8006)
  • Traefik reverse proxy

Docker Networks

  • web — external HTTPS routing
  • backend — internal service mesh
  • API depends on MySQL healthcheck
  • UI depends on API (build time)

Environment Config

  • MYSQL_ROOT_PASSWORD
  • MYSQL_DATABASE / USER / PASSWORD
  • JWT_KEY (signing secret)
  • MYSQL_HOST / PORT

Frontend Environments

  • Dev: localhost:5000 (API)
  • Prod: winapi.opelly.me (API)
  • Served by Nginx in production
  • Angular CLI dev server locally

DB Initialisation

  • Schemas from db/docker-init/
  • 51 stored procedures
  • Health check gates API startup
  • No ORM migrations — raw SQL

AI / External Services

  • Ollama LLM — llm.opelly.me
  • STT service — stt.opelly.me
  • Both called from API tier only
  • 5-minute timeout on both
⚙️ Key Design Decisions

Two-Phase JWT Auth

  • Phase 1: credentials → session token
  • Phase 2: program select → scoped JWT
  • Prevents data leakage before program chosen
  • JWT scoped to one program at a time

Refresh Token Rotation

  • New token issued on each refresh
  • Old token replaced (not deleted)
  • Genealogy tracked via replaced_by
  • Prevents replay attacks

Program-Scoped Multi-Tenancy

  • JWT contains program_id claim
  • All queries filtered by program
  • district_admin scoped to district
  • super_admin bypasses scope

Dapper + Stored Procedures

  • No ORM (Entity Framework)
  • Business logic close to data
  • Dapper maps rows to C# objects
  • 51 procedures for full coverage

Angular Signals (not RxJS)

  • Auth state as reactive signals
  • Computed signals derive user context
  • Simpler than Observable chains
  • Auto-update dependent UI

Centralised Permission Matrix

  • Declarative rules per entity + action
  • Allow / MineOnly / Deny granularity
  • Evaluated at request time in API
  • Single source of truth for authz