The engine runtime is the mutable half ofDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/0x-unkwn0wn/simterm/llms.txt
Use this file to discover all available pages before exploring further.
simterm-engine. GameState holds every piece of live session data, and the actions module provides pure functions that take a &mut GameState and transform it. No frontend code is required to drive the loop — you can run an entire campaign headlessly by calling actions in sequence and reading the game.logs vector after each one.
GameState
GameState is constructed from a loaded Campaign and starts the first mission automatically:
Key fields
| Field | Type | Description |
|---|---|---|
campaign | Campaign | The loaded campaign definition (immutable throughout the session) |
target | TargetNode | The currently active host; swapped by pivot_to in multi-host missions |
phase | Phase | Current kill-chain phase for the active host |
detection | DetectionState | Trace accumulator for the active host |
detection_limit | f32 | Threshold from the mission; reaching it triggers defeat |
intel | Vec<IntelFinding> | All findings discovered so far on the active host |
is_root | bool | Whether the player has root on the active host |
pivoted | bool | Whether connect has established the bastion tunnel (Pivot entry only) |
discovered_ports | Vec<u16> | Ports revealed by recon on the active host |
cwd | Vec<String> | Current VFS working directory as path components; empty = / |
extra_skill | f32 | Cumulative skill bonus earned from loot across the whole campaign |
creds | Vec<String> | Credential inventory accumulated across all missions |
foothold_tokens | Vec<String> | Reusable credential tokens for deterministic login on accepting hosts |
has_wordlist | bool | Whether a wordlist has been looted (enables cracking certain hashes with john) |
flags | Vec<String> | Active campaign flags set by declarative commands |
logs | Vec<String> | All output lines from every action taken this session |
outcome | Option<GameOutcome> | None while running; Some(Victory) or Some(Defeat) when over |
clock | u32 | Tick counter for the current mission |
campaign_clock | u32 | Cumulative ticks across all missions |
achievements | Vec<AchievementId> | Built-in engine achievements unlocked |
campaign_achievements | Vec<String> | Data-driven campaign achievement ids unlocked |
Phase
Phase represents the kill-chain progression for each host. It only ever advances forward.
| Phase | Entered when… |
|---|---|
Recon | Mission starts (default) |
Enum | At least one port is discovered (or Cold entry with seeded ports) |
Exploit | At least one intel finding exists |
Post | The player gains a foothold via exploit or login |
GameOutcome
Defeat is triggered by trace reaching detection_limit or by the mission clock exceeding time_limit. A Victory fires when the last mission is completed (objective exfiltrated or root achieved, depending on the mission definition).
actions module
All player-facing actions live insimterm_engine::actions. Every function takes &mut GameState and appends its output to game.logs. None of them return display strings; the caller reads game.logs.
Recon
nmap scan. Discovers all services on the current target in one pass, advances to Phase::Enum, and accumulates noise. Blocked until connect is called on Pivot entry missions. Extra noise penalty applies on Passive entry missions.
Passive entry missions.
Pivot entry missions. Must be called before recon or sniff can discover the real target.
Enumeration
toolbox::TOOLS ("probe", "nikto", "gobuster", "enum4linux", "hydra", "sqlmap"). Using a tool with an affinity match produces real findings and fewer false positives; a mismatch is noisy and unreliable. Pushes IntelFinding entries into game.intel and advances to Phase::Exploit once at least one finding exists.
searchsploit-style local research on a finding by its public id. Low noise. Accumulates positive and negative read counts; the consensus converges on the truth over multiple calls but no single call is deterministic (78% accuracy per read).
Exploitation
Reliable exploits are deterministic (always succeed). A false-positive finding backfires and adds extra noise (+25 trace). Failed exploits also add noise (+18 trace).
accepts_token to a value the player already holds in game.foothold_tokens. Lower noise than exploit, but still leaves a trace.
Post-exploitation
game.privesc_unlocked is true (player collected a file with loot.privesc_key = true, or used a local enumeration tool that identified the vector), success is guaranteed. Otherwise a probability roll is used based on skill and root_difficulty. On a mission with no objective, a successful privesc immediately completes the mission.
VFS operations
All VFS actions require a foothold (Phase::Post) first.
fs_cat applies Loot the first time a file is read (skill bonus, credential, token, privesc key). fs_exfil checks that the path matches game.objective and calls complete_level on success.
Offline work
john cracks a hash previously read with fs_cat; the probability depends on skill and hash strength, and has_wordlist adds a significant bonus for hashes that require one. strings/disasm/solve implement the binary reversing challenge. decode_cmd handles base64 and xor file decoding.
Local enumeration (POST)
"linpeas", "sudo", "suid", "sysinfo") that can reveal the host’s local_privesc vector. linpeas covers all vector types; the others are specific. On success sets game.privesc_unlocked = true, making privesc deterministic.
Declarative command dispatch
CampaignCommand by trigger verb, evaluates all its CommandConditions, and applies its CommandEffects in order. Returns true if a matching available command was found and executed, false if the verb should fall through to easter-egg lookup. This is the integration point between the frontend’s command parser and the engine’s declarative command system.
TerminalCommand by verb, rendering its output lines through the template engine (substituting {clock}, {user}, {host}, {env:VAR}, and $VAR). Sets game.last_exit to the command’s configured exit code. Returns true if a matching terminal command was found and executed.
ShellOutput
ShellOutput is returned by sysemu::run — the synthesized POSIX shell command layer.
sysemu::run(state, verb, args) handles uname, hostname, id, whoami, ps, netstat/ss, ifconfig, ip, env, export, grep, head, tail, wc, and file. All output is synthesized from the live TargetNode and VFS — no per-command authoring is needed in campaign data. Returns None if the verb is not a synthesized system command (the frontend should then try campaign commands and easter eggs).
Detection model
Trace accumulates inDetectionState as a running f32. Every action that uses network resources calls state.detection.add_noise(cost) and then state.check_detection(). Passive-phase dwell (time spent in RECON/ENUM/EXPLOIT without a foothold) also adds a small trickle via advance_clock.
When detection >= detection_limit, check_detection sets outcome = Some(GameOutcome::Defeat).
On missions with reactive: true the blue team escalates in stages as trace crosses percentage thresholds (35 %, 60 %, 80 % of the limit). Each stage adds a defense_penalty that reduces exploit and privesc success probabilities and may inject additional noise. Defense progression is cumulative and irreversible within a mission.
Asset sources
Asset sources decouple file-system access from campaign metadata. The engine’sOpenCampaign pairs a Campaign with an AssetSource so the frontend can read music or binary files without knowing whether the campaign came from a directory or an archive.
| Implementation | Use case |
|---|---|
DirAssetSource::new(root) | Campaign loaded from a directory; resolves logical paths against root |
MemAssetSource | In-memory assets loaded from an archive or injected in tests; supports insert(path, data) |
NoAssets | Placeholder that rejects all reads; use when audio is not needed |
Building a minimal headless frontend
The example below drives a campaign through the core RECON → ENUM → EXPLOIT loop without any terminal UI:Data Model
Immutable types that GameState reads
Validation API
Validate a campaign before constructing GameState