Appearance
Instructor Role & Workflow
Purpose
Instructors are faculty who supervise lab sessions. The implemented instructor features are monitoring and grading: they watch live labs, follow each student's progress and SIEM alerts, examine the evidence behind a report, and approve, reject or return reviews. They do not operate infrastructure or manage accounts.
Pages and navigation
Sidebar: Dashboard, Live labs, Students, Reviews (instructorNavItems in cyberrange/portal/src/lib/navigation.ts). All /instructor/* pages require the instructor or admin role, and an instructor who opens /dashboard or /admin is sent to /instructor (redirectPathForRole() in cyberrange/portal/src/lib/routeRoles.ts).
On first sign-in an instructor sees a one-time instructor orientation at /orientation (Watch live labs, Check student progress, Decide reviews, Export results) before reaching the requested page; Continue to the instructor dashboard records it with POST /users/me/orientation (cyberrange/portal/src/app/orientation/page.tsx, pull request 188).
| Page | What it shows | Backend calls |
|---|---|---|
/instructor Dashboard | Review queue preview, live labs, students and reviews summary, Knowledge-gain export (format and scenario; always anonymized for instructors, and the page reports how many test or staff accounts were left out) | GET /instructor/reviews, GET /instructor/students, GET /instructor/export/knowledge-gain |
/instructor/pods Live labs | Every live lab (student, scenario, status, time left), with Refresh. Container names, connection IDs and Wazuh agent IDs are removed from this view. | GET /instructor/pods |
/instructor/students Students | Roster of every student who has any lab, score or review on record: progress by scenario chips (tasks passed / total and points per scenario), total points, active pod, pending reviews and last activity. Each row expands to a per-scenario task table (status, points, detection) with a link to the full detail (pull request 220) | GET /instructor/students |
/instructor/students/[id] Student detail | Download of the student's milestones, reviews and live alerts as one CSV; active lab; SIEM alerts panel (live alerts for the active lab, plus Past labs with archived alerts per ended lab and a triaged view), with a milestone score strip and filters by search text, rule, agent and severity (pull request 214); check-failure reasons per scenario, milestone and lab; milestone attempt history with Source and SIEM detection columns; review history | GET /instructor/students/{id}, GET /instructor/students/{id}/diagnostics, GET /instructor/pods/{id}/alerts, GET /instructor/students/{id}/labs, GET /instructor/students/{id}/labs/{pod}/alerts |
/instructor/reviews Review queue | Review cases filtered by status (PENDING, APPROVED, REJECTED, RETRY, ALL) | GET /instructor/reviews?status_filter= |
/instructor/reviews/[id] Review detail | Milestone attempt, submitted report text, reported conflict reason, student screenshots, automated verifier evidence, alerts captured with this report, previous resolution, and the Instructor Evaluation form (score 0 to 100, feedback, and ✓ Approve Submission, ✕ Reject Submission, ↺ Request Student Retry) | GET /instructor/reviews/{id}, GET /reviews/{id}/images, GET /instructor/reviews/{id}/alert-snapshot, POST /instructor/reviews/{id}/resolve |
Where the student list comes from
The student list is built from the platform's own records (anyone with a lab, milestone result or review in SQLite), not from Keycloak. A newly created student who has never started a lab does not appear yet (instructor_list_students in cyberrange/src/provisioning/pods_router.py).
Instructor workflow
Main features and actions
Monitoring live labs and students
- Live labs: all labs that are not destroyed, for every student (
GET /instructor/pods). - Student progress: every milestone attempt with its status, catalog points, detection score and time, so an instructor can see not only the result but how many tries it took (
GET /instructor/students/{id}). - Where an attempt came from: since schema v15 each attempt records a
source:auto(background poller),manual(Manual Check),browser(Scenario 2 DVWA),flagorreview(instructor approval). Older rows show "—". Backgroundautoresults that are not a PASS are hidden by default behind Show background checks (N), because the poller writes one every few seconds (pull request 268,lib/instructorStudentProgress.ts). - SIEM detection label: the detection score is shown separately from points as Detected, Not detected, Not configured (no Wazuh rule is configured for that milestone, currently Scenarios 1, 2 and 4 and Scenario 3 after Task 1) or Unknown (pull request 209,
cyberrange/portal/src/lib/siemDetection.ts). - Check-failure reasons: for scenarios switched on in
SCORING_DIAGNOSTICS_SCENARIOS, the student page lists why the latest check of an unpassed milestone failed, as allowlisted reason codes with plain-language text (for example a missing artifact or a rule that does not match). Students only ever see a generic result. Rows are replaced by a PASS and purged afterSCORING_DIAGNOSTICS_RETENTION_DAYS(default 14) (GET /instructor/students/{id}/diagnostics,cyberrange/src/provisioning/scoring_diagnostics.py,components/instructor/ScoringDiagnostics.tsx). - SIEM visibility: the same six safe alert fields students see, for any live lab, for ended labs (read from the Wazuh manager's daily archives using the recorded lab time window), and frozen with each report at submission time. A triaged view groups alerts by rule and separates alerts expected for the scenario, likely student activity, and background noise (
cyberrange/src/provisioning/alerts_endpoint.py,lab_history.py,cyberrange/portal/src/lib/siemTriage.ts).
Grading reviews
- Resolve a case as APPROVED, REJECTED or RETRY, with a score from 0 to 100 and feedback. The grader's username is stored in
graded_by. A version check (expected_status) stops two instructors from overwriting each other's decision. - Approving credits the student. Approving any case tied to a milestone (a scoring conflict, a written report or a manual review, including the ones created when a student's one Manual Check fails) inserts a milestone PASS, marked
instructor_approved_conflictorinstructor_approved_review, and never twice for the same milestone. This PASS row is what the student's score reads. Before pull request 141 only conflict approvals did this, so approved reports never reached the student's score. - Changing your mind: moving an approved case to
REJECTEDorRETRYdeletes the instructor-granted PASS again; auto-detected and flag PASS rows are never touched. - Scoring conflicts: when the hybrid scoring path escalates a case (flag and lab state disagree), it appears as a
SCORING_CONFLICTcase visible only to staff.RETRYis not allowed for this case type. - Lab lists show each lab's own time limit (
ttl_minutes). - Every resolution writes a
REVIEW_CASE_RESOLVEDaudit event.
Exporting research data
GET /instructor/export/knowledge-gain returns completion rates, time from lab start to first PASS, detection indicators and rubric scores as JSON or CSV. Student IDs are anonymized with a salted HMAC (TELEMETRY_ANONYMIZATION_SALT). Since pull request 272, anonymize=false (cleartext IDs) is admin only: an instructor gets 403 and the attempt is audited. Every export writes a KNOWLEDGE_GAIN_EXPORTED audit event without student IDs. CSV output is protected against spreadsheet formula injection (cyberrange/src/provisioning/knowledge_gain.py, pods_router.py).
- Who is counted: accounts an admin marked as test accounts, and every account holding the
instructororadminrole in Keycloak, are left out. The summary (and, for CSV, theX-Excluded-AccountsandX-Staff-Exclusionheaders) says how many were left out and whether staff could be resolved (applied) or Keycloak was unreachable (unavailable, staff may be included). Only an admin can passinclude_excluded=true(pull requests 273 and 301). - Time to milestone: the start of the lab is read from
lab_sessions, so it stays correct after the lab slot is reused; PASS rows created by an instructor approval carry no time-to-milestone, because the approval time is the instructor's (pull request 270). - Each record also has a
scenario_number(1 to 4, the paper's numbering) next to the internalscenario_id.
Using the student workspace
The lab pages (/scenarios, /scenario/[id]) are open to every application role, so an instructor can start their own lab to try a scenario; only the student /dashboard redirects them to /instructor. It is limited exactly like a student's: one lab, their own data.
Allowed and restricted routes
| Allowed | Restricted |
|---|---|
/instructor/* pages; /scenarios and /scenario/[id] for their own lab | /admin/* pages and /dashboard (redirect to /instructor) |
All instructor API routes (instructor or admin) | All admin API routes, and the identified or include_excluded knowledge-gain export: 403 |
| All self-scoped routes for their own labs and reviews | Another user's lab actions (status, terminal, destroy, lab URLs): 404 |
| Reading any review and its screenshots | Uploading or deleting screenshots on another user's review: 404 |
Backend permissions
| Endpoint | Purpose |
|---|---|
GET /instructor/pods | All live labs, internals stripped |
GET /instructor/students, GET /instructor/students/{id} | Student list and detail (detail includes each attempt's source) |
GET /instructor/students/{id}/diagnostics | Check-failure reason codes, optionally for one lab (pod_id, pod_created_at) |
GET /instructor/pods/{id}/alerts | Live SIEM alerts for any lab |
GET /instructor/students/{id}/labs, GET /instructor/students/{id}/labs/{pod}/alerts?started_at= | Lab history and archived alerts |
GET /instructor/reviews/{id}/alert-snapshot | Alerts frozen with a report |
GET /instructor/reviews, GET /instructor/reviews/{id}, POST /instructor/reviews/{id}/resolve | Review queue and decisions |
GET /instructor/export/knowledge-gain | Research export (anonymized; identified and include_excluded are admin only) |
GET /reviews/{id}, GET /reviews/{id}/images, GET /reviews/{id}/images/{image} | Read any review and its screenshots (owner or staff) |
All are guarded by require_role(["instructor", "admin"]) or the owner-or-staff check, behind the router-level require_app_role (cyberrange/src/provisioning/pods_router.py, alerts_endpoint.py).
Data an instructor can view or modify
| Data | View | Modify |
|---|---|---|
| All live labs (without container names or agent IDs) | Yes | No (cannot destroy) |
| Every student's milestone history and detection evidence | Yes | Only indirectly, by approving (or un-approving) a milestone-scoped review |
| All review cases, including staff-only scoring conflicts | Yes | Resolve (status, score, feedback) |
| Evidence screenshots | Yes | No |
| SIEM alerts (allow-listed fields) | Yes | No |
| Check-failure reasons (allowlisted codes) | Yes | No |
| Knowledge-gain export | Anonymized only | No |
| Accounts, roles, research exclusions, system health, audit log | No | No |
What instructors cannot do
These are not role restrictions that were forgotten; the code has no path for them:
- Destroy or force-destroy another user's lab (owner-only destroy; force-destroy is admin-only).
- Open a terminal into a student's lab (the bridge's ownership check refuses non-owners who are not admins).
- Create, edit or reorder scenarios, milestones or points (Not implemented; the catalog is static).
- Message students directly (feedback is only written on a review).
- Manage users or roles.
Sources
cyberrange/portal/src/app/instructor/**, cyberrange/portal/src/app/orientation/page.tsx, cyberrange/portal/src/components/instructor/*, cyberrange/portal/src/lib/navigation.ts, lib/instructorStudentProgress.ts, lib/siemDetection.ts, lib/studentProgressCsv.ts, cyberrange/src/provisioning/pods_router.py (instructor and review routes), alerts_endpoint.py, lab_history.py, knowledge_gain.py, scoring_diagnostics.py, research_exclusions.py, test_sec01_rbac_matrix.py.