geocrop-platform./apps/nextgen/AGENTS.md

590 lines
38 KiB
Markdown

# AGENTS.md — Africa Alert PWA
> **Audience.** This file is the single source of truth for any AI coding agent (or human contributor) working in this repository. Read it end-to-end before making changes.
>
> **Status.** Living document. Update it in the same PR that changes the architecture, the coding standards, or the team-agent roster.
---
## 1. Project Overview
### 1.1 Purpose
**Africa Alert** is a Progressive Web App (PWA) school-management system for African schools. It unifies the four stakeholders of a school — **administrators, teachers, students, and parents** — into one role-aware interface, and ships with offline support so it works in low-connectivity environments (the primary deployment context is Zimbabwe, based on the Paynow payment-gateway integration at `https://www.paynow.co.zw`).
### 1.2 Key Business Goals
| Goal | What it means in code |
|---|---|
| **Unified school operations** | One app for student records, classes, attendance, fees, messaging, calendar, exams, payroll, inventory, hostel, transport, and front-office. |
| **Offline-first PWA** | The React client must install and run without a network. A local SQLite mirror keeps working data available offline; a sync engine reconciles to Supabase when online. |
| **Role-based experience** | Four user roles (admin / teacher / student / parent) with distinct dashboards, navigation, and permissions. |
| **Cashless fee collection** | Integration with **Paynow Zimbabwe** (Ecocash, Visa, etc.) so parents can pay fees online. |
| **Easy deployment** | One-command `docker-compose up` for non-technical school operators. |
### 1.3 High-Level Architecture
```
┌─────────────────────────────────────────────────────────────────────┐
│ Africa Alert System │
└─────────────────────────────────────────────────────────────────────┘
Browser / Mobile PWA
┌──────────────────────────────────────────┐
│ React 18 + Vite + TypeScript │
│ - React Router 6 (role-gated routes) │
│ - Zustand stores (auth, api, exams, …) │
│ - Axios HTTP client │
│ - vite-plugin-pwa (service worker) │
│ - Recharts, Lucide icons │
└──────────────┬───────────────────────────┘
│ /api/* (Vite dev proxy :3001)
┌──────────────────────────────────────────┐ ┌─────────────────────┐
│ Node.js 20 + Express API (port 3001) │ sync │ Supabase (Postgres)│
│ - JWT auth (jsonwebtoken) │ ◀────▶ │ api.next_gen. │
│ - bcrypt password hashing │ 30 s │ techarvest.co.zw │
│ - 12 controllers (one per module) │ └─────────────────────┘
│ - SQLite (better-sqlite3) local mirror │ ▲
│ - SyncEngine (push/pull, server-wins) │ ───────────────┘
│ - Paynow integration
│ - Multer file uploads
└──────────────────────────────────────────┘
./data/school.db (SQLite, persistent volume in Docker)
All in one container via multi-stage Dockerfile + docker-compose.
```
**Architectural style.** A classic **three-tier monolith** (React SPA → Express REST → SQLite) with a **bidirectional sync adapter** to a managed Postgres (Supabase) for backup and cross-device replication. There is no microservice decomposition; modules are Express `Router()` instances mounted under `/api/<module>`.
---
## 2. Technology Stack
### 2.1 Frontend (`client/`)
- **Language:** TypeScript 5.3+
- **Framework:** React 18.2 (function components + hooks)
- **Build tool:** Vite 5 (`vite.config.ts`)
- **Routing:** react-router-dom 6
- **State management:** Zustand 4 (with `persist` middleware for auth)
- **HTTP client:** Axios 1
- **Charts:** Recharts 2
- **Icons:** lucide-react
- **PWA:** vite-plugin-pwa (Workbox runtime caching, autoUpdate)
### 2.2 Backend (`server/`)
- **Runtime:** Node.js 20 (Alpine in Docker)
- **Framework:** Express 4
- **Storage:** SQLite via `better-sqlite3` 12 (WAL mode, `foreign_keys = ON`)
- **Auth:** `jsonwebtoken` 9 (HS256, 7-day expiry) + `bcryptjs` 2
- **CORS:** `cors` 2 (currently wide-open; see §6 Security)
- **Uploads:** `multer` 1.4
- **Cloud sync:** `@supabase/supabase-js` 2
- **IDs:** `uuid` 9 (for sync `uid` columns)
- **Dev loop:** `nodemon` 3
### 2.3 Database
- **Primary (local):** SQLite (single file at `server/data/school.db`, mode WAL).
- **Replica (cloud):** Supabase Postgres. Push/pull through the Supabase REST API.
- **Schema source of truth:** `server/src/database/init.js` (CREATE TABLE IF NOT EXISTS — see §10 for migration plan).
- **Soft-delete + sync metadata on every table:**
```sql
last_synced_at DATETIME,
sync_status TEXT DEFAULT 'pending' CHECK(sync_status IN ('synced','pending','conflict')),
is_deleted INTEGER DEFAULT 0
```
### 2.4 Infrastructure
- **Containerization:** Docker (multi-stage `Dockerfile``client-build``server-build``production`) + Docker Compose v3.8.
- **Base image:** `node:20-alpine`.
- **Persistent volumes:** `./data` (SQLite) and `./uploads` (file uploads).
- **Ports:** 3000 (static client via `npx serve`), 3001 (API).
- **Entrypoint:** `docker-entrypoint.sh` — runs DB init on first boot, then launches API + static server.
### 2.5 DevOps
- **CI/CD:** *None configured.* Recommended: GitHub Actions — see §13.
- **Backups:** Manual `cp -r data data-backup-…` (documented in `DOCKER_README.md`). No automation.
- **Monitoring / health check:** *Not implemented.* Recommended: `/api/health` endpoint, see §13.
- **Secrets management:** Currently via `docker-compose.yml` env block (with a **hardcoded fallback secret** — see §6). Recommended: Docker secrets or a `.env` file with `dotenv` (NOT committed).
### 2.6 AI/ML Components
- *None.* No ML models, embeddings, or AI services are integrated. The system is purely transactional. If you add AI features (e.g. attendance anomaly detection, grade prediction), add a new sub-section here.
---
## 3. Team AI Agents
These are the persistent agent roles this repo expects AI agents to assume. Each role is scoped so two agents can work in parallel without stepping on each other.
### 3.1 Architect Agent
**Owns:** `client/src/App.tsx`, `client/vite.config.ts`, `server/src/index.js`, `docker-compose.yml`, `Dockerfile`, `docker-entrypoint.sh`, `AGENTS.md`.
**Responsibilities:**
- System architecture and module boundaries.
- Scalability (SQLite → Postgres migration, sync sharding, multi-tenant tenancy).
- Security review (authn, authz, secrets, CORS, rate limiting, payload validation).
- Design decisions and ADRs (architecture-decision records — see §11).
- Cross-cutting concerns: error handling, logging, observability.
**Out of scope:** Implementing feature CRUD — that's the Backend / Frontend agents.
### 3.2 Backend Agent
**Owns:** `server/src/**` (controllers, services, middleware, db), `server/package.json`.
**Responsibilities:**
- Express API development.
- Business logic, validation, transactions.
- Database schema and migrations (when extracted from `init.js`).
- Sync engine correctness (push/pull ordering, conflict resolution).
- Backend testing (Jest / Vitest + supertest, once set up — see §13).
- Paynow integration and webhook security.
**Out of scope:** React UI, deployment infrastructure.
### 3.3 Frontend Agent
**Owns:** `client/src/**` (components, pages, stores, hooks), `client/package.json`, `client/vite.config.ts`, PWA manifest, service-worker config.
**Responsibilities:**
- UI / UX implementation.
- Accessibility (WCAG 2.1 AA — semantic HTML, focus management, ARIA only when needed).
- State management (Zustand stores) and API caching.
- Offline / service-worker behavior (Workbox strategies, runtime caching).
- Frontend testing (Vitest + React Testing Library, once set up — see §13).
- Form validation and user-facing error handling.
**Out of scope:** Server endpoints, DB schema.
### 3.4 DevOps Agent
**Owns:** `Dockerfile`, `docker-compose.yml`, `docker-entrypoint.sh`, `.github/` or `.gitlab/` (when created), CI/CD pipelines, backup scripts, deployment docs.
**Responsibilities:**
- CI/CD pipelines (lint → typecheck → test → build → deploy).
- Container hardening (non-root user, healthcheck, multi-stage size optimization).
- Infrastructure-as-code (when promoted beyond Docker Compose).
- Monitoring / observability (logs, metrics, traces).
- Backups (automated SQLite snapshots + restore drills).
- Secrets management (`.env`, Docker secrets, vault).
**Out of scope:** Application logic.
### 3.5 QA Agent
**Owns:** `tests/` (once created), test plans, regression suites, performance baselines.
**Responsibilities:**
- Test plans for each user role (admin / teacher / student / parent).
- Automated test suite (unit + integration + e2e — Playwright recommended for e2e).
- Regression testing on every PR.
- Performance testing (especially the exam timer and bulk attendance endpoints).
- Accessibility audits (axe-core, manual screen-reader checks).
**Out of scope:** Writing new feature code; QA flags defects, the owning agent fixes them.
### 3.6 Documentation Agent
**Owns:** `README.md`, `DOCKER_README.md`, `IMPLEMENTATION_SUMMARY.md`, `SCREENS.md`, this file, OpenAPI/Swagger specs (when added), ADRs in `docs/adr/`.
**Responsibilities:**
- Keep `README.md` in sync with the running app.
- Maintain `SCREENS.md` as the canonical UI map (it currently lists screens and the permissions matrix — keep that current as features land).
- Generate / curate API documentation (OpenAPI from route comments or a `swagger-jsdoc` setup).
- Architecture diagrams (use Mermaid in `.md` files — they render in GitHub / GitLab).
- Runbooks for common operations (DB reset, sync repair, backup restore).
**Out of scope:** Writing the code that the docs describe; that is owned by the implementing agent.
---
## 4. Coding Standards
### 4.1 Naming Conventions
| Layer | Convention | Example |
|---|---|---|
| React components | PascalCase, file matches export | `AdminDashboard.tsx` exports `AdminDashboard` |
| Hooks | `useX` camelCase | `useAuthStore` |
| Zustand stores | camelCase, `use` prefix | `useExamsStore` |
| TypeScript types / interfaces | PascalCase | `interface User { … }` |
| Express controllers | camelCase + `.controller.js` | `exams.controller.js` |
| Express services | camelCase + `.service.js` | `syncEngine.js` (existing convention) — keep aligned |
| Database columns | snake_case | `first_name`, `sync_status` |
| Environment variables | SCREAMING_SNAKE_CASE | `JWT_SECRET`, `SUPABASE_URL` |
| Branches | `feature/<kebab>` or `fix/<kebab>` | `feature/online-exams`, `fix/login-redirect` |
### 4.2 Folder Organization
- All **backend** code under `server/src/`. Subfolders: `controllers/`, `services/`, `database/`, `middleware/`, `routes/` (when split out).
- All **frontend** code under `client/src/`. Subfolders: `components/`, `pages/`, `store/`, `hooks/`, `utils/`, `services/`.
- **Role-specific** pages live under `client/src/pages/<role>/` (admin / teacher / student / parent). Cross-role pages stay at `client/src/pages/`.
- **Shared modules** (e.g. an `Avatar` used by every role) go in `client/src/components/`, never in a role subfolder.
- **Database artifacts** (`init.js`, future migrations) live in `server/src/database/`. Do not scatter SQL across controllers.
### 4.3 Commit Message Format
Use **Conventional Commits**:
```
<type>(<scope>): <short imperative summary>
<body — explain WHY, not what>
<footer — breaking changes, issue refs>
```
- Types: `feat`, `fix`, `refactor`, `docs`, `test`, `chore`, `ci`, `perf`, `build`.
- Scope examples: `client`, `server`, `db`, `docker`, `auth`, `exams`, `paynow`, `sync`.
- Example: `feat(exams): auto-grade multiple-choice on submit`.
### 4.4 Documentation Requirements
- Every **public module** (controller, store, hook, component used across roles) gets a JSDoc / TSDoc header explaining its purpose and one example.
- Every **non-trivial function** gets a short JSDoc with `@param` / `@returns` for edge cases.
- Every **API route** is documented in `docs/API.md` (or via generated OpenAPI — see §13).
- Every **architectural decision** gets an ADR in `docs/adr/NNNN-title.md` (use `docs/adr/0001-record-architecture-decisions.md` as the template).
### 4.5 Testing Requirements
- **Every new controller** has at least one integration test (supertest) covering the happy path and one error case.
- **Every new store** has a unit test for each action.
- **Every new page** has a smoke test (renders without crashing) and an interaction test for the primary user flow.
- **Bug fixes** add a regression test that fails before the fix and passes after.
- Coverage gate: target **≥ 70 %** lines on the backend, **≥ 60 %** on the frontend (raise over time — see §13).
---
## 5. Pull Request Rules
A PR is mergeable **only** when **all** of the following are true. The Author is responsible for self-checking; the Reviewer verifies.
### 5.1 Review Requirements
- **Minimum 1 reviewer** for changes under 200 lines, **2 reviewers** for ≥ 200 lines or any change touching `server/src/index.js`, `client/src/App.tsx`, `Dockerfile`, `docker-compose.yml`, or `server/src/database/init.js`.
- The owning agent (per §3) must approve changes in their owned area.
- The **Architect agent** must approve any change that crosses module boundaries or modifies the auth / sync layer.
### 5.2 Testing Requirements
- All **new and modified** code paths are covered by tests (see §4.5).
- The full test suite passes locally AND in CI.
- No tests are skipped or `.only` left in.
### 5.3 Security Checks
- No secrets in code or committed `.env` files. Verify with `git diff --staged` before pushing.
- Any new dependency has been audited (`npm audit --omit=dev` clean; criticals = 0).
- Any new endpoint goes through `auth` and (where applicable) role-check middleware.
- User-supplied input is validated; raw SQL is parameterized (it already is in this repo — keep that standard).
- Webhook handlers (Paynow, future Supabase webhooks) verify signatures.
### 5.4 Documentation Checks
- `README.md` and `SCREENS.md` updated if the user-visible behaviour changes.
- This `AGENTS.md` updated if the architecture, standards, or agent roster changes.
- New API endpoints documented in `docs/API.md` (or OpenAPI).
- ADRs added for any non-obvious decision.
### 5.5 Lint / Typecheck / Build
- `npm run lint` (or the configured linter) — 0 errors.
- `npm run typecheck` (frontend) — 0 errors.
- `npm run build` — succeeds.
---
## 6. Definition of Done
A user story is **Done** when **all** of the following hold:
1. **Code builds successfully**`docker-compose build` (or `npm run build` in each subproject) succeeds.
2. **Tests pass** — full test suite green, coverage meets the gate in §4.5.
3. **Documentation updated** — README, SCREENS, and AGENTS touched where applicable.
4. **Security checks completed** — secrets scan clean, deps audited, auth in place, input validated.
5. **Linting passes** — 0 lint errors, 0 type errors.
6. **Demo verified** — the change has been exercised end-to-end against a running dev environment.
7. **PR merged** — at least one approval, CI green, branch deleted.
---
## 7. Development Workflow
### 7.1 Branch Strategy
This repo follows a strict two-branch model — **Git Flow style, with `main` as production**.
| Branch | Purpose | Pushable from | Invariant |
|---|---|---|---|
| `dev` | **Work and test.** All day-to-day work, new features, refactors, and bug fixes land here. The integration branch for in-progress code. | Any local commit / merge | May be broken. May have unreleased features. |
| `main` | **Production.** Only receives code that has been **tested on `dev`**. Every commit on `main` MUST be deployable. | A green test run against `dev`, followed by a deliberate merge | **Must always be deployable.** No broken builds, no half-finished features, no WIP commits. |
| `feature/<kebab>` | A new feature. | — | Branched from `dev`. Merged back into `dev` (PR). |
| `fix/<kebab>` | A bug fix. | — | Branched from `dev`. Merged back into `dev` (PR). |
| `hotfix/<kebab>` | An emergency fix that must reach production **without** going through the full `dev` test cycle. | — | Branched from `main`. Merged into **both** `main` and `dev` so `dev` doesn't drift. Used sparingly. |
**Promotion path: `dev` → `main`.** Promote only after:
1. All required tests pass on `dev` (whatever the team has agreed — currently there are no automated tests, so this is at minimum: manual smoke + a green `docker-compose up`). The audit recommends a real test suite — see §13.4.
2. The `dev` tip is reviewed and accepted.
3. A human (the maintainer) merges `dev` into `main` with a deliberate commit (no fast-forward). Tag the resulting `main` tip as a release (see §7.3).
**Why this matters.** If you commit straight to `main`, or merge an untested feature into `main`, you break the "always deployable" invariant. Subsequent deployments will ship broken code. **Treat any direct commit to `main` as a release-blocker.**
**Branch names:** kebab-case, ≤ 50 chars, no prefixes like `my/`. Use the `worktree-management` skill — every code change goes in `.worktrees/feature-<name>/` (or `fix-<name>`, `hotfix-<name>`).
### 7.2 PR Workflow
1. Pick or open an issue describing the change.
2. **Branch from `dev`** (not `main`) unless this is a hotfix (§7.1).
3. Implement + test locally.
4. Run the Definition of Done checklist (§6).
5. Open a PR targeting **`dev`**. Title in Conventional Commits format. Description links the issue and lists the user-visible behaviour change.
6. Address review feedback.
7. Merge via **squash** (or whatever the team agrees on). Delete the source branch.
8. **Promote to `main` separately**, after a green test run on `dev` (§7.1).
### 7.3 Release Workflow
- `main` is the source of release tags. When you promote `dev``main` and that merge is a release candidate:
- Tag the new `main` tip as `vMAJOR.MINOR.PATCH` (semver).
- Maintain a `CHANGELOG.md` per release (group: Added / Changed / Fixed / Removed).
- Backwards-incompatible changes bump MAJOR and write an ADR + migration guide.
### 7.4 Deployment Workflow
- **Container image** is the unit of deployment. `Dockerfile` produces one image that runs both API and static client.
- `docker-compose up -d` on the target host. Persistent volumes: `data/`, `uploads/`.
- Pre-deploy checklist:
- [ ] DB backup taken (`cp -r data data-backup-$(date +%F)`).
- [ ] `.env` file present and validated (no missing required vars).
- [ ] Image tag matches the release tag.
- Post-deploy: run smoke tests, verify `/api/health` (once added), check sync status (`GET /api/sync/status`).
---
## 8. Security Guidelines
### 8.1 Secret Management
- **Never** commit secrets. The current repo already has a hardcoded JWT secret in `docker-compose.yml` (`africa-alert-production-secret-2024`) **and** in `server/src/index.js` (`africa-alert-secret-key-2024`). **Action required** — see §13.3.
- Use environment variables. Provide an `.env.example` (NOT `.env`) that lists every required var.
- Production secrets live in a secret manager (Docker secrets, HashiCorp Vault, cloud KMS). Rotate JWT secrets at least quarterly.
- The `Supabase service key` and `Paynow integration key` are particularly sensitive — never log them, never return them to the client.
### 8.2 Dependency Scanning
- `npm audit --omit=dev` on every PR. **Block** on any `high` or `critical` CVE.
- Dependabot / Renovate configured to open weekly PRs.
- Pin major versions in `package.json`; let `package-lock.json` pin exact.
### 8.3 Authentication Standards
- All `/api/*` routes (except `/api/auth/login` and `/api/auth/register`) require a valid JWT in the `Authorization: Bearer <token>` header.
- Passwords stored with **bcrypt** (cost factor ≥ 10). Never store plaintext.
- Tokens expire in **7 days** (current setting). Add refresh-token flow for longer sessions.
- Rate-limit `/api/auth/login` (e.g. `express-rate-limit`, 5 attempts per 15 min per IP) to slow credential stuffing.
- Invalidate tokens on logout (blocklist or short-lived access tokens + refresh tokens).
### 8.4 Authorization Standards
- Every endpoint must check **both** authentication (`auth` middleware) **and** role where applicable.
- Use a role-based check helper: `requireRole('admin', 'teacher')` — do not sprinkle `if (req.user.role !== 'admin')` across controllers (the codebase already has this duplication — see §13.2).
- Never trust client-supplied user IDs. Always read `req.user.id` for the "current user".
- Resource-level access (a student reading their own grade) must be enforced in the controller, not only in the UI.
---
## 9. AI Collaboration Rules
These rules apply to every AI agent (Mavis rein, GitHub Copilot, or any other tool) working in this repo. They are non-negotiable.
1. **Do not modify unrelated files.** Stay inside the area you own (§3). Cross-cutting changes require an Architect-agent review.
2. **Explain major architectural changes** in an ADR before writing code. Surface the trade-offs in the PR description.
3. **Create tests for every new feature** in the same change. No untested code lands on `dev`.
4. **Update documentation when changing functionality.** README, SCREENS, AGENTS, and the API doc all reflect what the code actually does.
5. **Prefer maintainability over cleverness.** A 50-line explicit controller beats a 10-line abstract one that nobody can debug at 2am.
6. **Ask before destructive changes.** Deleting files, force-pushing, dropping tables, hard-resetting the DB — always confirm with the user first.
7. **Read this file end-to-end before your first change** in the repo. If anything is unclear or out of date, flag it in the PR.
8. **Use the worktree workflow.** All code changes go in `.worktrees/feature-<name>/` — never in the main checkout. See `worktree-management` skill.
9. **Reference code with `path:line`** when discussing it in chat or PRs — never paste a wall of context.
10. **Be conservative with new dependencies.** Each new `package.json` entry increases the supply-chain surface. Justify it.
---
## 10. Repository Knowledge Base
> A condensed map of the codebase. New agents: read this section first.
### 10.1 Important Modules
| Module | Frontend | Backend Controller | Notes |
|---|---|---|---|
| Auth | `client/src/store/auth.ts` | `POST /api/auth/login`, `/register` in `server/src/index.js` | JWT + bcrypt; 7-day expiry |
| Users | `client/src/pages/Students.tsx`, `Teachers.tsx`, `admin/Users.tsx` | `controllers/users.controller.js` | Roles: admin / teacher / student / parent (+ accountant / librarian / nurse in schema) |
| Classes & Subjects | `client/src/pages/Classes.tsx` | inline in `server/src/index.js` (classes + subjects) | |
| Enrollments | — | inline in `server/src/index.js` | |
| Attendance | `client/src/pages/Attendance.tsx` | inline + `controllers/attendance.controller.js` | Supports bulk upsert |
| Fees | `client/src/pages/Fees.tsx` | inline + `controllers/paynow.controller.js` | Paynow integration for online payment |
| Messages | `client/src/pages/Messages.tsx` | `controllers/messages.controller.js` | |
| Calendar / Events | `client/src/pages/Events.tsx` | `controllers/calendar.controller.js` | |
| Exams | `client/src/pages/exams/ExamViews.tsx` + `client/src/store/exams.ts` | `controllers/exams.controller.js` | Full exam lifecycle: groups → questions → schedules → attempts → grading |
| Assignments | `client/src/store/assignments.ts` | `controllers/assignments.controller.js` | |
| Grades | `client/src/pages/student/Grades.tsx` | `controllers/grades.controller.js` | |
| Reports | `client/src/pages/admin/Reports.tsx` | `controllers/reports.controller.js` | |
| Settings | `client/src/pages/admin/Settings.tsx` | `controllers/settings.controller.js` | |
| Departments | `client/src/pages/admin/Departments.tsx` | `controllers/departments.controller.js` | |
| Payroll & HR | (frontend TBD) | DB only (`init.js`) | 8 tables: staff_roles, salary_grades, staff_records, leave_types, leave_requests, staff_attendance, payroll_runs, payslips |
| Inventory | (frontend TBD) | DB only | 7 tables: item_categories, suppliers, store_locations, items, item_stock, stock_transactions, item_issues |
| Hostel | (frontend TBD) | DB only | 6 tables |
| Transport | (frontend TBD) | DB only | 5 tables |
| Front Office | (frontend TBD) | DB only | 5 tables |
| Sync | — | `services/SyncEngine.js` | Bidirectional, server-wins conflict policy, 30 s interval |
### 10.2 Key Services
- **SyncEngine** (`server/src/services/SyncEngine.js`) — singleton (`getSyncEngine()`). Runs every `SYNC_INTERVAL` ms (default 30000). Pushes local `sync_status = 'pending'` rows to Supabase REST, pulls remote `updated_at >= lastSync` rows back. Logs errors to `sync_logs`. Exposes `/api/sync/status` and `/api/sync/force`.
- **Paynow client** (`server/src/controllers/paynow.controller.js`) — integrates with `https://www.paynow.co.zw/interface/initiatetransaction`. Endpoints: `POST /initiate`, `GET /status/:id`, `POST /webhook`, `GET /student/:id`, `DELETE /:id`.
- **Auth** — JWT signed with `JWT_SECRET` (env), 7-day expiry. Role embedded in payload.
### 10.3 Database Schema (39+ tables)
Full schema lives in `server/src/database/init.js` (1675 lines). All tables share the sync columns: `uid`, `last_synced_at`, `sync_status`, `is_deleted`. Core groups:
- **Core (11):** users, classes, subjects, enrollments, attendance, fee_groups, student_fees, payments, messages, events, expenses, grades, sync_logs, sync_config, system_settings.
- **Payroll & HR (8):** staff_roles, salary_grades, staff_records, leave_types, leave_requests, staff_attendance, payroll_runs, payslips.
- **Inventory (7):** item_categories, suppliers, store_locations, items, item_stock, stock_transactions, item_issues.
- **Exams (5):** exam_groups, question_banks, exam_schedules, exam_attempts, exam_answers.
- **Hostel (6):** hostels, room_types, rooms, room_assignments, hostel_fees.
- **Transport (5):** vehicles, routes, pickup_points, vehicle_routes, transport_allocations.
- **Front Office (5):** admission_enquiries, visitor_logs, phone_call_logs, postal_dispatch, complaints.
User roles supported by the schema `CHECK` constraint: `admin`, `teacher`, `student`, `parent`, `accountant`, `librarian`, `nurse`. The frontend currently only authenticates the first four.
### 10.4 API Structure
- All routes mounted under `/api/<module>` in `server/src/index.js`.
- 12 controller routers + 2 inline route blocks (auth, dashboard stats) + 2 inline CRUD blocks (classes, subjects, enrollments, attendance, fees, messages, events, expenses).
- Response shape: JSON. Errors are `{ "error": "<message>" }` (no error code, no stack trace in production — see §13.4).
- All authenticated routes set `req.user = { id, email, role }` from the JWT.
### 10.5 Infrastructure Components
- **Dockerfile** — multi-stage: `client-build` (Vite build) → `server-build` (npm ci) → `production` (Alpine runtime, copies built artefacts, runs `docker-entrypoint.sh`).
- **docker-compose.yml** — single `app` service. Mounts `./data` and `./uploads` as volumes. Exposes 3000 (client) and 3001 (API). **Currently hardcodes `JWT_SECRET`** — see §13.3.
- **docker-entrypoint.sh** — initializes DB on first run, starts API in background, then `npx serve` for the static client.
### 10.6 Environment Variables
| Name | Default | Required | Purpose |
|---|---|---|---|
| `PORT` | `3001` | no | API port |
| `JWT_SECRET` | ⚠️ hardcoded fallback | **yes (prod)** | Signs JWTs. **MUST** be set in production. |
| `DB_PATH` | `./data/school.db` | no | SQLite path |
| `SUPABASE_URL` | `https://api.next_gen.techarvest.co.zw` | no | Cloud sync target |
| `SUPABASE_KEY` | empty | no (offline mode if missing) | Cloud sync auth |
| `SYNC_INTERVAL` | `30000` | no | Sync period in ms |
| `PAYNOW_INTEGRATION_ID` | empty | **yes (prod)** | Paynow merchant id |
| `PAYNOW_INTEGRATION_KEY` | empty | **yes (prod)** | Paynow signing key |
| `PAYNOW_RETURN_URL` | `http://localhost:3000/fees` | no | Post-payment redirect |
| `PAYNOW_BLOCKING_URL` | `http://localhost:3000/api/payments/webhook` | no | Webhook target |
| `NODE_ENV` | `production` (in Docker) | no | Standard Node env |
---
## 11. Architecture Decision Records (ADRs)
Place new ADRs in `docs/adr/NNNN-<title>.md`. Use [MADR](https://adr.github.io/madr/) or a simplified template:
```markdown
# NNNN. <Title>
- **Status:** Proposed | Accepted | Superseded by NNNN
- **Date:** YYYY-MM-DD
- **Deciders:** <agents / humans>
## Context
<what's the situation>
## Decision
<what we chose>
## Consequences
<what becomes easier, what becomes harder>
```
Suggested first ADRs to write:
1. **0001** — Use SQLite as the local mirror + Supabase as cloud (current model).
2. **0002** — Server-wins conflict resolution in sync.
3. **0003** — JWT-only auth (no session cookies).
4. **0004** — Single-container deployment.
5. **0005** — Role-based access (admin / teacher / student / parent).
---
## 12. Onboarding Checklist (for any new agent)
Use this when picking up the repo fresh:
- [ ] Read this `AGENTS.md` end-to-end.
- [ ] Skim `README.md`, `SCREENS.md`, `IMPLEMENTATION_SUMMARY.md`, `DOCKER_README.md`.
- [ ] Run `docker-compose up -d` and log in as `admin@school.com / admin123`.
- [ ] Click through every dashboard and module once.
- [ ] Read `server/src/index.js` and `server/src/services/SyncEngine.js`.
- [ ] Read `client/src/App.tsx` and `client/src/store/auth.ts`.
- [ ] Read `server/src/database/init.js` (it's long — focus on the `CHECK` constraints and the sync columns).
- [ ] Skim one controller per module (e.g. `exams.controller.js`, `paynow.controller.js`).
- [ ] Run the dev servers in worktrees per the `worktree-management` skill.
- [ ] Pick a small, scoped issue from the backlog and follow the PR workflow in §7.
---
## 13. Recommendations (action items for the human maintainer)
The audit identified the following. Treat them as a prioritised backlog — not a demand. Every recommendation has a one-line rationale.
### 13.1 Architecture Improvements (priority: medium → high)
- **M1 — Move schema out of `init.js` into versioned migrations** (use `node-pg-migrate` style or `knex`). `init.js` is 1675 lines and grows linearly with every new module. *Why:* current `CREATE TABLE IF NOT EXISTS` pattern cannot represent ALTER statements, so any schema change is a full DB rebuild.
- **M2 — Extract a shared `auth` middleware module.** Each controller currently re-declares its own `auth` (e.g. `exams.controller.js:17`, `paynow.controller.js:18`, `index.js:55`). *Why:* drift risk — a security fix to one will be missed in the others.
- **M3 — Introduce a service layer between controllers and the DB.** Right now controllers inline SQL strings. Extract `users.service.js`, `exams.service.js`, etc. *Why:* testability (mock the service, not the DB) and reuse (the sync engine will share services).
- **M4 — Add request validation.** Use `zod` or `joi` schemas at the route boundary. *Why:* the current code trusts `req.body` to have the right shape; a malformed payload currently throws inside better-sqlite3.
- **M5 — Make the sync engine resilient to a missing/disrupted Supabase.** It already no-ops on missing key; harden the timeout, retry, and circuit-breaker paths. *Why:* when sync is down, the API must not slow down.
- **M6 — Multi-tenant design (if applicable).** If multiple schools will use one deployment, add a `school_id` to every table and a tenant-scoping middleware. *Why:* retrofitting tenant IDs is a painful migration.
### 13.2 Missing Documentation (priority: medium)
- **D1 — `docs/API.md` (or `docs/openapi.yaml`).** No central API reference. Generate from JSDoc with `swagger-jsdoc` + `swagger-ui-express`.
- **D2 — `docs/adr/` with the suggested ADRs from §11.**
- **D3 — `.env.example` at repo root** listing every variable from §10.6.
- **D4 — Operations runbook** (`docs/runbook.md`): how to reset the DB, repair a broken sync, restore from backup, rotate secrets.
- **D5 — Document the schema** in `docs/schema.md` (Mermaid ER diagram is fine).
- **D6 — Document the `scopes` / permissions** in a code-readable form (a JSON `permissions.json` that the server can import), so frontend and backend share one source of truth. Currently the matrix lives only in `SCREENS.md` and is duplicated in `App.tsx` and `Nav.tsx`.
### 13.3 Security Improvements (priority: HIGH)
- **S1 — Remove the hardcoded JWT secret fallbacks.** `server/src/index.js:25` and every controller that does `process.env.JWT_SECRET || 'africa-alert-secret-key-2024'`. Refuse to start without `JWT_SECRET` in production. *Why:* anyone reading the public repo can mint admin JWTs.
- **S2 — Remove the hardcoded `JWT_SECRET` from `docker-compose.yml`** (line 18). It is currently `africa-alert-production-secret-2024` — a **production-named** secret committed to git. *Why:* it is now public knowledge. Rotate immediately in any environment that ever used this value.
- **S3 — Add `express-rate-limit` to `/api/auth/login` and `/api/auth/register`.** No throttling today.
- **S4 — Tighten CORS.** `app.use(cors())` is wide-open (`server/src/index.js:36`). Restrict to known origins.
- **S5 — Add a request-size limit** (`express.json({ limit: '1mb' })`). Default is 100kb, but be explicit.
- **S6 — Verify Paynow webhook signatures.** Currently the webhook handler accepts any payload; the hash check appears in `generateHash` but is it verified on inbound? Audit `paynow.controller.js` end-to-end.
- **S7 — Add `helmet`** for sensible HTTP security headers.
- **S8 — Scan dependencies in CI** (`npm audit` + a Dependabot config).
### 13.4 Testing Improvements (priority: medium → high)
- **T1 — Add a test runner to both packages.** No tests exist today. Backend: **Jest** + **supertest**. Frontend: **Vitest** + **React Testing Library** + **@testing-library/jest-dom**. E2E: **Playwright**.
- **T2 — Write integration tests for the auth flow** first (login, bad password, expired token, role denial).
- **T3 — Write integration tests for the exam timer + auto-submit** (race-condition-prone).
- **T4 — Write a regression test for the sync engine's conflict resolution** (server-wins).
- **T5 — Add a coverage gate** in CI (see §4.5).
- **T6 — Add a smoke test for the Docker image** — start the container, hit `/api/health` (once added), assert 200.
### 13.5 DevOps Improvements (priority: medium)
- **O1 — Add `/api/health` and `/api/ready` endpoints.** Liveness vs readiness (the latter should return 503 until the DB is initialized).
- **O2 — Add CI** (GitHub Actions recommended): install → lint → typecheck → test → build → docker build.
- **O3 — Pin Node version in `.nvmrc` and in `Dockerfile` (`FROM node:20.x-alpine`).** Pinning to `node:20-alpine` is OK but explicit `20.x.y` is safer.
- **O4 — Add a non-root `USER` in the production Dockerfile stage.** Containers currently run as root.
- **O5 — Add a `HEALTHCHECK` instruction** to the Dockerfile.
- **O6 — Automate SQLite backups** (cron sidecar or a simple `node-cron` task in the API). Ship a documented `restore.sh`.
- **O7 — Add log aggregation.** Today logs go to stdout only. Pipe to a log shipper (Loki, CloudWatch, etc.) in production.
- **O8 — Add `.dockerignore`** to keep `node_modules`, `data/`, `dist/`, `uploads/`, `.git/`, `.harness/` out of the build context.
- **O9 — Split the Docker image** into `client` (Nginx serving `dist/`) + `api` for cleaner scaling. Optional — current single-image design is simpler to operate.
### 13.6 Documentation-as-Product (priority: low → medium)
- **P1 — Promote `SCREENS.md` to a generated artefact.** It is already excellent; consider a small script that builds it from the route table.
- **P2 — Add a `CONTRIBUTING.md`** linking to this file and the worktree workflow.
- **P3 — Add a `SECURITY.md`** with a disclosure address (e.g. `security@…`).
---
## 14. Open Questions for the Maintainer
These are decisions the audit surfaced that only you can answer. Each is one PR away from being resolved.
1. ~~Which branch is the integration target — `dev` or `main`?~~ **Resolved 2026-06-03**`dev` is for work + test, `main` is for deployable code only. Documented in §7.1.
2. Do you plan to scale beyond a single SQLite file? If so, when — that determines whether to invest in the migration to Postgres now or in 6 months.
3. Are `accountant`, `librarian`, `nurse` (defined in the schema) planned roles? They have no UI today.
4. Will the same deployment serve multiple schools, or one-school-per-install?
5. Who hosts the Supabase project, and who rotates the service key?
6. Is there a separate staging environment, or does `docker-compose up` on a developer laptop serve as staging?
7. What is the on-call story for a school that loses connectivity during exam day?
8. Are there any school-data privacy requirements I should encode into the architecture (e.g. POPIA, GDPR, local equivalents)?
---
*Document version: 1.0 — written 2026-06-03 by an initial repository audit. Update in the same PR as any change to the architecture, the standards, or the agent roster.*