Simulator Builder — Admin Tutorial
This is the operator guide for the Cyfotok config-driven Simulator Builder. Use it to author labs without writing TypeScript packages. Students play your configs through the shared runtime lab slug config-simulator.
| Builder (short URL) | `/create-simulator` |
| Builder (admin) | `/admin/create-simulator` |
| Library | `/admin/simulators` |
| In-app docs | `/admin/tutorial` |
| Play slug | config-simulator |
| Play URL shape | /lab/config-simulator?configId=<id>&token=<jwt> |
| Storage | Browser localStorage + JSON import/export |
| Templates | Bundled under simulator-configs/templates/ |
Important: The builder is ungated (no lab JWT). Playing a published config still requires a normal lab token for slug config-simulator.How to use this tutorial
- Keep this doc open beside the builder.
- Complete Part A once (orient + first publish).
- Use Part B for the engine you are authoring.
- Use Part C for sharing, play tokens, and troubleshooting.
Checkpoint style:
- Do: action you take
- See: what the UI should show
- Why: why it matters
- Next: where to go
Table of contents
Part A — Orient & first lab
- What the builder is
- Open the builder and library
- Wizard steps overview
- Pick a template
- Meta & intro
- Seed data vs content
- Live preview
- Validate, save, publish, export
- Play your lab with a JWT
Part B — Engines
Part C — Operations
- Library: templates, edit, export, delete
- Import / export JSON
- Query parameters & deep links
- Flag design guidelines
- Troubleshooting
- Appendix: field reference
Part A — Orient & first lab
1. What the builder is
Why
Historically each lab lived as a TypeScript package under labs/<slug>/. The Simulator Builder lets you author config JSON that the single runtime lab config-simulator interprets.
Engines
| Engine | What students get |
|---|---|
step | Guided info + quiz steps |
edr | Reduced live EDR console + scenario objectives |
siem | Reduced live SIEM console + scenario objectives |
web-pentest | Challenge range (XSS, SQLi, upload, auth, …) |
linux-shell | Fake bash terminal with authored commands & workflow |
What is *not* codegen
Publishing does not create a new labs/<slug>/ folder. It saves a definition (localStorage and/or JSON file) that config-simulator loads by configId.
2. Open the builder and library
Do
- From the vuln home page, click Simulator builder, or open
/create-simulator. - Or open
/admin/create-simulator//admin/simulators.
See
- Sticky admin nav: Create · Library · Docs · Home
- Soft grid + emerald glow background
- Create page: hero, 5-step progress, template cards, live preview pane
Why
Create is the authoring wizard. Library lists browser-saved drafts/published configs and one-click clones of bundled templates.
Next
Stay on Create for your first lab.
3. Wizard steps overview
| Step | Name | Purpose |
|---|---|---|
| 1 | Template | Choose starter, blank, or import JSON |
| 2 | Meta & intro | Title, difficulty, tags, final flag, briefing |
| 3 | Seed data | Inventory / shell settings / static seeds (engine-specific) |
| 4 | Content | Scenarios, steps, challenges, or command workflow |
| 5 | Review | Validate, save draft, publish, export, raw JSON |
You can click completed step pills to jump back. Steps after Template stay disabled until a definition exists.
The right column is a live preview of the student experience (reset anytime).
4. Pick a template
Do
- On step Template, choose a starter card (recommended first time), or a blank shell for the same engine.
- Or paste exported JSON → Import & continue.
- Or open Library / use
?template=<id>.
See
- Message like “Loaded linux-shell-starter”
- Wizard advances to Meta & intro
- Preview shows the lab intro
Starter IDs
| Catalog id | Bundled play id |
|---|---|
edr-starter | tpl-edr-starter |
siem-starter | tpl-siem-starter |
step-starter | tpl-step-starter |
web-pentest-starter | tpl-web-pentest-starter |
linux-shell-starter | tpl-linux-shell-starter |
Deep link example:
/create-simulator?template=linux-shell-starterWhy
Starters ship with working scenarios so you learn by editing, not inventing every field.
Next
Edit Meta & intro.
5. Meta & intro
Do
Fill:
| Field | Notes |
|---|---|
| Title | Student-facing lab name |
| Description | Short catalog blurb (max ~500 chars) |
| Difficulty | beginner · intermediate · advanced |
| Tags | Comma-separated topics |
| Final flag | Awarded when the lab’s win condition is met |
| Intro title / body / bullets | Briefing screen before play |
See
Preview intro updates as you type (refresh preview if needed).
Why
finalFlag is the primary completion reward for every engine. Engine-specific “unlock” flags (if any) are optional extras — see Flag design.
Next
Open Seed data, then Content.
6. Seed data vs content
Mental model:
| Layer | Meaning | Examples |
|---|---|---|
| Seed | World / environment that exists before the scenario runs | Endpoints, hosts, fake FS, env vars, blocked patterns |
| Content | What the student must do | Scenarios + objectives, quiz steps, web challenges, shell commands |
Linux shell: Seed = shell chrome + FS + policy; Content = Terminal workflow (commands).
7. Live preview
Do
- Interact with the preview like a student.
- Click Reset / Refresh preview after large edits.
See
Intro → play surface → completion overlay when win conditions are met.
Why
Catch match-rule mistakes (especially shell/web) before publish.
8. Validate, save, publish, export
Do (Review step)
- Confirm the green Definition is valid banner (or fix listed Zod errors).
- Save draft — keep iterating in this browser.
- Publish — marks
status: "published"in localStorage. - Export JSON — download a portable file for git, sharing, or another machine.
- Optional: Edit raw JSON → Apply JSON (advanced).
See
Toast/message with saved id, e.g. cfg-linux-….
Why
- Draft vs published is metadata for you; both can be played if you know the id.
- Export is the durable backup — localStorage is per-browser and can be cleared.
Next
9. Play your lab with a JWT
Do
- Issue a lab token whose claim
labis exactlyconfig-simulator(see root `HOW_TO_WORK.md` /npm run sign-token). - Open:
/lab/config-simulator?configId=<your-id>&token=<jwt>For bundled starters without saving:
/lab/config-simulator?configId=tpl-linux-shell-starter&token=<jwt>See
Same experience as the builder preview, under normal lab chrome and token middleware.
Why
Builder preview skips lab auth. Production play must use the RS256 gate like every other lab.
Resolution order
- Saved config in this browser’s localStorage matching
configId - Else bundled template id (
tpl-…or catalog alias)
Part B — Engines
10. Engine cheat sheet
| Engine | Win condition (final flag) | Author focus |
|---|---|---|
step | Complete all steps correctly | Step kinds + answers |
edr | Complete all scenario objectives | Endpoints, beats, objectives |
siem | Complete all scenario objectives | Sources, rules, beats, objectives |
web-pentest | Complete required challenges | Challenge kinds + success patterns |
linux-shell | Succeed every required command | Workflow gates + outputs |
11. Step quiz labs
When to use
Short lessons, concept checks, ordering exercises — no live console.
Content: step kinds
| Kind | Student task |
|---|---|
info | Read content, continue |
single_choice | Pick one correct option |
multi_choice | Pick all correct options |
order_list | Put items in correct order |
Do
- Template → Step Quiz Starter (or blank step).
- Meta → set
finalFlag. - Content → add/edit steps; set correct option ids carefully.
- Preview: walk the quiz; finish → Final Success.
Tips
- Option / item ids must match
correctOptionId/correctOptionIds/correctOrderIds. - Keep
infosteps short; put depth in the intro briefing.
12. EDR labs
When to use
Endpoint detection & response training: investigate hosts, alerts, IOCs, containment-style objectives.
Seed
Typical seeds:
- Endpoints — hostname, user, IP, OS, risk, health
- Alerts / IOCs / MITRE — static catalog entries
Content: scenarios
Each scenario has:
- Title / description
- Timed beats (spawn process, network, alert, …) that mutate the console over simulation time
- Objectives — typed actions the student must perform (
isolate_endpoint,kill_process_name,open_alert, …)
Do
- Start from EDR Starter.
- Adjust endpoints so objective targets match real ids/names.
- Edit objectives’
targetfields to match seed data (hostname, hash, process name). - Preview → launch scenario → complete objectives → flag.
Tips
- Objective
targetmismatches are the #1 “I did the action but it didn’t count” bug. - Keep beat timelines short for training labs.
13. SIEM labs
When to use
Log/search/incident workflow training.
Seed
Sources, hosts, users, detection rules, seed alerts/IOCs/MITRE.
Content: scenarios
Beats inject logs/alerts/incidents; objectives include run_search, open_incident, assign_incident, enable_rule, etc.
Do
- Start from SIEM Starter.
- Align objective targets with rule names, alert ids, or search strings your UI expects.
- Preview the investigation path end-to-end.
Tips
Same as EDR: targets must match seed identifiers exactly.
14. Web pentest labs
When to use
Browser-based vuln challenges in a sandboxed player (XSS stays in iframes).
Challenge kinds (overview)
| Kind | Idea |
|---|---|
reflected_input | Reflect payload; optional WAF blockedPatterns |
stored_form | Multi-field stored XSS-style render |
file_upload_sim | Filename/content inspection (simulated) |
sqli_form | Fake SQL string + tautology / pattern success |
auth_form | Weak password / hidden field / alg=none style modes |
open_redirect | Dangerous redirect parameter |
path_traversal | Fake FS read via ../ |
idor | Object id access control |
cmd_injection_sim | Simulated command injection |
ssrf_sim | Simulated SSRF allow/deny |
csrf_token_bypass | Token missing/wrong handling |
cookie_tamper | Client cookie privilege change |
info | Reading / interstitial |
Success is driven by successPatterns (includes / equals / regex).
Do
- Start from Web Pentest Starter.
- For each challenge: set prompt, hints, patterns, and sample payloads.
- Preview each challenge; confirm completion marks.
Tips
- Prefer
includespatterns for teaching labs; useregexwhen you need precision. - WAF patterns run before success checks on reflected challenges.
15. Linux shell labs (workflows)
This engine is built for multi-command missions with a single final flag.
Core ideas
- Add many commands — each with its own match rules and outputs.
- Mark only mission-critical ones as Required for final flag.
- Optional commands never block completion.
- Use prerequisites to lock tools until earlier steps succeed.
- Award `meta.finalFlag` only after every required command succeeds.
- Leave per-command intermediate unlock flags empty unless you want bonus breadcrumbs.
Seed (shell settings)
| Area | Purpose |
|---|---|
| User / hostname / cwd / home / prompt | Prompt chrome |
| Welcome lines | Banner on start |
Env (KEY=value) | Available to templates / env builtin |
| Fake FS | `path\ |
| Blocked patterns | Lab policy (e.g. block rm -rf) |
| Builtins / tab / clear | Quality-of-life toggles |
Stdout templates support: {{user}}, {{hostname}}, {{cwd}}, {{home}}, {{pwd}}.
Content: Terminal workflow
#### Workflow settings
- Mission title / description — shown in the player sidebar
- Final flag — same as Meta (editable here for convenience)
- Show numbered mission progress — required steps checklist
#### Per command
| Control | Purpose |
|---|---|
| Required for final flag | Counts toward completion |
| Match mode / pattern | exact, prefix, or regex selects the command |
| Default success output | Stdout on success |
| + Output variant | Alternate stdout when a secondary pattern matches |
| + Error rule | Wrong usage / wrong target → stderr (does not complete) |
| + Alias | Extra patterns that select this same command |
| Prerequisites | Checkbox gates (must complete those ids first) |
| Hint + reveal after N fails | Teaching aid |
| Intermediate unlock flag | Optional; usually leave blank |
#### Authoring a chain (recommended)
Do
- Template → Linux Shell Starter (or blank Linux shell).
- Content → review the workflow preview (
whoami → ping → nmap). - + Required step to append another gated step (auto-chains to previous required).
- + Optional command for practice tools that should not block the flag.
- For each tool, add error rules (wrong host) and success variants (e.g.
ping -c 4 …). - Preview: try locked tools early → see “Permission denied…”; complete required set → Final Success with only the final flag.
See
- Sidebar: numbered mission progress + catalog (required / optional / locked)
- After all required succeed → completion overlay with
finalFlag
Why
Students practice a realistic path; you control pacing with gates; you do not spam flags on every command.
Match evaluation order (simplified)
- Blocked line patterns (lab policy)
- Builtins (
clear,help, …) - Authored commands (primary match or alias)
- Inside a command: missing prereqs → error; else error rules; else success variants; else default success
- FS builtins (
cd,ls,cat, …) if enabled - Unknown command template
Part C — Operations
16. Library: templates, edit, export, delete
Do
Open `/admin/simulators`.
Bundled templates
- Use in builder →
/admin/create-simulator?template=<id>(fresh clone, new id) - Short link →
/create-simulator?template=<id>
Saved configs
| Action | Effect |
|---|---|
| Edit | ?edit=<id> loads that localStorage config |
| Export | Download JSON |
| Delete | Remove from this browser only |
17. Import / export JSON
Export
Review → Export JSON, or Library → Export.
Import
Template step → paste → Import & continue, or Review → Edit raw JSON → Apply.
Why
Move labs between machines, commit JSON under simulator-configs/, or hand off to another author.
Tip
After hand-editing JSON, always hit Review validation before publish.
18. Query parameters & deep links
| URL | Meaning |
|---|---|
/create-simulator | Alias → admin create |
/admin/create-simulator | New / continue wizard |
?template=linux-shell-starter | Clone bundled starter |
?edit=<configId> | Open saved config |
/admin/simulators | Library |
/admin/tutorial | This tutorial (in-app) |
/lab/config-simulator?configId=… | Play (needs JWT) |
19. Flag design guidelines
| Pattern | Use when |
|---|---|
| Single final flag | Default. Set meta.finalFlag; mark required steps/objectives only. |
| Intermediate `unlockFlag` (shell) | Rare. Prints FLAG: … on that command’s success — does not replace the final flag. |
| No required commands (shell) | Lab never auto-completes — always mark ≥1 required. |
Suggested naming:
cyfotok{short_lab_goal_complete}20. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Validation errors on Review | Missing fields / bad enums | Read path + message; fix in UI or JSON |
| Preview blank | No template chosen | Pick starter or import |
| Shell never completes | Zero required commands | Mark required steps |
| Shell tool always denied | Prerequisites not done / wrong ids | Check prerequisite checkboxes |
| Wrong stdout | Variant/error matched first | Reorder rules; tighten patterns |
| EDR/SIEM objective stuck | target ≠ seed id/name | Align targets |
| Play 401 / unauthorized | JWT missing or lab ≠ config-simulator | Re-issue token |
| Play loads wrong lab | Bad configId | Confirm id in Library / export |
| Lost draft | Cleared site data | Restore from exported JSON |
21. Appendix: field reference
Shared (meta / intro)
meta.title,meta.description,meta.difficulty,meta.tags[],meta.finalFlagintro.title,intro.body,intro.bullets[]status:draft\|publishedengine: one of the five kindsversion:1
Linux workflow
title,description,showMissionProgress
Linux commands[]
id,name,synopsis,hintmatch/aliases[]—{ mode, pattern, flags? }success—{ stdout, exitCode, delayMs }successVariants[]—{ id, when, stdout, exitCode }errors[]—{ id, when, stderr, exitCode }requiresCommandIds[],required,unlockFlag?,showInCatalog,revealHintAfterFails
Schema source of truth
TypeScript Zod schemas live in `lib/simulator-builder/schema.ts`. When the UI and this doc disagree, trust the schema + live preview.
Quick start (10 minutes)
- Open `/create-simulator?template=linux-shell-starter`.
- Skim Meta; keep the starter final flag or rename it.
- Open Content → note required chain and optional
curl/id. - Preview:
whoami→ping cyfotok.com→nmap cyfotok.com→ flag. - Publish → copy play path from Review.
- Sign a
config-simulatortoken and open/lab/config-simulator?configId=<id>&token=….
You are ready to author your own workflow.