# NEXSTREAM Audit — 2026-05-18

120 Kotlin files, 27.5K LOC. Build passes after fixing 262 compile errors.

## Multi-Model Audit vs File-Reading Reality

### Claims verified FALSE (all 3 models wrong):
| Claim | Reality |
|-------|---------|
| "No token storage security" | `SecureTokenStore` (Keystore+AES256) exists — but only used for debrid tokens |
| "No addon input validation" | kotlinx.serialization typed serializers — not raw JSON |
| "No network timeouts" | 8s per-addon, per-service qualifiers configured |
| "Race condition in FallbackEngine" | Sequential suspend fun, no shared mutable state |
| "No ProGuard rules" | 30+ lines in proguard-rules.pro |
| "Player no onPlayerError" | Handler exists but doesn't auto-recover |

### Claims verified TRUE:
| Claim | File |
|-------|------|
| Trakt/TMDB tokens in plain DataStore | `PreferencesManager.kt:430-465` |
| PlayerManager scope never cancelled | `PlayerManager.kt:75` |
| No DB migration objects | `NexStreamDatabase.kt:24` — version=1 |
| Zero test coverage | Empty test directories |
| SettingsViewModel 65-field combine | `SettingsViewModel.kt:195-260` |

### Critical issues (from file reading) — ALL 3 FIXED 2026-05-18:
1. **C1** ✅ FIXED: `preferencesManager` stores `traktAccessToken`, `traktRefreshToken` as plain `stringPreferencesKey` → migrated to `SecureTokenStore` (Keystore+AES256) with `MutableStateFlow` wrapper for reactive UI
2. **C2** ✅ FIXED: `PlayerManager.scope` = `CoroutineScope(SupervisorJob() + Dispatchers.Main)` never cancelled → extracted `scopeJob` field, added `dispose()` cancelling it, `release()` calls `dispose()`
3. **C3** ✅ FIXED: `playWithHeaders(url, headers)` builds MediaItem without applying headers → stores `currentHeaders`, injects via `DefaultHttpDataSource.Factory.setDefaultRequestProperties()` before player creation
   - **Note**: headers only applied when `playWithHeaders` is the first play call (lazy player init). For dynamic header changes, player would need recreation.

### 262 compile errors fixed (categories):
- Duplicate `import androidx.tv.material3.Border` (9 files)
- `focused=` → `focusedBorder=` in border() calls (15+ files)
- `shape = RoundedCornerShape/CircleShape` on Surface calls (15+ files)
- `color = NexColors.xxx` on Surface calls (10 files)
- `buttonColors(` → `ButtonDefaults.colors(` (7 files)
- Missing imports: ButtonDefaults, fillMaxWidth, Column, height, collectAsState, remember, flow (12+ files)
- `MediaType.get()` → `"application/json".toMediaType()` (1 file)
- `crossinline` on non-inline function (1 file)
- DAO suspend wrapped in `flow{}` (2 files)
- Missing domain model: `DiscoverableAddon.kt` (created)
- PlayerManager double `.build()` (1 file)
- PlayerScreen broken `.onKeyEvent` API removed (1 file)
- Double `ButtonDefaults.ButtonDefaults` prefix (7 files)
- Empty `.background()` calls from color removal (1 file)
