refactor(tor): extract TorSettings, TorType, TorPresetType to commons/commonMain

Move core Tor data models and preset logic to shared commons module:
- TorSettings data class, TorType enum, TorPresetType enum
- parseTorType, parseTorPresetType, isPreset, whichPreset functions
- All preset constants (torOnlyWhenNeededPreset, etc.)

R.string resource IDs removed from enums in commons. Android retains
resourceId/explainerId as extension properties in ui.tor package.

15 Android files updated to import from commons.tor. All 69 tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
nrobi144
2026-03-31 16:44:12 +03:00
parent ff9093f5cc
commit 1d9c1eb9b6
17 changed files with 224 additions and 142 deletions
@@ -26,6 +26,7 @@ import coil3.disk.DiskCache
import coil3.memory.MemoryCache
import com.vitorpamplona.amethyst.commons.model.NoteState
import com.vitorpamplona.amethyst.commons.robohash.CachedRobohash
import com.vitorpamplona.amethyst.commons.tor.TorSettings
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.UiSettings
@@ -72,7 +73,6 @@ import com.vitorpamplona.amethyst.ui.resourceCacheInit
import com.vitorpamplona.amethyst.ui.screen.AccountSessionManager
import com.vitorpamplona.amethyst.ui.screen.UiSettingsState
import com.vitorpamplona.amethyst.ui.tor.TorManager
import com.vitorpamplona.amethyst.ui.tor.TorSettings
import com.vitorpamplona.quartz.nip01Core.core.Address
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
@@ -26,9 +26,9 @@ import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import com.vitorpamplona.amethyst.ui.tor.TorSettings
import com.vitorpamplona.amethyst.commons.tor.TorSettings
import com.vitorpamplona.amethyst.commons.tor.TorType
import com.vitorpamplona.amethyst.ui.tor.TorSettingsFlow
import com.vitorpamplona.amethyst.ui.tor.TorType
import com.vitorpamplona.quartz.utils.Log
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -20,9 +20,9 @@
*/
package com.vitorpamplona.amethyst.model.privacyOptions
import com.vitorpamplona.amethyst.commons.tor.TorType
import com.vitorpamplona.amethyst.service.okhttp.DualHttpClientManager
import com.vitorpamplona.amethyst.ui.tor.TorSettingsFlow
import com.vitorpamplona.amethyst.ui.tor.TorType
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
import okhttp3.OkHttpClient
import java.net.InetSocketAddress
@@ -20,7 +20,7 @@
*/
package com.vitorpamplona.amethyst.model.torState
import com.vitorpamplona.amethyst.ui.tor.TorType
import com.vitorpamplona.amethyst.commons.tor.TorType
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.isLocalHost
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.isOnion
@@ -20,7 +20,7 @@
*/
package com.vitorpamplona.amethyst.model.torState
import com.vitorpamplona.amethyst.ui.tor.TorType
import com.vitorpamplona.amethyst.commons.tor.TorType
data class TorRelaySettings(
val torType: TorType = TorType.OFF,
@@ -21,9 +21,9 @@
package com.vitorpamplona.amethyst.model.torState
import androidx.compose.runtime.Stable
import com.vitorpamplona.amethyst.commons.tor.TorType
import com.vitorpamplona.amethyst.service.okhttp.DualHttpClientManager
import com.vitorpamplona.amethyst.ui.tor.TorSettingsFlow
import com.vitorpamplona.amethyst.ui.tor.TorType
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -49,6 +49,7 @@ import com.vitorpamplona.amethyst.commons.model.emphChat.EphemeralChatChannel
import com.vitorpamplona.amethyst.commons.model.nip28PublicChats.PublicChatChannel
import com.vitorpamplona.amethyst.commons.model.nip53LiveActivities.LiveActivitiesChannel
import com.vitorpamplona.amethyst.commons.model.observables.CreatedAtComparator
import com.vitorpamplona.amethyst.commons.tor.TorType
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState
import com.vitorpamplona.amethyst.commons.ui.notifications.CardFeedState
import com.vitorpamplona.amethyst.logTime
@@ -88,7 +89,6 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.CombinedZap
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.eventsync.EventSync
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.tor.TorSettingsFlow
import com.vitorpamplona.amethyst.ui.tor.TorType
import com.vitorpamplona.quartz.experimental.ephemChat.chat.RoomId
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryBaseEvent
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryReadingStateEvent
@@ -34,11 +34,11 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.tor.TorSettings
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.topbars.SavingTopBar
import com.vitorpamplona.amethyst.ui.tor.PrivacySettingsBody
import com.vitorpamplona.amethyst.ui.tor.TorDialogViewModel
import com.vitorpamplona.amethyst.ui.tor.TorSettings
import com.vitorpamplona.amethyst.ui.tor.TorSettingsFlow
@OptIn(ExperimentalMaterial3Api::class)
@@ -35,12 +35,12 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.tor.TorType
import com.vitorpamplona.amethyst.model.preferences.OtsSharedPreferences
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.tor.TorSettingsFlow
import com.vitorpamplona.amethyst.ui.tor.TorType
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@@ -24,6 +24,14 @@ import androidx.compose.runtime.Stable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import com.vitorpamplona.amethyst.commons.tor.TorPresetType
import com.vitorpamplona.amethyst.commons.tor.TorSettings
import com.vitorpamplona.amethyst.commons.tor.TorType
import com.vitorpamplona.amethyst.commons.tor.torDefaultPreset
import com.vitorpamplona.amethyst.commons.tor.torFullyPrivate
import com.vitorpamplona.amethyst.commons.tor.torOnlyWhenNeededPreset
import com.vitorpamplona.amethyst.commons.tor.torSmallPayloadsPreset
import com.vitorpamplona.amethyst.commons.tor.whichPreset
@Stable
class TorDialogViewModel : ViewModel() {
@@ -21,6 +21,7 @@
package com.vitorpamplona.amethyst.ui.tor
import android.content.Context
import com.vitorpamplona.amethyst.commons.tor.TorType
import com.vitorpamplona.amethyst.model.preferences.TorSharedPreferences
import com.vitorpamplona.quartz.utils.Log
import kotlinx.coroutines.CoroutineScope
@@ -21,138 +21,40 @@
package com.vitorpamplona.amethyst.ui.tor
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.tor.TorPresetType
import com.vitorpamplona.amethyst.commons.tor.TorType
data class TorSettings(
val torType: TorType = TorType.INTERNAL,
val externalSocksPort: Int = 9050,
val onionRelaysViaTor: Boolean = true,
val dmRelaysViaTor: Boolean = true,
val newRelaysViaTor: Boolean = true,
val trustedRelaysViaTor: Boolean = false,
val urlPreviewsViaTor: Boolean = false,
val profilePicsViaTor: Boolean = false,
val imagesViaTor: Boolean = false,
val videosViaTor: Boolean = false,
val moneyOperationsViaTor: Boolean = false,
val nip05VerificationsViaTor: Boolean = false,
val mediaUploadsViaTor: Boolean = false,
)
// Re-export shared types so existing Android imports continue to work
// The canonical types now live in commons/commonMain
@Suppress("unused")
private const val RE_EXPORTS = 0
enum class TorType(
val screenCode: Int,
val resourceId: Int,
) {
OFF(0, R.string.tor_off),
INTERNAL(1, R.string.tor_internal),
EXTERNAL(2, R.string.tor_external),
}
// Android-specific resource ID mappings for TorType
val TorType.resourceId: Int
get() =
when (this) {
TorType.OFF -> R.string.tor_off
TorType.INTERNAL -> R.string.tor_internal
TorType.EXTERNAL -> R.string.tor_external
}
fun parseTorType(code: Int?): TorType =
when (code) {
TorType.OFF.screenCode -> TorType.OFF
TorType.INTERNAL.screenCode -> TorType.INTERNAL
TorType.EXTERNAL.screenCode -> TorType.EXTERNAL
else -> TorType.INTERNAL
}
// Android-specific resource ID mappings for TorPresetType
val TorPresetType.resourceId: Int
get() =
when (this) {
TorPresetType.ONLY_WHEN_NEEDED -> R.string.tor_when_needed
TorPresetType.DEFAULT -> R.string.tor_default
TorPresetType.SMALL_PAYLOADS -> R.string.tor_small_payloads
TorPresetType.FULL_PRIVACY -> R.string.tor_full_privacy
TorPresetType.CUSTOM -> R.string.tor_custom
}
enum class TorPresetType(
val screenCode: Int,
val resourceId: Int,
val explainerId: Int,
) {
ONLY_WHEN_NEEDED(0, R.string.tor_when_needed, R.string.tor_when_needed_explainer),
DEFAULT(1, R.string.tor_default, R.string.tor_default_explainer),
SMALL_PAYLOADS(2, R.string.tor_small_payloads, R.string.tor_small_payloads_explainer),
FULL_PRIVACY(3, R.string.tor_full_privacy, R.string.tor_full_privacy_explainer),
CUSTOM(4, R.string.tor_custom, R.string.tor_custom_explainer),
}
fun parseTorPresetType(code: Int?): TorPresetType =
when (code) {
TorPresetType.ONLY_WHEN_NEEDED.screenCode -> TorPresetType.ONLY_WHEN_NEEDED
TorPresetType.DEFAULT.screenCode -> TorPresetType.DEFAULT
TorPresetType.SMALL_PAYLOADS.screenCode -> TorPresetType.SMALL_PAYLOADS
TorPresetType.FULL_PRIVACY.screenCode -> TorPresetType.FULL_PRIVACY
else -> TorPresetType.CUSTOM
}
fun isPreset(
torSettings: TorSettings,
preset: TorSettings,
): Boolean =
torSettings.onionRelaysViaTor == preset.onionRelaysViaTor &&
torSettings.dmRelaysViaTor == preset.dmRelaysViaTor &&
torSettings.newRelaysViaTor == preset.newRelaysViaTor &&
torSettings.trustedRelaysViaTor == preset.trustedRelaysViaTor &&
torSettings.urlPreviewsViaTor == preset.urlPreviewsViaTor &&
// torSettings.profilePicsViaTor == preset.profilePicsViaTor &&
torSettings.imagesViaTor == preset.imagesViaTor &&
torSettings.videosViaTor == preset.videosViaTor &&
torSettings.moneyOperationsViaTor == preset.moneyOperationsViaTor &&
torSettings.nip05VerificationsViaTor == preset.nip05VerificationsViaTor &&
torSettings.mediaUploadsViaTor == preset.mediaUploadsViaTor
fun whichPreset(torSettings: TorSettings): TorPresetType {
if (isPreset(torSettings, torOnlyWhenNeededPreset)) return TorPresetType.ONLY_WHEN_NEEDED
if (isPreset(torSettings, torDefaultPreset)) return TorPresetType.DEFAULT
if (isPreset(torSettings, torSmallPayloadsPreset)) return TorPresetType.SMALL_PAYLOADS
if (isPreset(torSettings, torFullyPrivate)) return TorPresetType.FULL_PRIVACY
return TorPresetType.CUSTOM
}
val torOnlyWhenNeededPreset =
TorSettings(
onionRelaysViaTor = true,
dmRelaysViaTor = false,
newRelaysViaTor = false,
trustedRelaysViaTor = false,
urlPreviewsViaTor = false,
profilePicsViaTor = false,
imagesViaTor = false,
videosViaTor = false,
moneyOperationsViaTor = false,
nip05VerificationsViaTor = false,
mediaUploadsViaTor = false,
)
val torDefaultPreset =
TorSettings(
onionRelaysViaTor = true,
dmRelaysViaTor = true,
newRelaysViaTor = true,
trustedRelaysViaTor = false,
urlPreviewsViaTor = false,
profilePicsViaTor = false,
imagesViaTor = false,
videosViaTor = false,
moneyOperationsViaTor = false,
nip05VerificationsViaTor = false,
mediaUploadsViaTor = false,
)
val torSmallPayloadsPreset =
TorSettings(
onionRelaysViaTor = true,
dmRelaysViaTor = true,
newRelaysViaTor = true,
trustedRelaysViaTor = true,
urlPreviewsViaTor = true,
profilePicsViaTor = true,
imagesViaTor = false,
videosViaTor = false,
moneyOperationsViaTor = true,
nip05VerificationsViaTor = true,
mediaUploadsViaTor = false,
)
val torFullyPrivate =
TorSettings(
onionRelaysViaTor = true,
dmRelaysViaTor = true,
newRelaysViaTor = true,
trustedRelaysViaTor = true,
urlPreviewsViaTor = true,
profilePicsViaTor = true,
imagesViaTor = true,
videosViaTor = true,
moneyOperationsViaTor = true,
nip05VerificationsViaTor = true,
mediaUploadsViaTor = true,
)
val TorPresetType.explainerId: Int
get() =
when (this) {
TorPresetType.ONLY_WHEN_NEEDED -> R.string.tor_when_needed_explainer
TorPresetType.DEFAULT -> R.string.tor_default_explainer
TorPresetType.SMALL_PAYLOADS -> R.string.tor_small_payloads_explainer
TorPresetType.FULL_PRIVACY -> R.string.tor_full_privacy_explainer
TorPresetType.CUSTOM -> R.string.tor_custom_explainer
}
@@ -49,6 +49,11 @@ import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.tor.TorPresetType
import com.vitorpamplona.amethyst.commons.tor.TorSettings
import com.vitorpamplona.amethyst.commons.tor.TorType
import com.vitorpamplona.amethyst.commons.tor.parseTorPresetType
import com.vitorpamplona.amethyst.commons.tor.parseTorType
import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge
import com.vitorpamplona.amethyst.ui.components.TitleExplainer
import com.vitorpamplona.amethyst.ui.navigation.topbars.SavingTopBar
@@ -21,6 +21,8 @@
package com.vitorpamplona.amethyst.ui.tor
import androidx.compose.runtime.Stable
import com.vitorpamplona.amethyst.commons.tor.TorSettings
import com.vitorpamplona.amethyst.commons.tor.TorType
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
@@ -20,7 +20,7 @@
*/
package com.vitorpamplona.amethyst.model.torState
import com.vitorpamplona.amethyst.ui.tor.TorType
import com.vitorpamplona.amethyst.commons.tor.TorType
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -20,6 +20,17 @@
*/
package com.vitorpamplona.amethyst.ui.tor
import com.vitorpamplona.amethyst.commons.tor.TorPresetType
import com.vitorpamplona.amethyst.commons.tor.TorSettings
import com.vitorpamplona.amethyst.commons.tor.TorType
import com.vitorpamplona.amethyst.commons.tor.isPreset
import com.vitorpamplona.amethyst.commons.tor.parseTorPresetType
import com.vitorpamplona.amethyst.commons.tor.parseTorType
import com.vitorpamplona.amethyst.commons.tor.torDefaultPreset
import com.vitorpamplona.amethyst.commons.tor.torFullyPrivate
import com.vitorpamplona.amethyst.commons.tor.torOnlyWhenNeededPreset
import com.vitorpamplona.amethyst.commons.tor.torSmallPayloadsPreset
import com.vitorpamplona.amethyst.commons.tor.whichPreset
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotEquals
@@ -0,0 +1,153 @@
/*
* Copyright (c) 2025 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.amethyst.commons.tor
data class TorSettings(
val torType: TorType = TorType.INTERNAL,
val externalSocksPort: Int = 9050,
val onionRelaysViaTor: Boolean = true,
val dmRelaysViaTor: Boolean = true,
val newRelaysViaTor: Boolean = true,
val trustedRelaysViaTor: Boolean = false,
val urlPreviewsViaTor: Boolean = false,
val profilePicsViaTor: Boolean = false,
val imagesViaTor: Boolean = false,
val videosViaTor: Boolean = false,
val moneyOperationsViaTor: Boolean = false,
val nip05VerificationsViaTor: Boolean = false,
val mediaUploadsViaTor: Boolean = false,
)
enum class TorType(
val screenCode: Int,
) {
OFF(0),
INTERNAL(1),
EXTERNAL(2),
}
fun parseTorType(code: Int?): TorType =
when (code) {
TorType.OFF.screenCode -> TorType.OFF
TorType.INTERNAL.screenCode -> TorType.INTERNAL
TorType.EXTERNAL.screenCode -> TorType.EXTERNAL
else -> TorType.INTERNAL
}
enum class TorPresetType(
val screenCode: Int,
) {
ONLY_WHEN_NEEDED(0),
DEFAULT(1),
SMALL_PAYLOADS(2),
FULL_PRIVACY(3),
CUSTOM(4),
}
fun parseTorPresetType(code: Int?): TorPresetType =
when (code) {
TorPresetType.ONLY_WHEN_NEEDED.screenCode -> TorPresetType.ONLY_WHEN_NEEDED
TorPresetType.DEFAULT.screenCode -> TorPresetType.DEFAULT
TorPresetType.SMALL_PAYLOADS.screenCode -> TorPresetType.SMALL_PAYLOADS
TorPresetType.FULL_PRIVACY.screenCode -> TorPresetType.FULL_PRIVACY
else -> TorPresetType.CUSTOM
}
fun isPreset(
torSettings: TorSettings,
preset: TorSettings,
): Boolean =
torSettings.onionRelaysViaTor == preset.onionRelaysViaTor &&
torSettings.dmRelaysViaTor == preset.dmRelaysViaTor &&
torSettings.newRelaysViaTor == preset.newRelaysViaTor &&
torSettings.trustedRelaysViaTor == preset.trustedRelaysViaTor &&
torSettings.urlPreviewsViaTor == preset.urlPreviewsViaTor &&
// torSettings.profilePicsViaTor == preset.profilePicsViaTor &&
torSettings.imagesViaTor == preset.imagesViaTor &&
torSettings.videosViaTor == preset.videosViaTor &&
torSettings.moneyOperationsViaTor == preset.moneyOperationsViaTor &&
torSettings.nip05VerificationsViaTor == preset.nip05VerificationsViaTor &&
torSettings.mediaUploadsViaTor == preset.mediaUploadsViaTor
fun whichPreset(torSettings: TorSettings): TorPresetType {
if (isPreset(torSettings, torOnlyWhenNeededPreset)) return TorPresetType.ONLY_WHEN_NEEDED
if (isPreset(torSettings, torDefaultPreset)) return TorPresetType.DEFAULT
if (isPreset(torSettings, torSmallPayloadsPreset)) return TorPresetType.SMALL_PAYLOADS
if (isPreset(torSettings, torFullyPrivate)) return TorPresetType.FULL_PRIVACY
return TorPresetType.CUSTOM
}
val torOnlyWhenNeededPreset =
TorSettings(
onionRelaysViaTor = true,
dmRelaysViaTor = false,
newRelaysViaTor = false,
trustedRelaysViaTor = false,
urlPreviewsViaTor = false,
profilePicsViaTor = false,
imagesViaTor = false,
videosViaTor = false,
moneyOperationsViaTor = false,
nip05VerificationsViaTor = false,
mediaUploadsViaTor = false,
)
val torDefaultPreset =
TorSettings(
onionRelaysViaTor = true,
dmRelaysViaTor = true,
newRelaysViaTor = true,
trustedRelaysViaTor = false,
urlPreviewsViaTor = false,
profilePicsViaTor = false,
imagesViaTor = false,
videosViaTor = false,
moneyOperationsViaTor = false,
nip05VerificationsViaTor = false,
mediaUploadsViaTor = false,
)
val torSmallPayloadsPreset =
TorSettings(
onionRelaysViaTor = true,
dmRelaysViaTor = true,
newRelaysViaTor = true,
trustedRelaysViaTor = true,
urlPreviewsViaTor = true,
profilePicsViaTor = true,
imagesViaTor = false,
videosViaTor = false,
moneyOperationsViaTor = true,
nip05VerificationsViaTor = true,
mediaUploadsViaTor = false,
)
val torFullyPrivate =
TorSettings(
onionRelaysViaTor = true,
dmRelaysViaTor = true,
newRelaysViaTor = true,
trustedRelaysViaTor = true,
urlPreviewsViaTor = true,
profilePicsViaTor = true,
imagesViaTor = true,
videosViaTor = true,
moneyOperationsViaTor = true,
nip05VerificationsViaTor = true,
mediaUploadsViaTor = true,
)