Local Storage¶
All user data is stored locally on Windows under %LocalAppData%\ApexLab\. Nothing is deleted automatically — the user controls their own data.
4. Local Storage — Session Catalog (SQLite)¶
File: %LocalAppData%\ApexLab\sessions.db
Why it exists: CSV files contain all data but reading them is slow. The catalog caches key metadata so the session list UI loads instantly without re-parsing every file.
sessions table¶
| Column | Type | Why it exists | How it's set | When it changes |
|---|---|---|---|---|
id |
INTEGER PK AUTOINCREMENT | Stable local key for settings (LastSessionCatalogId) |
Auto-assigned on insert | Never |
track |
TEXT | UI display; search filter | From TelemetrySessionMetadata.TrackName on import |
On UpdateSessionSnapshot() if metadata improves |
vehicle |
TEXT | UI display; search filter | From TelemetrySessionMetadata.VehicleName |
On snapshot update |
file_path |
TEXT UNIQUE | Locates the CSV file for loading | Set at RegisterFile() time; unique constraint prevents duplicates |
Never (file moves break the link) |
imported_at |
TEXT | Sort order; "catalogued" timestamp in UI | DateTime.UtcNow at RegisterFile() |
Never |
notes |
TEXT | User-authored session notes | Set on import dialog; editable from session detail | On user edit |
game |
TEXT | Game filter in session library | From GameSourceId.ToString() |
On snapshot update |
is_favorite |
INTEGER (0/1) | ★ filtering and display | Set via ToggleFavorite() command |
On user toggle |
tags |
TEXT | Future tag-based filtering (V2) | Not yet populated | Reserved |
lap_count |
INTEGER | Shown in session list without reading CSV | From SessionData.Laps.Count on import |
On snapshot update |
metadata_json |
TEXT | Full TelemetrySessionMetadata for dashboard subtitle and weather display |
JSON-serialized on import | On snapshot update |
best_lap_seconds |
REAL | Personal best comparison; shown as subtitle | From SessionData.BestValidLap.LapTimeSeconds |
On snapshot update |
synced_at |
TEXT | Indicates whether session has been uploaded to cloud | Set by SyncService.MarkSynced() after successful API upload |
Once, on first successful sync |
cloud_session_id |
TEXT | Required to submit this session to the leaderboard | Returned by POST /v1/sessions; stored by MarkSynced() |
Once, on sync |
5. Local Storage — Session Files (CSV)¶
Location: %LocalAppData%\ApexLab\Sessions\sess_{guid}.csv
Why it exists: Full telemetry cannot be reconstructed from summaries. The CSV is the ground truth for all analysis and playback.
Comment header (metadata lines before the column header)¶
| Header | Example | Why it's written |
|---|---|---|
# Track: |
Spa-Francorchamps |
Human readability; re-read by CsvTelemetryReader to populate TelemetrySessionMetadata |
# TrackLayout: |
Full |
Distinguishes circuit variants |
# Vehicle: |
Ferrari 499P |
Session context for coaching |
# Recorded: |
2026-04-19T20:00:00Z |
Session timestamp |
# Source: |
ApexLab 1.0 |
App version that wrote the file (audit) |
# GameSource: |
LmuRf2 |
Which connector produced this data |
# WeatherAmbientC: |
18.5 |
Ambient temperature (°C) |
# WeatherTrackC: |
32.1 |
Track surface temperature (°C) |
Data columns¶
| Column | Type | Required? | Why it exists |
|---|---|---|---|
Time |
float (seconds) | Yes | Lap time derivation; cumulative time for delta |
Distance |
float (metres) | Yes | Primary alignment axis for lap comparison |
Speed |
float (km/h) | Yes | Core performance metric |
Throttle |
float (0–1) | Yes | Driver input analysis |
Brake |
float (0–1) | Yes | Driver input analysis |
Steering |
float (degrees) | Yes | Corner detection |
Gear |
int | Yes | Vehicle context |
X |
float | Yes | Track map rendering; centerline fusion |
Y |
float | Yes | Track map rendering; centerline fusion |
Lap |
int | Yes | Lap boundary detection (changes = new lap) |
OfficialLapTime |
float (seconds) | Optional | Best-quality lap time source; preferred over time-delta method |
NumPenalties |
int | Optional | Uploaded to API as lap quality context |
NumPitstops |
int | Optional | Flags pit stops within the lap |
InPits |
int (0/1) | Optional | Marks pit laps; sets CountsForSessionTiming = false |
6. Local Storage — Track Profiles (SQLite)¶
File: %LocalAppData%\ApexLab\track_profiles.db
track_profiles table¶
| Column | Why it exists |
|---|---|
slug (PK) |
URL-safe identifier for the track (e.g. spa-lmu-complete); used as SVG cache key |
game |
Which game the profile came from |
layout_label |
Track variant label |
geometry_hash |
Hash of the track centerline; changing it invalidates the SVG cache |
track_name_hint |
Original game name for display |
created_at_utc |
Audit |
notes |
Admin notes |
Why this is separate from sessions.db: Track profiles represent canonical track geometry, not user sessions. They are shared across all sessions on the same track.
7. Local Storage — User Settings (JSON)¶
File: %LocalAppData%\ApexLab\settings.json
Why it exists: Persists user preferences across app restarts without a database.
| Setting | Default | Why it exists |
|---|---|---|
ApiBaseUrl |
https://simcopilot-production.up.railway.app |
Allows switching to a staging or local API |
SentryDsn |
null | Optional crash reporting; null = disabled |
AcUdpHost / AcUdpPort |
127.0.0.1:9996 |
Assetto Corsa UDP listener address |
F1UdpPort |
20777 |
F1 Codemasters/EA UDP port |
ForzaUdpPort |
5300 |
Forza Data Out UDP port |
LiveAutoCaptureLmu |
true |
Start LMU recording automatically when SHM provides data |
LiveAutoListenAcUdp |
true |
Start AC UDP listener at app launch |
MinimizeToTray |
true |
Minimize to system tray icon instead of taskbar |
HideLiveDeltaMapOverlay |
false |
Hide Δ overlay on track map during recording |
HideLiveDeltaStatusBar |
false |
Hide Δ/prediction in status bar |
HideLiveDeltaInMainWhenHudOpen |
false |
Suppress main-window Δ when HUD window is open (avoid triple display) |
LastSessionCatalogId |
null | Restore last-opened session on next launch |
MainWindow* |
null | Window position/size restoration |
HudWindow* |
null | HUD overlay position restoration |
LapPlaybackSpeedMultiplier |
1.0 |
Playback speed (0.5×, 1×, 2×) |
LapPlaybackLoop |
false |
Auto-loop lap playback |
LapPlaybackMapFollow |
false |
Pan map to track cursor during playback |
StartWithWindows |
false |
Windows Run registry key |
UiLanguage |
null (= system) | Force "fr" or "en" |
HasSeenOnboarding |
false |
Skip onboarding screen after first run |
Analysis* |
null | Override default AnalysisOptions values (brake margin, throttle margin, etc.) |
SectorBoundariesByTrackKey |
null | Custom mini-sector boundaries per track (track key → list of distance bounds in metres) |
Read at: app startup. Written at: any settings change; window close (position/size).
8. Local Storage — Auth Tokens (encrypted file)¶
File: %LocalAppData%\ApexLab\auth.dat
Why it exists: JWT tokens must survive app restarts so the user does not need to log in every session.
| Field stored | Why |
|---|---|
AccessToken |
JWT Bearer token sent with every authenticated API request |
RefreshToken |
Long-lived token (30 days) used to obtain a new access token without re-login |
ExpiresAt |
Expiry of the access token; checked before each API call to decide whether to refresh |
DisplayName |
Shown in the UI header without an API round-trip |
AvatarUrl |
Shown in the UI header without an API round-trip |
Encryption: The file is protected using the Windows Data Protection API (DPAPI) — encrypted to the current Windows user account. The raw token values are never stored in plaintext.
Lifecycle: Written on successful login or token refresh. Cleared on Logout(). If missing or unreadable at startup, the user is treated as not authenticated.