Skip to content

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:

  1. Standalone self-service: a student training alone can activate chaos on their machine without needing an instructor.
  2. 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": 1 }

Level → CPU intensity mapping

// ChaosStudentController.PERF_CPU_INTENSITY
{ 0, 40, 60, 80, 95 }
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

{
  "success": true,
  "level": 1,
  "cpu": 40,
  "cpuRatio": 1,
  "activeScenarioId": null
}

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

{ "level": 2 }

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

{ "scenarioId": "N1-01" }

Behavior

  1. Student mode check (403 if locked)
  2. If scenarioId is empty → chaosService.resetAll() + frontendChaosController.resetState() + activeScenarioId = null, returns {success, scenarioId: "", active: false}
  3. Check that the scenario exists in PerformanceScenario.ALL (key student.scenario.unknown if not)
  4. If requiresLicense == true and no license → 402 LICENSE_REQUIRED
  5. Call chaosService.resetAll() and frontendChaosController.resetState() — full reset before applying
  6. Conditional application of non-null parameters: cpu, cpuRatio, memory, memoryHeapCap, dbPool, threadPool, slowQuery, deadlock, network, and the 4 frontend chaos (cpuBurn, memoryLeak, domFlood, fetchFlood)
  7. Stores activeScenarioId = scenarioId

Response — 200 OK (scenario launched)

{
  "success": true,
  "scenarioId": "N1-01",
  "name": "Light Breeze",
  "level": 1,
  "active": true
}

Response — 200 OK (reset via empty scenarioId)

{
  "success": true,
  "scenarioId": "",
  "active": false
}

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

{
  "enabled": false,
  "message": "Student mode disabled during the exam — resume at 2pm"
}
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:

  1. Set all levels back to 0 (one by one), OR
  2. 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.