/ INFIL_EXFIL_BOUNDARY.md
INFIL_EXFIL_BOUNDARY.md
1 # Infil/Exfil Boundary Documentation 2 3 ## Overview 4 5 GUNRPG implements a strict architectural boundary between **combat (infil)** and **operator management (exfil)** to ensure data integrity, prevent state corruption, and maintain a clear separation of concerns. 6 7 ## Key Concepts 8 9 ### Infil (Combat) 10 **Infil** refers to the combat phase where operators engage in tactical combat scenarios. During infil: 11 - Operators use a **copy** of their stats (snapshot) 12 - Combat state is transient and exists only for the duration of the session 13 - No operator progression or permanent state changes occur 14 - Combat produces **outcomes** but does not persist operator changes directly 15 16 ### Exfil (Operator Management) 17 **Exfil** refers to the out-of-combat phase where operator progression and management occur. During exfil: 18 - Operators are represented as event-sourced aggregates 19 - All state changes are recorded as **append-only events** 20 - Events are hash-chained for tamper detection 21 - This is the **ONLY** place where operator events may be committed 22 23 ## Architecture 24 25 ``` 26 ┌─────────────────────────────────────────────────────────────────┐ 27 │ INFIL (Combat) │ 28 ├─────────────────────────────────────────────────────────────────┤ 29 │ │ 30 │ CombatSession │ 31 │ ├─ Operator (snapshot/copy) │ 32 │ │ ├─ Health, Stamina, etc. (mutable during combat) │ 33 │ │ └─ Equipment, Stats (read-only from exfil) │ 34 │ │ │ 35 │ └─ Produces CombatOutcome │ 36 │ ├─ Damage taken │ 37 │ ├─ XP earned │ 38 │ ├─ Victory/defeat status │ 39 │ └─ Other combat results │ 40 │ │ 41 └─────────────────────────────────────────────────────────────────┘ 42 │ 43 │ CombatOutcome 44 ▼ 45 ┌─────────────────────────────────────────────────────────────────┐ 46 │ EXFIL (Operator Management) │ 47 ├─────────────────────────────────────────────────────────────────┤ 48 │ │ 49 │ OperatorExfilService │ 50 │ ├─ ProcessCombatOutcome(outcome) │ 51 │ │ ├─ Player reviews/confirms outcome │ 52 │ │ └─ Translates to operator events │ 53 │ │ │ 54 │ └─ Exfil-Only Actions │ 55 │ ├─ ApplyXp() │ 56 │ ├─ TreatWounds() │ 57 │ ├─ ChangeLoadout() │ 58 │ └─ UnlockPerk() │ 59 │ │ 60 │ OperatorAggregate (Event-Sourced) │ 61 │ ├─ State derived from events │ 62 │ ├─ Events are append-only │ 63 │ └─ Events are hash-chained │ 64 │ │ 65 │ IOperatorEventStore (LiteDB) │ 66 │ └─ Persists events with integrity checks │ 67 │ │ 68 └─────────────────────────────────────────────────────────────────┘ 69 ``` 70 71 ## Event Sourcing 72 73 ### Why Event Sourcing? 74 75 Operators are event-sourced to provide: 76 1. **Complete audit trail** - Every change to an operator is recorded 77 2. **Tamper detection** - Hash chaining prevents unauthorized modifications 78 3. **Time-travel debugging** - Can replay events to any point in history 79 4. **Offline-first design** - Events can be synced across devices (future) 80 5. **Clear transactional boundaries** - Events are atomic and ordered 81 82 ### Event Types 83 84 All operator events inherit from `OperatorEvent` and include: 85 86 - **OperatorCreatedEvent** - Initial operator creation (sequence 0) 87 - **XpGainedEvent** - Experience points awarded 88 - **WoundsTreatedEvent** - Health restoration 89 - **LoadoutChangedEvent** - Equipment changes 90 - **PerkUnlockedEvent** - Skill/perk unlocks 91 - **CombatVictoryEvent** - Combat victory (clears ActiveCombatSessionId, does not affect streak) 92 - **ExfilFailedEvent** - Failed exfil (resets streak) 93 - **OperatorDiedEvent** - Operator death (marks IsDead=true, resets streak) 94 95 ### Hash Chaining 96 97 Each event contains: 98 - `SequenceNumber` - Monotonically increasing counter 99 - `PreviousHash` - Hash of the previous event 100 - `Hash` - SHA256 hash of event contents + previous hash 101 - `Timestamp` - When the event occurred 102 103 This creates a tamper-evident chain: 104 ``` 105 Event 0: Hash A (prev: "") 106 Event 1: Hash B (prev: A) 107 Event 2: Hash C (prev: B) 108 Event 3: Hash D (prev: C) 109 ``` 110 111 If any event is modified, the hash chain breaks and the corruption is detected immediately on load. 112 113 ### Rollback on Corruption 114 115 When hash chain verification fails during load: 116 1. The system stops at the first corrupted event 117 2. All events from that point onward are deleted from storage 118 3. The operator is restored to the last valid state 119 4. No attempt is made to repair or rewrite corrupted events 120 121 This ensures deterministic recovery - operators always roll back to their last known-good state. 122 123 ## The Boundary Contract 124 125 ### Combat MUST NOT: 126 - ❌ Directly mutate operator aggregate state 127 - ❌ Append events to the operator event store 128 - ❌ Persist operator changes 129 - ❌ Grant XP, unlock perks, or modify loadout 130 131 ### Combat MUST: 132 - ✅ Use a **snapshot** of operator stats 133 - ✅ Produce a **CombatOutcome** at completion 134 - ✅ Include all relevant combat results in the outcome 135 - ✅ Remain deterministic and reproducible 136 137 ### Exfil MUST NOT: 138 - ❌ Access combat system internals 139 - ❌ Simulate or execute combat 140 - ❌ Modify combat state 141 142 ### Exfil MUST: 143 - ✅ Be the **ONLY** place events are committed 144 - ✅ Validate all state changes 145 - ✅ Verify hash chain integrity on load 146 - ✅ Allow player confirmation before committing outcomes 147 148 ## Usage Examples 149 150 ### Exfil Semantics 151 152 #### Exfil Streak 153 The `ExfilStreak` property tracks consecutive successful exfils: 154 - Increments by 1 on each successful `InfilEndedEvent` (`wasSuccessful=true`) 155 - Resets to 0 on: 156 - `ExfilFailedEvent` (retreat, abandon mission, etc.) 157 - `OperatorDiedEvent` (operator death) 158 - Event chain rollback past the last successful exfil 159 160 The streak is informational only (no gameplay effects yet) but provides foundation for future features like bonus rewards or difficulty modifiers. 161 162 #### Operator Death 163 Once an operator dies (`IsDead = true`): 164 - All mutating operations are rejected at the service level (via `OperatorExfilService`) 165 - Health is set to 0 166 - Exfil streak is reset to 0 167 - `OperatorExfilService` will reject any further attempts to append events for this operator 168 - The underlying event store does not enforce this constraint by itself (allows replay for audit) 169 - The aggregate can still replay historical events for audit purposes 170 171 The `IsDead` flag is derived from events - it's not persisted separately, ensuring event sourcing integrity. 172 173 ### Creating an Operator (Exfil) 174 ```csharp 175 var service = new OperatorExfilService(eventStore); 176 var result = await service.CreateOperatorAsync("Operative Alpha"); 177 var operatorId = result.Value; 178 ``` 179 180 ### Starting Combat (Infil) 181 ```csharp 182 // Load operator from exfil 183 var loadResult = await exfilService.LoadOperatorAsync(operatorId); 184 var aggregate = loadResult.Value; 185 186 // Create combat snapshot 187 var combatSnapshot = aggregate.CreateCombatSnapshot(); 188 189 // Start combat session with the snapshot 190 var session = CombatSession.Create(combatSnapshot); 191 ``` 192 193 ### Completing Combat (Boundary) 194 ```csharp 195 // Combat produces outcome 196 var outcome = CombatOutcome.FromSession(session); 197 198 // Outcome flows to exfil for processing 199 var result = await exfilService.ProcessCombatOutcomeAsync( 200 outcome, 201 playerConfirmed: true 202 ); 203 ``` 204 205 ### Applying Exfil Actions 206 ```csharp 207 // Only in exfil - apply XP 208 await exfilService.ApplyXpAsync(operatorId, 150, "Victory"); 209 210 // Only in exfil - treat wounds 211 await exfilService.TreatWoundsAsync(operatorId, 50f); 212 213 // Only in exfil - change loadout 214 await exfilService.ChangeLoadoutAsync(operatorId, "AK-47"); 215 216 // Only in exfil - unlock perk 217 await exfilService.UnlockPerkAsync(operatorId, "Fast Reload"); 218 219 // Only in exfil - complete successful exfil (increments streak) 220 await exfilService.CompleteExfilAsync(operatorId); 221 222 // Only in exfil - fail exfil (resets streak) 223 await exfilService.FailExfilAsync(operatorId, "Retreat"); 224 225 // Only in exfil - operator death (permanent, resets streak) 226 await exfilService.KillOperatorAsync(operatorId, "Combat casualty"); 227 ``` 228 229 ## Data Integrity 230 231 ### Hash Verification 232 On load, the system verifies: 233 1. Each event's hash matches its computed hash 234 2. Each event's previous hash matches the prior event's hash 235 3. Sequence numbers are consecutive with no gaps 236 237 If any check fails, the system: 238 1. Stops processing at the corrupted event 239 2. Rolls back (deletes) all events from that point onward 240 3. Returns only the valid events before the corruption 241 4. The aggregate is restored to the last known-good state 242 243 ### Append Constraints 244 When appending events: 245 1. Sequence must be exactly CurrentSequence + 1 246 2. Previous hash must match the last event's hash 247 3. Event hash must verify correctly 248 4. No concurrent modifications (enforced by store) 249 250 ## Future Enhancements 251 252 ### Planned Features 253 - **Multi-player sync** - Events can be synced across clients 254 - **Replay system** - Rebuild operator state at any point in time 255 - **Digital signatures** - Add cryptographic signing for authentication 256 - **Conflict resolution** - Handle concurrent modifications gracefully 257 - **Event migrations** - Versioning and schema evolution 258 259 ### Not Included Yet 260 - ❌ Cryptographic keys (hash-only for now) 261 - ❌ Authentication/authorization 262 - ❌ Network sync 263 - ❌ Conflict resolution for concurrent edits 264 265 ## Testing 266 267 The implementation includes comprehensive tests: 268 - `OperatorEventTests` - Event creation, hashing, chain verification, new event types 269 - `OperatorAggregateTests` - Event replay, state derivation, streak tracking, death handling, rollback 270 - `LiteDbOperatorEventStoreTests` - Persistence, integrity checks, rollback behavior 271 - `OperatorExfilServiceTests` - Service operations, validation, dead operator constraints 272 273 All 77 operator tests pass, ensuring the boundary is maintained correctly. 274 275 ## Summary 276 277 The infil/exfil boundary provides: 278 - ✅ **Correctness** - Clear separation prevents state corruption 279 - ✅ **Integrity** - Hash chaining detects tampering 280 - ✅ **Clarity** - Explicit boundaries make the system easier to reason about 281 - ✅ **Testability** - Each side can be tested independently 282 - ✅ **Future-proof** - Event sourcing enables powerful future features 283 284 This design favors **correctness, integrity, and clarity over convenience**, as specified in the requirements.