API — Student page (self-service)¶
This page documents the non-pedagogical part of ChaosStudentController, mounted under /api/chaos/student. These endpoints let the student drive chaos themselves on their own machine — within their license limits — without going through the instructor.
Controller covered
ChaosStudentController — self-service part (status, performance, scripting, business, functional, security, scenarios, admin/mode)
The pedagogical journeys part of the same controller (instructor activation, join, validate, logique, finale, etc.) is documented separately in pedagogique.md.
Student mode¶
The PerfShop student page has a dual purpose:
- Standalone self-service: a student training alone can activate chaos on their machine without needing an instructor.
- Pedagogical journey support: when an instructor activates a BAC1–BAC5 journey, that same page switches to guided mode.
The separation is carried by the internal state of the controller, not by different endpoints. The studentModeEnabled flag controls self-service access and can be locked by the instructor to force students to follow the pedagogical journey.
Overview¶
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/chaos/student/status |
None | Full state (mode, journey, active levels) |
POST |
/api/chaos/student/performance |
None (license-gated) | Self-service performance level |
POST |
/api/chaos/student/scripting |
None (license-gated) | Self-service scripting level |
POST |
/api/chaos/student/business |
None (license-gated) | Self-service business level |
POST |
/api/chaos/student/functional |
None (license-gated) | Self-service functional level |
POST |
/api/chaos/student/security |
None (license-gated) | Self-service security level |
GET |
/api/chaos/student/performance/scenarios |
None (license-gated) | Catalog of 20 weather scenarios |
POST |
/api/chaos/student/performance/scenario |
None (license-gated) | Launch a preconfigured scenario |
POST |
/api/chaos/student/admin/mode |
Admin | Lock/unlock student mode |
The freemium wall — code 402¶
All student-driven endpoints (/performance, /scripting, /business, /functional, /security, /scenarios) check the current license before authorizing a level change. If the requested level exceeds the free limit, the response is 402 Payment Required:
{
"error": "LICENSE_REQUIRED",
"chaos": "performance",
"requested": 3,
"maxFree": 1,
"message": "Level 3 not available without license. Maximum: 1",
"portalUrl": "https://perfshop.io"
}
| Field | Description |
|---|---|
error |
Always LICENSE_REQUIRED |
chaos |
Name of the affected family (performance, scripting, business, functional, security) |
requested |
Level requested by the student |
maxFree |
Maximum level allowed without a license |
message |
Localized message (key student.license.required.message) |
portalUrl |
Commercial URL to obtain a license |
Default freemium limits¶
| Family | Max level without license | Accessible scenarios |
|---|---|---|
| Performance | 1 (Junior) | N1-01, N1-02 only |
| Scripting | 0 (disabled) | — |
| Business | 0 (disabled) | — |
| Functional | 0 (disabled) | — |
| Security | 0 (disabled) | — |
The Pro license unlocks all levels and all scenarios. See license.md.
402 does not block anything already running
A 402 prevents a student without a license from activating a high level, but it does not interrupt chaos already running. If the instructor has started a level 4 with their Pro license, all students experience the effects — they just cannot trigger any themselves.
GET /api/chaos/student/status¶
Returns the consolidated state for the student page: active mode, levels of each family, pedagogical journey in progress, license, etc.
Auth: none Recommended polling: every 15 seconds from the student page
Response — 200 OK¶
{
"studentModeEnabled": true,
"studentBlockedMessage": null,
"license": {
"active": false,
"plan": "FREE",
"maxLevels": {
"performance": 1,
"scripting": 0,
"business": 0,
"functional": 0,
"security": 0
}
},
"chaos": {
"performance": {
"cpu": 40,
"cpuRatio": 1,
"memoryLeakTarget": 0,
"gcPressure": 0,
"dbPool": 0,
"threadPool": 0,
"slowQueries": 0,
"deadlock": 0,
"network": 0
},
"scripting": { "level": 0, "levelName": "Disabled" },
"business": { "level": 0, "levelName": "Level 0 — Disabled", "anomalies": [] },
"functional":{ "level": 0, "levelName": "Level 0 — Disabled", "anomalies": [] },
"security": { "level": 0, "levelName": "Level 0 — Disabled", "faults": [] }
},
"activeScenarioId": null,
"pedagogique": {
"active": false,
"level": 0,
"hintsEnabled": true,
"session": null
}
}
| Key field | Description |
|---|---|
studentModeEnabled |
true if the student can drive self-service chaos |
studentBlockedMessage |
Message displayed if the mode is blocked by the instructor (otherwise null) |
license.active |
true if a Pro license is active |
license.maxLevels |
Maximum level allowed per family according to the license |
activeScenarioId |
ID of the currently running scenario (N1-01, N3-02, etc.) or null |
pedagogique.active |
true if an instructor journey is in progress — see pedagogique.md |
POST /api/chaos/student/performance¶
Activates a simplified Performance chaos level for the student. Unlike POST /api/admin/chaos/cpu (which tunes each parameter independently), this endpoint takes a global level 0–4 and applies a predefined configuration.
Auth: none (but license check)
Request¶
Level → CPU intensity mapping¶
| Student level | CPU intensity | Admin-equivalent level |
|---|---|---|
| 0 | 0 | No chaos |
| 1 | 40 | Light load |
| 2 | 60 | Moderate load |
| 3 | 80 | Heavy load |
| 4 | 95 | Extreme load |
The endpoint tunes only CPU — the other parameters (memory, DB pool, etc.) keep their current values. For granular control, the student must use POST /api/chaos/student/performance/scenario with a predefined scenario.
Response — 200 OK¶
This change resets activeScenarioId — if a scenario was running, it is disabled.
Error codes¶
| Code | Cause |
|---|---|
| 400 | Level outside [0–4] |
| 402 | Insufficient license (see freemium wall) |
| 403 | Student mode locked (key student.error.blocked) |
POST /api/chaos/student/scripting, /business, /functional, /security¶
These 4 endpoints share the same structure as /performance — they take a global level 0–4 and apply it to the corresponding family. They delegate to the underlying admin service after the license check.
Request¶
Response — 200 OK¶
{
"success": true,
"family": "business",
"level": 2,
"levelName": "Level 2 — Intermediate",
"activeAnomalies": ["A1", "A2", "A3", "A4", "A5", "A6", "A7"]
}
Error codes¶
Identical to the other student endpoints: 400, 402 (license), 403 (mode locked).
No license = no business/functional/security/scripting chaos
These 4 families have a freemium limit of 0 — no level can be activated without a license. This choice is deliberate: performance chaos is enough to illustrate the basic concepts standalone, while advanced families need a structured pedagogical context.
GET /api/chaos/student/performance/scenarios¶
Returns the catalog of 20 preconfigured weather scenarios. Each scenario is a multi-parameter combination of the performance chaos, designed to illustrate a real-world situation.
Auth: none (but filtered by license)
Response — 200 OK¶
{
"licensed": false,
"activeScenario": "",
"scenarios": [
{
"id": "N1-01",
"name": "Light Breeze",
"level": 1,
"requiresLicense": false,
"accessible": true
},
{
"id": "N1-02",
"name": "Morning Mist",
"level": 1,
"requiresLicense": false,
"accessible": true
},
{
"id": "N1-03",
"name": "Passing Squall",
"level": 1,
"requiresLicense": true,
"accessible": false
},
{
"id": "N4-05",
"name": "Perfect Storm",
"level": 4,
"requiresLicense": true,
"accessible": false
}
]
}
| Field | Description |
|---|---|
licensed |
true if a valid license is active |
activeScenario |
Identifier of the running scenario (empty string if none) |
scenarios[].id |
Identifier N<level>-<index> where level ∈ {1,2,3,4}, index ∈ {01..05} |
scenarios[].name |
Weather name localized via the scenario.N*-**.name key |
scenarios[].level |
Difficulty level 1–4 |
scenarios[].requiresLicense |
true if the scenario requires a license to launch |
scenarios[].accessible |
true if the current student can launch it (!requiresLicense || licensed) |
Each entry is built from the PerformanceScenario.toPublicMap() block enriched by the controller with the localized name and the accessible flag computed from the current license.
The 20 weather scenarios¶
Level 1 — Junior: Light Breeze, Morning Mist, Passing Squall, Network Drizzle, Passing Cloud Level 2 — Intermediate: Crosswind, Frozen Pond, Gust, Double Fog, Rising Tide Level 3 — Expert: Sandstorm, Deep Freeze, Cross Flood, Storm Front, Forming Cyclone Level 4 — Maestro: Hurricane, Collapse, Breaking Point, Tsunami, Perfect Storm
Free scenarios: by convention, the first 2 scenarios of level 1 (N1-01 Light Breeze and N1-02 Morning Mist) are accessible without a license. All others have requiresLicense: true.
Error codes¶
| Code | Cause |
|---|---|
| 403 | Student mode locked by the instructor |
POST /api/chaos/student/performance/scenario¶
Launches a scenario by its ID. If another scenario is running, it is replaced (mutual exclusivity).
Auth: none (but license check)
Request¶
Behavior¶
- Student mode check (403 if locked)
- If
scenarioIdis empty →chaosService.resetAll()+frontendChaosController.resetState()+activeScenarioId = null, returns{success, scenarioId: "", active: false} - Check that the scenario exists in
PerformanceScenario.ALL(keystudent.scenario.unknownif not) - If
requiresLicense == trueand no license →402 LICENSE_REQUIRED - Call
chaosService.resetAll()andfrontendChaosController.resetState()— full reset before applying - Conditional application of non-null parameters:
cpu,cpuRatio,memory,memoryHeapCap,dbPool,threadPool,slowQuery,deadlock,network, and the 4 frontend chaos (cpuBurn,memoryLeak,domFlood,fetchFlood) - Stores
activeScenarioId = scenarioId
Response — 200 OK (scenario launched)¶
Response — 200 OK (reset via empty scenarioId)¶
Error codes¶
| Code | Body | Cause |
|---|---|---|
| 400 | {"error": "Unknown scenario: X"} |
ID not in the catalog (key student.scenario.unknown) |
| 402 | {"error": "LICENSE_REQUIRED", ...} |
Non-freemium scenario, inactive license |
| 403 | {"error": "Student mode locked by instructor"} |
studentModeEnabled = false |
Exclusivity via resetAll()
Each scenario launch begins with a full reset of the performance chaos. This is intentional: scenarios are standalone configurations, not deltas. Launching N2-01 after N1-01 produces exactly the N2-01 parameters, not an accumulation.
POST /api/chaos/student/admin/mode¶
Locks or unlocks student mode. Admin endpoint — the instructor uses this endpoint to block self-service during a guided journey.
Auth: admin (session or X-Admin-Token)
Request¶
| Field | Type | Required | Description |
|---|---|---|---|
enabled |
boolean | yes | true to allow self-service, false to block it |
message |
string | no | Message displayed to the student (if enabled: false) |
Response — 200 OK¶
{
"success": true,
"studentModeEnabled": false,
"studentBlockedMessage": "Student mode disabled during the exam — resume at 2pm"
}
If enabled: false and message is absent, the default message is Student mode locked by instructor (key student.mode.disabled).
Error codes¶
| Code | Cause |
|---|---|
| 401 | Admin not authenticated |
Reconciliation with isAllChaosOff()¶
When student mode is locked and all chaos are at 0, the controller forces activeScenarioId = null — for consistency of the student-side display. This logic is in ChaosStudentController.isAllChaosOff().
curl example¶
# 1. Initial state
curl http://localhost:9080/api/chaos/student/status | jq
# 2. Launch the Light Breeze scenario (free)
curl -H "Content-Type: application/json" \
-d '{"scenarioId":"N1-01"}' \
http://localhost:9080/api/chaos/student/performance/scenario
# 3. Attempt a paid scenario → 402
curl -H "Content-Type: application/json" \
-d '{"scenarioId":"N3-02"}' \
http://localhost:9080/api/chaos/student/performance/scenario
# Response:
# { "error": "LICENSE_REQUIRED", "chaos": "performance",
# "requested": 3, "maxFree": 1, "portalUrl": "https://perfshop.io" }
# 4. Activate a Pro license (see license.md)
curl -H "Content-Type: application/json" \
-d '{"licenseKey":"PFSH-XXXX-XXXX-XXXX-XXXX"}' \
http://localhost:9080/api/license/activate
# 5. Retry the scenario → 200
curl -H "Content-Type: application/json" \
-d '{"scenarioId":"N3-02"}' \
http://localhost:9080/api/chaos/student/performance/scenario
# 6. The instructor locks student mode (admin required)
TOKEN=$(curl -s -H "Content-Type: application/json" \
-d '{"email":"admin@perfshop.fr","password":"admin"}' \
http://localhost:9080/api/admin/login | jq -r '.adminToken')
curl -H "X-Admin-Token: $TOKEN" -H "Content-Type: application/json" \
-d '{"enabled":false,"message":"Guided lab in progress"}' \
http://localhost:9080/api/chaos/student/admin/mode
Points of attention¶
Strict student / admin separation
The student page does not have reset endpoints. To reset, the student must:
- Set all levels back to 0 (one by one), OR
- Launch the N1-01 scenario (Light Breeze) which performs a reset before applying
Full reset via POST /api/admin/chaos/reset remains reserved for admin. This choice prevents a student from "erasing their tracks" after a failure demo — the instructor keeps control of the session.
Pedagogical journey compatibility
When a pedagogical journey is active (see pedagogique.md), self-service control remains allowed unless the instructor has explicitly locked it via /admin/mode. This flexibility lets a student combine guided exercises and free exploration in the same session.
Related links¶
pedagogique.md— BAC1–BAC5 pedagogical journeys (same controller)license.md— Pro license activationchaos-admin.md— instructor-side control endpoints (comparison)chaos/performance.md— detail of the 8 underlying parameterschaos/scenarios.md— detailed catalog of the 20 weather scenarios