# ClassGuard viewing server

This server pairs teacher and student devices, handles classroom approval and WebRTC signaling, and serves an optional browser-based teacher viewer. Screen video travels through WebRTC between the student and teacher; this server does not receive, record, or save video frames. Screen sharing is required for monitored class participation in the accompanying Chrome extension. Chrome’s native screen picker is still required to start capture.

Keep `classguard-server/` and `classguard/` next to each other when unzipping the package. Node.js 20 or newer is required. There are no npm dependencies to install.

## Test on one computer

Generate two different private keys by running this command twice in your terminal:

```sh
node -e "console.log(require('node:crypto').randomBytes(32).toString('base64url'))"
```

Copy the result into the environment variable below, then run the server from the `classguard-server` directory:

```sh
export CLASSGUARD_TEACHER_KEY='PASTE-YOUR-GENERATED-PRIVATE-TEACHER-KEY-HERE'
export CLASSGUARD_ADMIN_KEY='PASTE-A-DIFFERENT-GENERATED-PRIVATE-ADMIN-KEY-HERE'
node server.mjs
```

The server listens at `http://127.0.0.1:8787`. It intentionally defaults to loopback, so other computers cannot connect to this test address. Use the real generated key in Teacher access key; share only the class join code with students. The server does not log your key, session tokens, SDP messages, or student names.

1. Load/reload the `classguard` extension.
2. Open **Screen viewing → Open teacher view**, or open `http://127.0.0.1:8787` in Chrome.
3. Enter the server address, teacher key, teacher display name, class name, and period length. Enter allowed domains such as `ixl.com`, or turn off **Only allow listed websites** for a screen-viewing test without class website filtering. Select **Start class**.
4. On the student extension, select **Share screen with teacher**, enter the same server address and class code, and choose a display name.
5. Approve the student in the teacher grid.
6. The student selects **Share screen to enter class** within 60 seconds of approval and uses Chrome’s picker. They become active only when the teacher viewer confirms the stream arrived.
7. The teacher can view/expand the stream, remove a student, or end the class. Canceling capture, stopping, leaving, closing the sharing page, or losing the screen connection ends the student’s class session.

Only video is shared. Audio, camera, and microphone capture are not requested. Sharing does not start automatically after approval, and it does not silently restart if a stream ends.

## Deploy for different computers

Use a school-controlled HTTPS address that both teacher and student devices can reach, such as `https://screens.your-school.example`. Localhost on a student computer refers to that student's own computer, not the teacher's server.

Two supported arrangements:

- Run behind a school-managed HTTPS reverse proxy. Keep the Node server on loopback and set `CLASSGUARD_BEHIND_HTTPS_PROXY=1` plus explicit allowed origins.
- Terminate TLS in Node by setting `CLASSGUARD_TLS_CERT` and `CLASSGUARD_TLS_KEY` to your trusted certificate and key files. Set `CLASSGUARD_HOST` to the network interface address and configure explicit allowed origins.

Environment variables:

| Variable | Purpose |
| --- | --- |
| `CLASSGUARD_TEACHER_KEY` | Private staff key, at least 24 characters; use a generated high-entropy key. |
| `CLASSGUARD_ADMIN_KEY` | Separate private district administration key, at least 24 characters and different from the teacher key. Required for roster import and code export. |
| `CLASSGUARD_ROSTER_FILE` | Persistent private roster path. Default `data/roster.json` next to server.mjs. Back up privately; never publish or bundle it. |
| `CLASSGUARD_HOST` | Bind address. Default `127.0.0.1`. Use a network interface only with TLS or a trusted HTTPS proxy. |
| `CLASSGUARD_PORT` | Server port. Default `8787`. |
| `CLASSGUARD_TLS_CERT`, `CLASSGUARD_TLS_KEY` | Optional trusted certificate and private key file paths. |
| `CLASSGUARD_BEHIND_HTTPS_PROXY` | Set to `1` only when an actual trusted HTTPS proxy fronts the service. |
| `CLASSGUARD_ALLOWED_ORIGINS` | Comma-separated teacher website origin and deployed extension origin(s), for example `https://screens.your-school.example,chrome-extension://YOUR-EXTENSION-ID`. Required for TLS/network/proxy deployment. |
| `CLASSGUARD_ICE_SERVERS` | Optional JSON array of WebRTC ICE server settings supplied by your school. |

Find the extension ID on its card at `chrome://extensions`. Enterprise distribution gives your deployed extension a stable ID. Unpacked extension IDs can differ between computers, so include the actual allowed origins for each test installation. The local loopback development configuration accepts local HTTP origins and extension origins; network deployment requires an explicit list.

Default ICE settings are empty, suitable for a direct same-network connection when browsers can reach each other. Across NATs, isolated Wi-Fi networks, or restrictive school firewalls, configure appropriate school-operated STUN/TURN services using `CLASSGUARD_ICE_SERVERS`. A connection failure ends the student’s monitored class session; they must rejoin and establish sharing again. Configure and test these services before school rollout.

An ICE configuration uses the standard RTCPeerConnection shape:

```json
[{"urls":"turn:YOUR-TURN-SERVER:3478","username":"YOUR-USERNAME","credential":"YOUR-CREDENTIAL"}]
```

Supply your actual server settings through the environment, not committed source files. ICE configuration is provided only after a teacher creates a class or a student joins a valid class.

## Access and lifecycle

- Staff need the server teacher key to create a class.
- Each class receives a random invitation code and a separate 256-bit teacher session token. Teacher tokens stay in page memory. The installed student extension retains its class credential in Chrome session storage to poll policy after the sharing page closes. Class session credentials never appear in URLs. Enrollment links use fragments, which the student page removes after filling the form.
- Open classes use an invitation code and self-reported display name, requiring individual teacher approval. Roster classes require a personal enrollment code belonging to the selected group. Their registered name is used and approval is automatic. Enrollment codes are bearer credentials, not school SSO or verified email identity.
- Screen sharing is mandatory: approval starts a 60-second deadline. Signaling that sharing started does not grant active status; only the teacher viewer can confirm a received stream. The viewer reports screen health every 5 seconds; 30 seconds without that confirmation ends participation. Canceling or stopping capture ends participation. Rejoining creates a new pending entry.
- The teacher grid plays every connected student screen at once, up to 30 per class. Compact layout and Focus classroom reduce extra interface space. Expand is an optional close-up; it does not control which streams run.
- Teachers can supply up to 200 allowed domains for a period of 1–120 minutes. Only the class teacher can edit that list. Policy changes notify students; the installed extension also polls every 30 seconds. Active screen confirmation requires the current policy revision to be acknowledged by the student extension. This is an application acknowledgment, not hardware attestation or a verified school identity.
- A student leaving or losing sharing does not release the period’s website restriction. Their extension can still fetch policy using its student token until class ending; End class or scheduled expiry restores normal browser policy.
- The teacher grid distinguishes joined students from active students. A waiting or connecting student is not active.
- Students can signal only their own class's teacher. Teachers can signal only approved members of their own class.
- Up to 30 students can join a class. Classes expire after two hours or after 90 seconds without a teacher heartbeat. Student inactivity also expires after 90 seconds.
- Ending a class or removing a student notifies clients; sharing pages stop their tracks and viewers clear video. Browser tab closure sends a best-effort leave/end notice, with heartbeat expiry as backup.
- Live class names, display names, approval state, and connection messages are held only in server memory. Imported district student IDs, names, optional emails, groups, and hashed enrollment codes persist separately in the private roster file. Ended room data is purged after one minute; a restart clears all sessions.
- API requests have size/rate limits. Static file serving is allowlisted; server source and environment configuration cannot be downloaded through it.

The staff-key arrangement is a functional starter for a controlled deployment. Update the extension and server together to version 1.9.0. This requirement applies to the ClassGuard class session, not attendance or access in unrelated applications. School SSO, verified email identity, per-teacher account provisioning, managed extension distribution, and school-network testing are separate deployment work. Normal district filtering is not centrally administered by this server; it supplies only each classroom’s temporary list. This does not provide silent monitoring, background screenshots, recordings, remote keyboard/mouse control, or centrally enforced device surveillance.

## Checks

```sh
npm test
```

Tests exercise real HTTP API requests, authentication, approval, role/class isolation, teacher-only admission, required-sharing deadlines, stop/removal, missing screen-heartbeat expiry, stale-sharing rejection, class ending, static-file restrictions, and peer negotiation behavior. A live browser test in the development workspace transmitted a synthetic lesson canvas through the real server to the teacher viewer and verified that stopping sharing cleared the video. No real desktop or student account was captured for that test. The installed Chrome desktop picker and a real school network still need validation after deployment.

Browser API references:
- https://developer.chrome.com/docs/extensions/reference/api/desktopCapture
- https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia
- https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Signaling_and_video_calling

## District roster

Open `/district.html` on this server to import and search a CSV roster, create enrollment codes, and organize students into class groups. See [BULK-ENROLLMENT.md](BULK-ENROLLMENT.md) for the complete rollout flow.

The CLI stores roster records in `data/roster.json`, with atomic replacement and owner-only file permissions where supported. Raw enrollment codes are returned only to the authenticated admin export; the server stores SHA-256 hashes. Teacher keys can list group names/counts but cannot read/export the district roster. Student codes reveal only that student’s record and open classes for their groups. Do not expose the data directory, operator backups, keys, or enrollment exports through a web server or public archive. The provided static server does not serve them.

Roster storage persists across server restarts. There is no automatic retention expiry or student deletion UI in this release. For a full reset, stop the server, remove the private roster file and any operator backups, and restart; existing enrollment codes become invalid. A code export replaces all prior codes for future entries, while existing live sessions continue until their normal end. Importing a CSV adds/updates records; omitting a student does not delete them.

The roster supports 20,000 students, paginated in batches of 100. Live capacity remains 30 students per class and 50 rooms on one server; the 1,200-student import test is not a 1,200-live-stream load test. Test district infrastructure and network capacity before scaling live viewing.

## Local HTML inspection

Version 1.9 adds local game-content inspection in the installed Chrome extension. The pairing server receives no HTML, page text, inspection signals, or detected game URLs. Inspection configuration belongs to the extension’s local/managed policy. The website-only viewer and student preview do not themselves install a browser-wide inspector. See the extension README for detection limits and trusted-activity exceptions.
