From f59d70778a29c3e8850299bc8a8a9424bdcf1013 Mon Sep 17 00:00:00 2001 From: ospab Date: Wed, 19 Aug 2026 21:08:24 +0300 Subject: [PATCH] feat(flutter): TTL-desync toggle in the Android profile editor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors the desktop toggle: a per-profile ttlDesync flag on OstpProfile, a SwitchListTile in the profile editor (shown for all transports, since it is the UDP-path counterpart to the UoT-only junk/fragmentation block), and transport.ttl_desync in the config handed to the engine, with ttl_desync_auto true so the shared ostp-client measures the hop distance and aims the decoys itself. The auto-calibration engine is shared, so Android already had the capability — this just exposes the switch. --- ostp-flutter/lib/models/ostp_profile.dart | 4 ++++ ostp-flutter/lib/ui/home_screen.dart | 2 ++ ostp-flutter/lib/ui/settings_screen.dart | 10 ++++++++++ 3 files changed, 16 insertions(+) diff --git a/ostp-flutter/lib/models/ostp_profile.dart b/ostp-flutter/lib/models/ostp_profile.dart index 80bc3e8..ff704b2 100644 --- a/ostp-flutter/lib/models/ostp_profile.dart +++ b/ostp-flutter/lib/models/ostp_profile.dart @@ -23,6 +23,7 @@ class OstpProfile { int junkPcMax; int junkPsMin; int junkPsMax; + bool ttlDesync; // TTL-desync decoys (UDP), auto-calibrated in the engine OstpProfile({ required this.id, @@ -38,6 +39,7 @@ class OstpProfile { this.junkPcMax = 5, this.junkPsMin = 100, this.junkPsMax = 1000, + this.ttlDesync = false, }); Map toJson() { @@ -55,6 +57,7 @@ class OstpProfile { 'junkPcMax': junkPcMax, 'junkPsMin': junkPsMin, 'junkPsMax': junkPsMax, + 'ttlDesync': ttlDesync, }; } @@ -73,6 +76,7 @@ class OstpProfile { junkPcMax: json['junkPcMax'] as int? ?? 5, junkPsMin: json['junkPsMin'] as int? ?? 100, junkPsMax: json['junkPsMax'] as int? ?? 1000, + ttlDesync: json['ttlDesync'] as bool? ?? false, ); } } diff --git a/ostp-flutter/lib/ui/home_screen.dart b/ostp-flutter/lib/ui/home_screen.dart index 9d72f50..8d85875 100644 --- a/ostp-flutter/lib/ui/home_screen.dart +++ b/ostp-flutter/lib/ui/home_screen.dart @@ -138,6 +138,8 @@ class _HomeScreenState extends State with TickerProviderStateMixin { "frag_sleep": p?.fragSleep ?? 2, "junk_pc": [p?.junkPcMin ?? 2, p?.junkPcMax ?? 5], "junk_ps": [p?.junkPsMin ?? 100, p?.junkPsMax ?? 1000], + "ttl_desync": p?.ttlDesync ?? false, + "ttl_desync_auto": true, }, "multiplex": { "enabled": muxEnabled, diff --git a/ostp-flutter/lib/ui/settings_screen.dart b/ostp-flutter/lib/ui/settings_screen.dart index 8a65bcb..8cfa4e5 100644 --- a/ostp-flutter/lib/ui/settings_screen.dart +++ b/ostp-flutter/lib/ui/settings_screen.dart @@ -224,6 +224,7 @@ class _SettingsScreenState extends State { final junkPsMaxCtrl = TextEditingController(text: (profile?.junkPsMax ?? 1000).toString()); String transportMode = profile?.transportMode ?? 'udp'; bool tcpFragmentation = profile?.tcpFragmentation ?? false; + bool ttlDesync = profile?.ttlDesync ?? false; bool obscureKey = true; showDialog( @@ -301,6 +302,13 @@ class _SettingsScreenState extends State { ], ), ], + SwitchListTile( + contentPadding: EdgeInsets.zero, + title: const Text('TTL Desync', style: TextStyle(fontSize: 14)), + subtitle: const Text('Decoy packets that die before the server (UDP, auto-tuned)', style: TextStyle(fontSize: 12, color: Colors.white54)), + value: ttlDesync, + onChanged: (v) => setDialogState(() => ttlDesync = v), + ), ], ), ), @@ -348,6 +356,7 @@ class _SettingsScreenState extends State { junkPcMax: int.tryParse(junkPcMaxCtrl.text) ?? 5, junkPsMin: int.tryParse(junkPsMinCtrl.text) ?? 100, junkPsMax: int.tryParse(junkPsMaxCtrl.text) ?? 1000, + ttlDesync: ttlDesync, )); } else { profile.name = nameCtrl.text.trim().isNotEmpty ? nameCtrl.text.trim() : server; @@ -361,6 +370,7 @@ class _SettingsScreenState extends State { profile.junkPcMax = int.tryParse(junkPcMaxCtrl.text) ?? 5; profile.junkPsMin = int.tryParse(junkPsMinCtrl.text) ?? 100; profile.junkPsMax = int.tryParse(junkPsMaxCtrl.text) ?? 1000; + profile.ttlDesync = ttlDesync; } _saveProfiles(); });