diff --git a/frontend/appflowy_flutter/dev.env b/frontend/appflowy_flutter/dev.env
deleted file mode 100644
index 2dc13ab045..0000000000
--- a/frontend/appflowy_flutter/dev.env
+++ /dev/null
@@ -1,40 +0,0 @@
-# Copy the 'dev.env' file to '.env':
-# Use the command 'cp dev.env .env' to create a copy of 'dev.env' named '.env'.
-# After copying, update the '.env' file with the necessary environment parameters.
-
-# Generate the 'env.dart' from this '.env' file:
-# Option 1: Use the "Generate Env File" task in VSCode.
-# Option 2: Execute the commands in the appflowy_flutter directory:
-#   cd appflowy_flutter
-#   dart run build_runner clean && dart run build_runner build --delete-conflicting-outputs
-
-# Note on Configuration Priority:
-# If both Supabase config and AppFlowy cloud config are provided in the '.env' file,
-# the AppFlowy cloud config will be prioritized and the Supabase config ignored.
-# Ensure only one of these configurations is active at any given time.
-
-
-# Cloud Type Configuration
-# Use this configuration file to specify the cloud type and its associated settings. The available cloud types are:
-# Local: 0
-# Supabase: 1
-# AppFlowy Cloud: 2
-# By default, it's set to Local.
-CLOUD_TYPE=0
-
-# Supabase Configuration
-# If using Supabase (CLOUD_TYPE=1), provide the following details:
-SUPABASE_URL=
-SUPABASE_ANON_KEY=
-
-# AppFlowy Cloud Configuration
-# If using Supabase (CLOUD_TYPE=2), provide the following details:
-#
-# When using localhost for development. you can use the following settings:
-# APPFLOWY_CLOUD_BASE_URL=http://localhost:8000
-# APPFLOWY_CLOUD_WS_BASE_URL=ws://localhost:8000/ws
-# APPFLOWY_CLOUD_GOTRUE_URL=http://localhost:9998
-
-APPFLOWY_CLOUD_BASE_URL=
-APPFLOWY_CLOUD_WS_BASE_URL=
-APPFLOWY_CLOUD_GOTRUE_URL=
diff --git a/frontend/appflowy_flutter/integration_test/runner.dart b/frontend/appflowy_flutter/integration_test/runner.dart
index 700e500938..ada0383dfd 100644
--- a/frontend/appflowy_flutter/integration_test/runner.dart
+++ b/frontend/appflowy_flutter/integration_test/runner.dart
@@ -1,8 +1,6 @@
-import 'package:appflowy/env/env.dart';
 import 'package:integration_test/integration_test.dart';
 
 import 'appearance_settings_test.dart' as appearance_test_runner;
-import 'auth/auth_test.dart' as auth_test_runner;
 import 'board/board_test_runner.dart' as board_test_runner;
 import 'database_calendar_test.dart' as database_calendar_test;
 import 'database_cell_test.dart' as database_cell_test;
@@ -32,7 +30,7 @@ import 'tabs_test.dart' as tabs_test;
 /// If flutter/flutter#101031 is resolved, this file can be removed completely.
 /// Once removed, the integration_test.yaml must be updated to exclude this as
 /// as the test target.
-void main() {
+Future<void> main() async {
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();
 
   // This test must be run first, otherwise the CI will fail.
@@ -76,9 +74,10 @@ void main() {
   // User settings
   settings_test_runner.main();
 
-  if (isCloudEnabled) {
-    auth_test_runner.main();
-  }
+  // final cloudType = await getCloudType();
+  // if (cloudType == CloudType.supabase) {
+  //   auth_test_runner.main();
+  // }
 
   // board_test.main();
   // empty_document_test.main();
diff --git a/frontend/appflowy_flutter/integration_test/util/auth_operation.dart b/frontend/appflowy_flutter/integration_test/util/auth_operation.dart
index 1f945dd5d6..9e79b8dca8 100644
--- a/frontend/appflowy_flutter/integration_test/util/auth_operation.dart
+++ b/frontend/appflowy_flutter/integration_test/util/auth_operation.dart
@@ -1,5 +1,5 @@
 import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/widgets.dart';
-import 'package:appflowy/workspace/presentation/settings/widgets/setting_cloud_view.dart';
+import 'package:appflowy/workspace/presentation/settings/widgets/setting_supabase_cloud.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_test/flutter_test.dart';
 
@@ -37,7 +37,7 @@ extension AppFlowyAuthTest on WidgetTester {
   void assertEnableSyncSwitchValue(bool value) {
     assertSwitchValue(
       find.descendant(
-        of: find.byType(EnableSync),
+        of: find.byType(SupabaseEnableSync),
         matching: find.byWidgetPredicate((widget) => widget is Switch),
       ),
       value,
@@ -55,7 +55,7 @@ extension AppFlowyAuthTest on WidgetTester {
 
   Future<void> toggleEnableSync() async {
     final finder = find.descendant(
-      of: find.byType(EnableSync),
+      of: find.byType(SupabaseEnableSync),
       matching: find.byWidgetPredicate((widget) => widget is Switch),
     );
 
diff --git a/frontend/appflowy_flutter/integration_test/util/base.dart b/frontend/appflowy_flutter/integration_test/util/base.dart
index b28188629b..835ca2ab87 100644
--- a/frontend/appflowy_flutter/integration_test/util/base.dart
+++ b/frontend/appflowy_flutter/integration_test/util/base.dart
@@ -83,7 +83,7 @@ extension AppFlowyTestBase on WidgetTester {
   }
 
   Future<void> waitUntilSignInPageShow() async {
-    if (isCloudEnabled) {
+    if (isAuthEnabled) {
       final finder = find.byType(SignInAnonymousButton);
       await pumpUntilFound(finder);
       expect(finder, findsOneWidget);
diff --git a/frontend/appflowy_flutter/integration_test/util/common_operations.dart b/frontend/appflowy_flutter/integration_test/util/common_operations.dart
index ea7232a0fb..d5fb515293 100644
--- a/frontend/appflowy_flutter/integration_test/util/common_operations.dart
+++ b/frontend/appflowy_flutter/integration_test/util/common_operations.dart
@@ -303,7 +303,7 @@ extension CommonOperations on WidgetTester {
       KVKeys.showRenameDialogWhenCreatingNewFile,
       (value) => bool.parse(value),
     );
-    final showRenameDialog = settingsOrFailure.fold((l) => false, (r) => r);
+    final showRenameDialog = settingsOrFailure.fold(() => false, (r) => r);
     if (showRenameDialog) {
       await tapOKButton();
     }
diff --git a/frontend/appflowy_flutter/lib/core/config/kv.dart b/frontend/appflowy_flutter/lib/core/config/kv.dart
index 6b3cd813e9..971f9ab246 100644
--- a/frontend/appflowy_flutter/lib/core/config/kv.dart
+++ b/frontend/appflowy_flutter/lib/core/config/kv.dart
@@ -1,13 +1,10 @@
-import 'package:appflowy_backend/dispatch/dispatch.dart';
-import 'package:appflowy_backend/protobuf/flowy-config/entities.pb.dart';
-import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
 import 'package:dartz/dartz.dart';
 import 'package:shared_preferences/shared_preferences.dart';
 
 abstract class KeyValueStorage {
   Future<void> set(String key, String value);
-  Future<Either<FlowyError, String>> get(String key);
-  Future<Either<FlowyError, T>> getWithFormat<T>(
+  Future<Option<String>> get(String key);
+  Future<Option<T>> getWithFormat<T>(
     String key,
     T Function(String value) formatter,
   );
@@ -20,25 +17,25 @@ class DartKeyValue implements KeyValueStorage {
   SharedPreferences get sharedPreferences => _sharedPreferences!;
 
   @override
-  Future<Either<FlowyError, String>> get(String key) async {
+  Future<Option<String>> get(String key) async {
     await _initSharedPreferencesIfNeeded();
 
     final value = sharedPreferences.getString(key);
     if (value != null) {
-      return Right(value);
+      return Some(value);
     }
-    return Left(FlowyError());
+    return none();
   }
 
   @override
-  Future<Either<FlowyError, T>> getWithFormat<T>(
+  Future<Option<T>> getWithFormat<T>(
     String key,
     T Function(String value) formatter,
   ) async {
     final value = await get(key);
     return value.fold(
-      (l) => left(l),
-      (r) => right(formatter(r)),
+      () => none(),
+      (s) => Some(formatter(s)),
     );
   }
 
@@ -67,38 +64,3 @@ class DartKeyValue implements KeyValueStorage {
     _sharedPreferences ??= await SharedPreferences.getInstance();
   }
 }
-
-/// Key-value store
-/// The data is stored in the local storage of the device.
-class RustKeyValue {
-  static Future<void> set(String key, String value) async {
-    await ConfigEventSetKeyValue(
-      KeyValuePB.create()
-        ..key = key
-        ..value = value,
-    ).send();
-  }
-
-  static Future<Either<FlowyError, String>> get(String key) async {
-    final payload = KeyPB.create()..key = key;
-    final response = await ConfigEventGetKeyValue(payload).send();
-    return response.swap().map((r) => r.value);
-  }
-
-  static Future<Either<FlowyError, T>> getWithFormat<T>(
-    String key,
-    T Function(String value) formatter,
-  ) async {
-    final value = await get(key);
-    return value.fold(
-      (l) => left(l),
-      (r) => right(formatter(r)),
-    );
-  }
-
-  static Future<void> remove(String key) async {
-    await ConfigEventRemoveKeyValue(
-      KeyPB.create()..key = key,
-    ).send();
-  }
-}
diff --git a/frontend/appflowy_flutter/lib/core/config/kv_keys.dart b/frontend/appflowy_flutter/lib/core/config/kv_keys.dart
index fef160c98c..264d40ed07 100644
--- a/frontend/appflowy_flutter/lib/core/config/kv_keys.dart
+++ b/frontend/appflowy_flutter/lib/core/config/kv_keys.dart
@@ -42,4 +42,9 @@ class KVKeys {
   /// The value is a boolean string.
   static const String showRenameDialogWhenCreatingNewFile =
       'showRenameDialogWhenCreatingNewFile';
+
+  static const String kCloudType = 'kCloudType';
+  static const String kAppflowyCloudBaseURL = 'kAppFlowyCloudBaseURL';
+  static const String kSupabaseURL = 'kSupbaseURL';
+  static const String kSupabaseAnonKey = 'kSupabaseAnonKey';
 }
diff --git a/frontend/appflowy_flutter/lib/env/backend_env.dart b/frontend/appflowy_flutter/lib/env/backend_env.dart
index d4fb32b057..44958882d8 100644
--- a/frontend/appflowy_flutter/lib/env/backend_env.dart
+++ b/frontend/appflowy_flutter/lib/env/backend_env.dart
@@ -49,6 +49,10 @@ class SupabaseConfiguration {
       anon_key: '',
     );
   }
+
+  bool get isValid {
+    return url.isNotEmpty && anon_key.isNotEmpty;
+  }
 }
 
 @JsonSerializable()
@@ -75,4 +79,10 @@ class AppFlowyCloudConfiguration {
       gotrue_url: '',
     );
   }
+
+  bool get isValid {
+    return base_url.isNotEmpty &&
+        ws_base_url.isNotEmpty &&
+        gotrue_url.isNotEmpty;
+  }
 }
diff --git a/frontend/appflowy_flutter/lib/env/env.dart b/frontend/appflowy_flutter/lib/env/env.dart
index 3cb2493926..750577f617 100644
--- a/frontend/appflowy_flutter/lib/env/env.dart
+++ b/frontend/appflowy_flutter/lib/env/env.dart
@@ -1,76 +1,104 @@
-// lib/env/env.dart
+import 'package:appflowy/core/config/kv.dart';
+import 'package:appflowy/core/config/kv_keys.dart';
+import 'package:appflowy/env/backend_env.dart';
 import 'package:appflowy/startup/startup.dart';
 import 'package:appflowy_backend/log.dart';
-import 'package:envied/envied.dart';
+import 'package:dartz/dartz.dart';
 
-part 'env.g.dart';
-
-/// The environment variables are defined in `.env` file that is located in the
-/// appflowy_flutter.
-///   Run `dart run build_runner build --delete-conflicting-outputs`
-///   to generate the keys from the env file.
+/// Sets the cloud type for the application.
 ///
-///   If you want to regenerate the keys, you need to run `dart run
-///   build_runner clean` before running `dart run build_runner build
-///    --delete-conflicting-outputs`.
-
-/// Follow the guide on https://supabase.com/docs/guides/auth/social-login/auth-google to setup the auth provider.
+/// This method updates the cloud type setting in the key-value storage
+/// using the [KeyValueStorage] service. The cloud type is identified
+/// by the [CloudType] enum.
 ///
-@Envied(path: '.env')
-abstract class Env {
-  @EnviedField(
-    obfuscate: true,
-    varName: 'CLOUD_TYPE',
-    defaultValue: '0',
-  )
-  static final int cloudType = _Env.cloudType;
-
-  /// AppFlowy Cloud Configuration
-  @EnviedField(
-    obfuscate: true,
-    varName: 'APPFLOWY_CLOUD_BASE_URL',
-    defaultValue: '',
-  )
-  static final String afCloudBaseUrl = _Env.afCloudBaseUrl;
-
-  @EnviedField(
-    obfuscate: true,
-    varName: 'APPFLOWY_CLOUD_WS_BASE_URL',
-    defaultValue: '',
-  )
-  static final String afCloudWSBaseUrl = _Env.afCloudWSBaseUrl;
-
-  @EnviedField(
-    obfuscate: true,
-    varName: 'APPFLOWY_CLOUD_GOTRUE_URL',
-    defaultValue: '',
-  )
-  static final String afCloudGoTrueUrl = _Env.afCloudGoTrueUrl;
-
-  // Supabase Configuration:
-  @EnviedField(
-    obfuscate: true,
-    varName: 'SUPABASE_URL',
-    defaultValue: '',
-  )
-  static final String supabaseUrl = _Env.supabaseUrl;
-  @EnviedField(
-    obfuscate: true,
-    varName: 'SUPABASE_ANON_KEY',
-    defaultValue: '',
-  )
-  static final String supabaseAnonKey = _Env.supabaseAnonKey;
+/// [ty] - The type of cloud to be set. It must be one of the values from
+/// [CloudType] enum. The corresponding integer value of the enum is stored:
+/// - `CloudType.local` is stored as "0".
+/// - `CloudType.supabase` is stored as "1".
+/// - `CloudType.appflowyCloud` is stored as "2".
+Future<void> setCloudType(CloudType ty) async {
+  switch (ty) {
+    case CloudType.local:
+      getIt<KeyValueStorage>().set(KVKeys.kCloudType, 0.toString());
+      break;
+    case CloudType.supabase:
+      getIt<KeyValueStorage>().set(KVKeys.kCloudType, 1.toString());
+      break;
+    case CloudType.appflowyCloud:
+      getIt<KeyValueStorage>().set(KVKeys.kCloudType, 2.toString());
+      break;
+  }
 }
 
-bool get isCloudEnabled {
+/// Retrieves the currently set cloud type.
+///
+/// This method fetches the cloud type setting from the key-value storage
+/// using the [KeyValueStorage] service and returns the corresponding
+/// [CloudType] enum value.
+///
+/// Returns:
+/// A Future that resolves to a [CloudType] enum value representing the
+/// currently set cloud type. The default return value is `CloudType.local`
+/// if no valid setting is found.
+///
+Future<CloudType> getCloudType() async {
+  final value = await getIt<KeyValueStorage>().get(KVKeys.kCloudType);
+  return value.fold(() => CloudType.local, (s) {
+    switch (s) {
+      case "0":
+        return CloudType.local;
+      case "1":
+        return CloudType.supabase;
+      case "2":
+        return CloudType.appflowyCloud;
+      default:
+        return CloudType.local;
+    }
+  });
+}
+
+/// Determines whether authentication is enabled.
+///
+/// This getter evaluates if authentication should be enabled based on the
+/// current integration mode and cloud type settings.
+///
+/// Returns:
+/// A boolean value indicating whether authentication is enabled. It returns
+/// `true` if the application is in release or develop mode, and the cloud type
+/// is not set to `CloudType.local`. Additionally, it checks if either the
+/// AppFlowy Cloud or Supabase configuration is valid.
+/// Returns `false` otherwise.
+bool get isAuthEnabled {
   // Only enable supabase in release and develop mode.
   if (integrationMode().isRelease || integrationMode().isDevelop) {
-    return currentCloudType().isEnabled;
+    final env = getIt<AppFlowyCloudSharedEnv>();
+    if (env.cloudType == CloudType.local) {
+      return false;
+    }
+
+    if (env.cloudType == CloudType.supabase) {
+      return env.supabaseConfig.isValid;
+    }
+
+    if (env.cloudType == CloudType.appflowyCloud) {
+      return env.appflowyCloudConfig.isValid;
+    }
+
+    return false;
   } else {
     return false;
   }
 }
 
+/// Checks if Supabase is enabled.
+///
+/// This getter evaluates if Supabase should be enabled based on the
+/// current integration mode and cloud type setting.
+///
+/// Returns:
+/// A boolean value indicating whether Supabase is enabled. It returns `true`
+/// if the application is in release or develop mode and the current cloud type
+/// is `CloudType.supabase`. Otherwise, it returns `false`.
 bool get isSupabaseEnabled {
   // Only enable supabase in release and develop mode.
   if (integrationMode().isRelease || integrationMode().isDevelop) {
@@ -80,6 +108,15 @@ bool get isSupabaseEnabled {
   }
 }
 
+/// Determines if AppFlowy Cloud is enabled.
+///
+/// This getter assesses if AppFlowy Cloud should be enabled based on the
+/// current integration mode and cloud type setting.
+///
+/// Returns:
+/// A boolean value indicating whether AppFlowy Cloud is enabled. It returns
+/// `true` if the application is in release or develop mode and the current
+/// cloud type is `CloudType.appflowyCloud`. Otherwise, it returns `false`.
 bool get isAppFlowyCloudEnabled {
   // Only enable appflowy cloud in release and develop mode.
   if (integrationMode().isRelease || integrationMode().isDevelop) {
@@ -90,40 +127,125 @@ bool get isAppFlowyCloudEnabled {
 }
 
 enum CloudType {
-  unknown,
+  local,
   supabase,
   appflowyCloud;
 
-  bool get isEnabled => this != CloudType.unknown;
+  bool get isEnabled => this != CloudType.local;
+  int get value {
+    switch (this) {
+      case CloudType.local:
+        return 0;
+      case CloudType.supabase:
+        return 1;
+      case CloudType.appflowyCloud:
+        return 2;
+    }
+  }
 }
 
 CloudType currentCloudType() {
-  final value = Env.cloudType;
-  if (value == 1) {
-    if (Env.supabaseUrl.isEmpty || Env.supabaseAnonKey.isEmpty) {
-      Log.error(
-        "Supabase is not configured correctly. The values are: "
-        "url: ${Env.supabaseUrl}, anonKey: ${Env.supabaseAnonKey}",
-      );
-      return CloudType.unknown;
-    } else {
-      return CloudType.supabase;
-    }
-  }
-
-  if (value == 2) {
-    if (Env.afCloudBaseUrl.isEmpty ||
-        Env.afCloudWSBaseUrl.isEmpty ||
-        Env.afCloudGoTrueUrl.isEmpty) {
-      Log.error(
-        "AppFlowy cloud is not configured correctly. The values are: "
-        "baseUrl: ${Env.afCloudBaseUrl}, wsBaseUrl: ${Env.afCloudWSBaseUrl}, gotrueUrl: ${Env.afCloudGoTrueUrl}",
-      );
-      return CloudType.unknown;
-    } else {
-      return CloudType.appflowyCloud;
-    }
-  }
-
-  return CloudType.unknown;
+  return getIt<AppFlowyCloudSharedEnv>().cloudType;
+}
+
+Future<void> setAppFlowyCloudBaseUrl(Option<String> url) async {
+  await url.fold(
+    () => getIt<KeyValueStorage>().remove(KVKeys.kAppflowyCloudBaseURL),
+    (s) => getIt<KeyValueStorage>().set(KVKeys.kAppflowyCloudBaseURL, s),
+  );
+}
+
+/// Use getIt<AppFlowyCloudSharedEnv>() to get the shared environment.
+class AppFlowyCloudSharedEnv {
+  final CloudType cloudType;
+  final AppFlowyCloudConfiguration appflowyCloudConfig;
+  final SupabaseConfiguration supabaseConfig;
+
+  AppFlowyCloudSharedEnv({
+    required this.cloudType,
+    required this.appflowyCloudConfig,
+    required this.supabaseConfig,
+  });
+}
+
+Future<AppFlowyCloudConfiguration> getAppFlowyCloudConfig() async {
+  return AppFlowyCloudConfiguration(
+    base_url: await getAppFlowyCloudUrl(),
+    ws_base_url: await _getAppFlowyCloudWSUrl(),
+    gotrue_url: await _getAppFlowyCloudGotrueUrl(),
+  );
+}
+
+Future<String> getAppFlowyCloudUrl() async {
+  final result =
+      await getIt<KeyValueStorage>().get(KVKeys.kAppflowyCloudBaseURL);
+  return result.fold(
+    () => "",
+    (url) => url,
+  );
+}
+
+Future<String> _getAppFlowyCloudWSUrl() async {
+  try {
+    final serverUrl = await getAppFlowyCloudUrl();
+    final uri = Uri.parse(serverUrl);
+
+    // Construct the WebSocket URL directly from the parsed URI.
+    final wsScheme = uri.isScheme('HTTPS') ? 'wss' : 'ws';
+    final wsUrl = Uri(scheme: wsScheme, host: uri.host, path: '/ws');
+
+    return wsUrl.toString();
+  } catch (e) {
+    Log.error("Failed to get WebSocket URL: $e");
+    return "";
+  }
+}
+
+Future<String> _getAppFlowyCloudGotrueUrl() async {
+  final serverUrl = await getAppFlowyCloudUrl();
+  return "$serverUrl/gotrue";
+}
+
+Future<void> setSupbaseServer(
+  Option<String> url,
+  Option<String> anonKey,
+) async {
+  assert(
+    (url.isSome() && anonKey.isSome()) || (url.isNone() && anonKey.isNone()),
+    "Either both Supabase URL and anon key must be set, or both should be unset",
+  );
+
+  await url.fold(
+    () => getIt<KeyValueStorage>().remove(KVKeys.kSupabaseURL),
+    (s) => getIt<KeyValueStorage>().set(KVKeys.kSupabaseURL, s),
+  );
+  await anonKey.fold(
+    () => getIt<KeyValueStorage>().remove(KVKeys.kSupabaseAnonKey),
+    (s) => getIt<KeyValueStorage>().set(KVKeys.kSupabaseAnonKey, s),
+  );
+}
+
+Future<SupabaseConfiguration> getSupabaseCloudConfig() async {
+  final url = await _getSupbaseUrl();
+  final anonKey = await _getSupabaseAnonKey();
+  return SupabaseConfiguration(
+    url: url,
+    anon_key: anonKey,
+  );
+}
+
+Future<String> _getSupbaseUrl() async {
+  final result = await getIt<KeyValueStorage>().get(KVKeys.kSupabaseURL);
+  return result.fold(
+    () => "",
+    (url) => url,
+  );
+}
+
+Future<String> _getSupabaseAnonKey() async {
+  final result = await getIt<KeyValueStorage>().get(KVKeys.kSupabaseAnonKey);
+  return result.fold(
+    () => "",
+    (url) => url,
+  );
 }
diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/setting/personal_info/personal_info_setting_group.dart b/frontend/appflowy_flutter/lib/mobile/presentation/setting/personal_info/personal_info_setting_group.dart
index a2807732a1..9b9e2499e3 100644
--- a/frontend/appflowy_flutter/lib/mobile/presentation/setting/personal_info/personal_info_setting_group.dart
+++ b/frontend/appflowy_flutter/lib/mobile/presentation/setting/personal_info/personal_info_setting_group.dart
@@ -33,7 +33,7 @@ class PersonalInfoSettingGroup extends StatelessWidget {
             settingItemList: [
               MobileSettingItem(
                 name: userName,
-                subtitle: isCloudEnabled
+                subtitle: isAuthEnabled
                     ? Text(
                         userProfile.email,
                         style: theme.textTheme.bodyMedium?.copyWith(
diff --git a/frontend/appflowy_flutter/lib/startup/deps_resolver.dart b/frontend/appflowy_flutter/lib/startup/deps_resolver.dart
index cce062dd3a..433063d7f9 100644
--- a/frontend/appflowy_flutter/lib/startup/deps_resolver.dart
+++ b/frontend/appflowy_flutter/lib/startup/deps_resolver.dart
@@ -43,6 +43,10 @@ class DependencyResolver {
     GetIt getIt,
     IntegrationMode mode,
   ) async {
+    // getIt.registerFactory<KeyValueStorage>(() => RustKeyValue());
+    getIt.registerFactory<KeyValueStorage>(() => DartKeyValue());
+
+    await _resolveCloudDeps(getIt);
     _resolveUserDeps(getIt, mode);
     _resolveHomeDeps(getIt);
     _resolveFolderDeps(getIt);
@@ -52,12 +56,23 @@ class DependencyResolver {
   }
 }
 
+Future<void> _resolveCloudDeps(GetIt getIt) async {
+  final cloudType = await getCloudType();
+  final appflowyCloudConfig = await getAppFlowyCloudConfig();
+  final supabaseCloudConfig = await getSupabaseCloudConfig();
+  getIt.registerFactory<AppFlowyCloudSharedEnv>(() {
+    return AppFlowyCloudSharedEnv(
+      cloudType: cloudType,
+      appflowyCloudConfig: appflowyCloudConfig,
+      supabaseConfig: supabaseCloudConfig,
+    );
+  });
+}
+
 void _resolveCommonService(
   GetIt getIt,
   IntegrationMode mode,
 ) async {
-  // getIt.registerFactory<KeyValueStorage>(() => RustKeyValue());
-  getIt.registerFactory<KeyValueStorage>(() => DartKeyValue());
   getIt.registerFactory<FilePickerService>(() => FilePicker());
   if (mode.isTest) {
     getIt.registerFactory<ApplicationDataStorage>(
@@ -115,7 +130,7 @@ void _resolveCommonService(
 
 void _resolveUserDeps(GetIt getIt, IntegrationMode mode) {
   switch (currentCloudType()) {
-    case CloudType.unknown:
+    case CloudType.local:
       getIt.registerFactory<AuthService>(
         () => BackendAuthService(
           AuthTypePB.Local,
diff --git a/frontend/appflowy_flutter/lib/startup/startup.dart b/frontend/appflowy_flutter/lib/startup/startup.dart
index a325af3b54..709d3242d9 100644
--- a/frontend/appflowy_flutter/lib/startup/startup.dart
+++ b/frontend/appflowy_flutter/lib/startup/startup.dart
@@ -45,7 +45,7 @@ class FlowyRunner {
     await getIt.reset();
 
     // Specify the env
-    initGetIt(getIt, mode, f, config);
+    await initGetIt(getIt, mode, f, config);
 
     final applicationDataDirectory =
         await getIt<ApplicationDataStorage>().getPath().then(
diff --git a/frontend/appflowy_flutter/lib/startup/tasks/app_window_size_manager.dart b/frontend/appflowy_flutter/lib/startup/tasks/app_window_size_manager.dart
index 6a7707998c..9683039465 100644
--- a/frontend/appflowy_flutter/lib/startup/tasks/app_window_size_manager.dart
+++ b/frontend/appflowy_flutter/lib/startup/tasks/app_window_size_manager.dart
@@ -50,7 +50,7 @@ class WindowSizeManager {
   Future<Offset?> getPosition() async {
     final position = await getIt<KeyValueStorage>().get(KVKeys.windowPosition);
     return position.fold(
-      (l) => null,
+      () => null,
       (r) {
         final offset = json.decode(r);
         return Offset(offset[dx], offset[dy]);
diff --git a/frontend/appflowy_flutter/lib/startup/tasks/rust_sdk.dart b/frontend/appflowy_flutter/lib/startup/tasks/rust_sdk.dart
index a86fe871c6..2ad1793abd 100644
--- a/frontend/appflowy_flutter/lib/startup/tasks/rust_sdk.dart
+++ b/frontend/appflowy_flutter/lib/startup/tasks/rust_sdk.dart
@@ -45,41 +45,15 @@ AppFlowyConfiguration _getAppFlowyConfiguration(
   String originAppPath,
   String deviceId,
 ) {
-  if (isCloudEnabled) {
-    final supabaseConfig = SupabaseConfiguration(
-      url: Env.supabaseUrl,
-      anon_key: Env.supabaseAnonKey,
-    );
-
-    final appflowyCloudConfig = AppFlowyCloudConfiguration(
-      base_url: Env.afCloudBaseUrl,
-      ws_base_url: Env.afCloudWSBaseUrl,
-      gotrue_url: Env.afCloudGoTrueUrl,
-    );
-
-    return AppFlowyConfiguration(
-      custom_app_path: customAppPath,
-      origin_app_path: originAppPath,
-      device_id: deviceId,
-      cloud_type: Env.cloudType,
-      supabase_config: supabaseConfig,
-      appflowy_cloud_config: appflowyCloudConfig,
-    );
-  } else {
-    // Use the default configuration if the cloud feature is disabled
-    final supabaseConfig = SupabaseConfiguration.defaultConfig();
-    final appflowyCloudConfig = AppFlowyCloudConfiguration.defaultConfig();
-
-    return AppFlowyConfiguration(
-      custom_app_path: customAppPath,
-      origin_app_path: originAppPath,
-      device_id: deviceId,
-      // 0 means the cloud type is local
-      cloud_type: 0,
-      supabase_config: supabaseConfig,
-      appflowy_cloud_config: appflowyCloudConfig,
-    );
-  }
+  final env = getIt<AppFlowyCloudSharedEnv>();
+  return AppFlowyConfiguration(
+    custom_app_path: customAppPath,
+    origin_app_path: originAppPath,
+    device_id: deviceId,
+    cloud_type: env.cloudType.value,
+    supabase_config: env.supabaseConfig,
+    appflowy_cloud_config: env.appflowyCloudConfig,
+  );
 }
 
 /// The default directory to store the user data. The directory can be
diff --git a/frontend/appflowy_flutter/lib/startup/tasks/supabase_task.dart b/frontend/appflowy_flutter/lib/startup/tasks/supabase_task.dart
index b8763f2e20..1dca479892 100644
--- a/frontend/appflowy_flutter/lib/startup/tasks/supabase_task.dart
+++ b/frontend/appflowy_flutter/lib/startup/tasks/supabase_task.dart
@@ -36,8 +36,8 @@ class InitSupabaseTask extends LaunchTask {
     supabase?.dispose();
     supabase = null;
     final initializedSupabase = await Supabase.initialize(
-      url: Env.supabaseUrl,
-      anonKey: Env.supabaseAnonKey,
+      url: getIt<AppFlowyCloudSharedEnv>().supabaseConfig.url,
+      anonKey: getIt<AppFlowyCloudSharedEnv>().supabaseConfig.anon_key,
       debug: kDebugMode,
       localStorage: const SupabaseLocalStorage(),
     );
diff --git a/frontend/appflowy_flutter/lib/user/application/auth/af_cloud_auth_service.dart b/frontend/appflowy_flutter/lib/user/application/auth/af_cloud_auth_service.dart
index 9e08959e6d..ee9dee46be 100644
--- a/frontend/appflowy_flutter/lib/user/application/auth/af_cloud_auth_service.dart
+++ b/frontend/appflowy_flutter/lib/user/application/auth/af_cloud_auth_service.dart
@@ -68,6 +68,7 @@ class AFCloudAuthService implements AuthService {
         final completer = Completer<Either<FlowyError, UserProfilePB>>();
         _deeplinkSubscription = _appLinks.uriLinkStream.listen(
           (Uri? uri) async {
+            Log.info('onDeepLink: ${uri.toString()}');
             await _handleUri(uri, completer);
           },
           onError: (Object err, StackTrace stackTrace) {
@@ -108,6 +109,9 @@ class AFCloudAuthService implements AuthService {
             .then((value) => value.swap());
         _deeplinkSubscription?.cancel();
         completer.complete(result);
+      } else {
+        Log.error('onDeepLinkError: Unexpect deep link: ${uri.toString()}');
+        completer.complete(left(AuthError.signInWithOauthError));
       }
     } else {
       Log.error('onDeepLinkError: Unexpect empty deep link callback');
diff --git a/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/desktop_sign_in_screen.dart b/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/desktop_sign_in_screen.dart
index 1da2e7cf84..cfe8efb7d2 100644
--- a/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/desktop_sign_in_screen.dart
+++ b/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/desktop_sign_in_screen.dart
@@ -1,4 +1,5 @@
 import 'package:appflowy/core/frameless_window.dart';
+import 'package:appflowy/env/env.dart';
 import 'package:appflowy/generated/locale_keys.g.dart';
 import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/widgets.dart';
 import 'package:appflowy/user/presentation/widgets/widgets.dart';
@@ -46,9 +47,12 @@ class DesktopSignInScreen extends StatelessWidget {
 
             // third-party sign in.
             const VSpace(20),
-            const _OrDivider(),
-            const VSpace(10),
-            const ThirdPartySignInButtons(),
+
+            if (isAuthEnabled) ...[
+              const _OrDivider(),
+              const VSpace(10),
+              const ThirdPartySignInButtons(),
+            ],
             const VSpace(20),
             // loading status
             const VSpace(indicatorMinHeight),
diff --git a/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/mobile_sign_in_screen.dart b/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/mobile_sign_in_screen.dart
index 2449e9bb27..310f31e263 100644
--- a/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/mobile_sign_in_screen.dart
+++ b/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/mobile_sign_in_screen.dart
@@ -1,3 +1,4 @@
+import 'package:appflowy/env/env.dart';
 import 'package:appflowy/generated/flowy_svgs.g.dart';
 import 'package:appflowy/generated/locale_keys.g.dart';
 import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/widgets.dart';
@@ -85,7 +86,7 @@ class MobileSignInScreen extends StatelessWidget {
                     ],
                   ),
                   const VSpace(spacing),
-                  const ThirdPartySignInButtons(),
+                  if (isAuthEnabled) const ThirdPartySignInButtons(),
                   const VSpace(spacing),
                 ],
               ),
diff --git a/frontend/appflowy_flutter/lib/user/presentation/screens/skip_log_in_screen.dart b/frontend/appflowy_flutter/lib/user/presentation/screens/skip_log_in_screen.dart
index 9bcc4e5ff7..2cd43baa6a 100644
--- a/frontend/appflowy_flutter/lib/user/presentation/screens/skip_log_in_screen.dart
+++ b/frontend/appflowy_flutter/lib/user/presentation/screens/skip_log_in_screen.dart
@@ -67,7 +67,7 @@ class _SkipLogInScreenState extends State<SkipLogInScreen> {
         ),
         const VSpace(32),
         SizedBox(
-          width: size.width * 0.5,
+          width: size.width * 0.7,
           child: FolderWidget(
             createFolderCallback: () async {
               _didCustomizeFolder = true;
diff --git a/frontend/appflowy_flutter/lib/user/presentation/screens/splash_screen.dart b/frontend/appflowy_flutter/lib/user/presentation/screens/splash_screen.dart
index 3cdba2c1fe..8f19b0b05f 100644
--- a/frontend/appflowy_flutter/lib/user/presentation/screens/splash_screen.dart
+++ b/frontend/appflowy_flutter/lib/user/presentation/screens/splash_screen.dart
@@ -95,10 +95,10 @@ class SplashScreen extends StatelessWidget {
 
   void _handleUnauthenticated(BuildContext context, Unauthenticated result) {
     Log.trace(
-      '_handleUnauthenticated -> cloud is enabled: $isCloudEnabled',
+      '_handleUnauthenticated -> cloud is enabled: $isAuthEnabled',
     );
     // replace Splash screen as root page
-    if (isCloudEnabled) {
+    if (isAuthEnabled) {
       context.go(SignInScreen.routeName);
     } else {
       // if the env is not configured, we will skip to the 'skip login screen'.
diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/appflowy_cloud_setting_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/appflowy_cloud_setting_bloc.dart
new file mode 100644
index 0000000000..4777cbcae8
--- /dev/null
+++ b/frontend/appflowy_flutter/lib/workspace/application/settings/appflowy_cloud_setting_bloc.dart
@@ -0,0 +1,91 @@
+import 'package:appflowy/generated/locale_keys.g.dart';
+import 'package:appflowy/workspace/application/settings/cloud_setting_listener.dart';
+import 'package:appflowy_backend/dispatch/dispatch.dart';
+import 'package:appflowy_backend/log.dart';
+import 'package:appflowy_backend/protobuf/flowy-user/user_setting.pb.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:dartz/dartz.dart';
+
+part 'appflowy_cloud_setting_bloc.freezed.dart';
+
+class AppFlowyCloudSettingBloc
+    extends Bloc<AppFlowyCloudSettingEvent, AppFlowyCloudSettingState> {
+  final UserCloudConfigListener _listener;
+  AppFlowyCloudSettingBloc(CloudSettingPB setting)
+      : _listener = UserCloudConfigListener(),
+        super(AppFlowyCloudSettingState.initial(setting)) {
+    on<AppFlowyCloudSettingEvent>((event, emit) async {
+      await event.when(
+        initial: () async {
+          _listener.start(
+            onSettingChanged: (result) {
+              if (isClosed) {
+                return;
+              }
+              result.fold(
+                (setting) =>
+                    add(AppFlowyCloudSettingEvent.didReceiveSetting(setting)),
+                (error) => Log.error(error),
+              );
+            },
+          );
+        },
+        enableSync: (isEnable) async {
+          final config = UpdateCloudConfigPB.create()..enableSync = isEnable;
+          await UserEventSetCloudConfig(config).send();
+        },
+        didReceiveSetting: (CloudSettingPB setting) {
+          emit(
+            state.copyWith(
+              setting: setting,
+            ),
+          );
+        },
+      );
+    });
+  }
+
+  @override
+  Future<void> close() async {
+    _listener.stop();
+    return super.close();
+  }
+}
+
+@freezed
+class AppFlowyCloudSettingEvent with _$AppFlowyCloudSettingEvent {
+  const factory AppFlowyCloudSettingEvent.initial() = _Initial;
+  const factory AppFlowyCloudSettingEvent.enableSync(bool isEnable) =
+      _EnableSync;
+  const factory AppFlowyCloudSettingEvent.didReceiveSetting(
+    CloudSettingPB setting,
+  ) = _DidUpdateSetting;
+}
+
+@freezed
+class AppFlowyCloudSettingState with _$AppFlowyCloudSettingState {
+  const factory AppFlowyCloudSettingState({
+    required CloudSettingPB setting,
+  }) = _AppFlowyCloudSettingState;
+
+  factory AppFlowyCloudSettingState.initial(CloudSettingPB setting) =>
+      AppFlowyCloudSettingState(
+        setting: setting,
+      );
+}
+
+Either<String, ()> validateUrl(String url) {
+  try {
+    // Use Uri.parse to validate the url.
+    final uri = Uri.parse(url);
+    if (uri.isScheme('HTTP') || uri.isScheme('HTTPS')) {
+      return right(());
+    } else {
+      return left(LocaleKeys.settings_menu_invalidCloudURLScheme.tr());
+    }
+  } catch (e) {
+    return left(e.toString());
+  }
+}
diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/appflowy_cloud_urls_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/appflowy_cloud_urls_bloc.dart
new file mode 100644
index 0000000000..cdf88ee111
--- /dev/null
+++ b/frontend/appflowy_flutter/lib/workspace/application/settings/appflowy_cloud_urls_bloc.dart
@@ -0,0 +1,95 @@
+import 'package:appflowy/env/backend_env.dart';
+import 'package:appflowy/env/env.dart';
+import 'package:appflowy/generated/locale_keys.g.dart';
+import 'package:appflowy/startup/startup.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:dartz/dartz.dart';
+
+part 'appflowy_cloud_urls_bloc.freezed.dart';
+
+class AppFlowyCloudURLsBloc
+    extends Bloc<AppFlowyCloudURLsEvent, AppFlowyCloudURLsState> {
+  AppFlowyCloudURLsBloc() : super(AppFlowyCloudURLsState.initial()) {
+    on<AppFlowyCloudURLsEvent>((event, emit) async {
+      await event.when(
+        initial: () async {},
+        updateServerUrl: (url) {
+          emit(state.copyWith(updatedServerUrl: url));
+        },
+        confirmUpdate: () async {
+          if (state.updatedServerUrl.isEmpty) {
+            emit(
+              state.copyWith(
+                updatedServerUrl: "",
+                urlError: none(),
+                restartApp: true,
+              ),
+            );
+            await setAppFlowyCloudBaseUrl(none());
+          } else {
+            validateUrl(state.updatedServerUrl).fold(
+              (error) => emit(state.copyWith(urlError: Some(error))),
+              (_) async {
+                if (state.config.base_url != state.updatedServerUrl) {
+                  await setAppFlowyCloudBaseUrl(Some(state.updatedServerUrl));
+                  add(const AppFlowyCloudURLsEvent.didSaveConfig());
+                }
+              },
+            );
+          }
+        },
+        didSaveConfig: () {
+          emit(
+            state.copyWith(
+              urlError: none(),
+              restartApp: true,
+            ),
+          );
+        },
+      );
+    });
+  }
+}
+
+@freezed
+class AppFlowyCloudURLsEvent with _$AppFlowyCloudURLsEvent {
+  const factory AppFlowyCloudURLsEvent.initial() = _Initial;
+  const factory AppFlowyCloudURLsEvent.updateServerUrl(String text) =
+      _ServerUrl;
+  const factory AppFlowyCloudURLsEvent.confirmUpdate() = _UpdateConfig;
+  const factory AppFlowyCloudURLsEvent.didSaveConfig() = _DidSaveConfig;
+}
+
+@freezed
+class AppFlowyCloudURLsState with _$AppFlowyCloudURLsState {
+  const factory AppFlowyCloudURLsState({
+    required AppFlowyCloudConfiguration config,
+    required String updatedServerUrl,
+    required Option<String> urlError,
+    required bool restartApp,
+  }) = _AppFlowyCloudURLsState;
+
+  factory AppFlowyCloudURLsState.initial() => AppFlowyCloudURLsState(
+        config: getIt<AppFlowyCloudSharedEnv>().appflowyCloudConfig,
+        urlError: none(),
+        updatedServerUrl:
+            getIt<AppFlowyCloudSharedEnv>().appflowyCloudConfig.base_url,
+        restartApp: false,
+      );
+}
+
+Either<String, ()> validateUrl(String url) {
+  try {
+    // Use Uri.parse to validate the url.
+    final uri = Uri.parse(url);
+    if (uri.isScheme('HTTP') || uri.isScheme('HTTPS')) {
+      return right(());
+    } else {
+      return left(LocaleKeys.settings_menu_invalidCloudURLScheme.tr());
+    }
+  } catch (e) {
+    return left(e.toString());
+  }
+}
diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/application_data_storage.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/application_data_storage.dart
index 41963bdc34..400663f387 100644
--- a/frontend/appflowy_flutter/lib/workspace/application/settings/application_data_storage.dart
+++ b/frontend/appflowy_flutter/lib/workspace/application/settings/application_data_storage.dart
@@ -65,7 +65,7 @@ class ApplicationDataStorage {
 
     final response = await getIt<KeyValueStorage>().get(KVKeys.pathLocation);
     String path = await response.fold(
-      (error) async {
+      () async {
         // return the default path if the path is not set
         final directory = await appFlowyApplicationDataDirectory();
         return directory.path;
diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/cloud_setting_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/cloud_setting_bloc.dart
new file mode 100644
index 0000000000..3bff8b7b40
--- /dev/null
+++ b/frontend/appflowy_flutter/lib/workspace/application/settings/cloud_setting_bloc.dart
@@ -0,0 +1,38 @@
+import 'package:appflowy/env/env.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'cloud_setting_bloc.freezed.dart';
+
+class CloudSettingBloc extends Bloc<CloudSettingEvent, CloudSettingState> {
+  CloudSettingBloc(CloudType cloudType)
+      : super(CloudSettingState.initial(cloudType)) {
+    on<CloudSettingEvent>((event, emit) async {
+      await event.when(
+        initial: () async {},
+        updateCloudType: (CloudType newCloudType) async {
+          await setCloudType(newCloudType);
+          emit(state.copyWith(cloudType: newCloudType));
+        },
+      );
+    });
+  }
+}
+
+@freezed
+class CloudSettingEvent with _$CloudSettingEvent {
+  const factory CloudSettingEvent.initial() = _Initial;
+  const factory CloudSettingEvent.updateCloudType(CloudType newCloudType) =
+      _UpdateCloudType;
+}
+
+@freezed
+class CloudSettingState with _$CloudSettingState {
+  const factory CloudSettingState({
+    required CloudType cloudType,
+  }) = _CloudSettingState;
+
+  factory CloudSettingState.initial(CloudType cloudType) => CloudSettingState(
+        cloudType: cloudType,
+      );
+}
diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/cloud_setting_listener.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/cloud_setting_listener.dart
index be02c26501..a67da1a43d 100644
--- a/frontend/appflowy_flutter/lib/workspace/application/settings/cloud_setting_listener.dart
+++ b/frontend/appflowy_flutter/lib/workspace/application/settings/cloud_setting_listener.dart
@@ -10,21 +10,18 @@ import 'package:dartz/dartz.dart';
 import '../../../core/notification/user_notification.dart';
 
 class UserCloudConfigListener {
-  final String userId;
   StreamSubscription<SubscribeObject>? _subscription;
   void Function(Either<CloudSettingPB, FlowyError>)? _onSettingChanged;
 
   UserNotificationParser? _userParser;
-  UserCloudConfigListener({
-    required this.userId,
-  });
+  UserCloudConfigListener();
 
   void start({
     void Function(Either<CloudSettingPB, FlowyError>)? onSettingChanged,
   }) {
     _onSettingChanged = onSettingChanged;
     _userParser = UserNotificationParser(
-      id: userId,
+      id: 'user_cloud_config',
       callback: _userNotificationCallback,
     );
     _subscription = RustStreamReceiver.listen((observable) {
diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/create_file_settings_cubit.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/create_file_settings_cubit.dart
index 564fea5a51..143048c030 100644
--- a/frontend/appflowy_flutter/lib/workspace/application/settings/create_file_settings_cubit.dart
+++ b/frontend/appflowy_flutter/lib/workspace/application/settings/create_file_settings_cubit.dart
@@ -22,7 +22,7 @@ class CreateFileSettingsCubit extends Cubit<bool> {
       (value) => bool.parse(value),
     );
     settingsOrFailure.fold(
-      (_) => emit(false),
+      () => emit(false),
       (settings) => emit(settings),
     );
   }
diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/setting_supabase_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/setting_supabase_bloc.dart
deleted file mode 100644
index 4a064c665a..0000000000
--- a/frontend/appflowy_flutter/lib/workspace/application/settings/setting_supabase_bloc.dart
+++ /dev/null
@@ -1,86 +0,0 @@
-import 'package:appflowy/plugins/database_view/application/defines.dart';
-import 'package:appflowy_backend/dispatch/dispatch.dart';
-import 'package:appflowy_backend/log.dart';
-import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:freezed_annotation/freezed_annotation.dart';
-import 'package:dartz/dartz.dart';
-
-import 'cloud_setting_listener.dart';
-
-part 'setting_supabase_bloc.freezed.dart';
-
-class CloudSettingBloc extends Bloc<CloudSettingEvent, CloudSettingState> {
-  final UserCloudConfigListener _listener;
-
-  CloudSettingBloc({
-    required String userId,
-    required CloudSettingPB config,
-  })  : _listener = UserCloudConfigListener(userId: userId),
-        super(CloudSettingState.initial(config)) {
-    on<CloudSettingEvent>((event, emit) async {
-      await event.when(
-        initial: () async {
-          _listener.start(
-            onSettingChanged: (result) {
-              if (isClosed) {
-                return;
-              }
-
-              result.fold(
-                (config) => add(CloudSettingEvent.didReceiveConfig(config)),
-                (error) => Log.error(error),
-              );
-            },
-          );
-        },
-        enableSync: (bool enable) async {
-          final update = UpdateCloudConfigPB.create()..enableSync = enable;
-          updateCloudConfig(update);
-        },
-        didReceiveConfig: (CloudSettingPB config) {
-          emit(
-            state.copyWith(
-              config: config,
-              loadingState: LoadingState.finish(left(unit)),
-            ),
-          );
-        },
-        enableEncrypt: (bool enable) {
-          final update = UpdateCloudConfigPB.create()..enableEncrypt = enable;
-          updateCloudConfig(update);
-          emit(state.copyWith(loadingState: const LoadingState.loading()));
-        },
-      );
-    });
-  }
-
-  Future<void> updateCloudConfig(UpdateCloudConfigPB config) async {
-    await UserEventSetCloudConfig(config).send();
-  }
-}
-
-@freezed
-class CloudSettingEvent with _$CloudSettingEvent {
-  const factory CloudSettingEvent.initial() = _Initial;
-  const factory CloudSettingEvent.didReceiveConfig(
-    CloudSettingPB config,
-  ) = _DidSyncSupabaseConfig;
-  const factory CloudSettingEvent.enableSync(bool enable) = _EnableSync;
-  const factory CloudSettingEvent.enableEncrypt(bool enable) = _EnableEncrypt;
-}
-
-@freezed
-class CloudSettingState with _$CloudSettingState {
-  const factory CloudSettingState({
-    required CloudSettingPB config,
-    required Either<Unit, String> successOrFailure,
-    required LoadingState loadingState,
-  }) = _CloudSettingState;
-
-  factory CloudSettingState.initial(CloudSettingPB config) => CloudSettingState(
-        config: config,
-        successOrFailure: left(unit),
-        loadingState: LoadingState.finish(left(unit)),
-      );
-}
diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/supabase_cloud_setting_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/supabase_cloud_setting_bloc.dart
new file mode 100644
index 0000000000..d8ad992eca
--- /dev/null
+++ b/frontend/appflowy_flutter/lib/workspace/application/settings/supabase_cloud_setting_bloc.dart
@@ -0,0 +1,97 @@
+import 'package:appflowy/env/backend_env.dart';
+import 'package:appflowy/env/env.dart';
+import 'package:appflowy/plugins/database_view/application/defines.dart';
+import 'package:appflowy/startup/startup.dart';
+import 'package:appflowy_backend/dispatch/dispatch.dart';
+import 'package:appflowy_backend/log.dart';
+import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:dartz/dartz.dart';
+
+import 'cloud_setting_listener.dart';
+
+part 'supabase_cloud_setting_bloc.freezed.dart';
+
+class SupabaseCloudSettingBloc
+    extends Bloc<SupabaseCloudSettingEvent, SupabaseCloudSettingState> {
+  final UserCloudConfigListener _listener;
+
+  SupabaseCloudSettingBloc({
+    required CloudSettingPB setting,
+  })  : _listener = UserCloudConfigListener(),
+        super(SupabaseCloudSettingState.initial(setting)) {
+    on<SupabaseCloudSettingEvent>((event, emit) async {
+      await event.when(
+        initial: () async {
+          _listener.start(
+            onSettingChanged: (result) {
+              if (isClosed) {
+                return;
+              }
+              result.fold(
+                (setting) =>
+                    add(SupabaseCloudSettingEvent.didReceiveSetting(setting)),
+                (error) => Log.error(error),
+              );
+            },
+          );
+        },
+        enableSync: (bool enable) async {
+          final update = UpdateCloudConfigPB.create()..enableSync = enable;
+          updateCloudConfig(update);
+        },
+        didReceiveSetting: (CloudSettingPB setting) {
+          emit(
+            state.copyWith(
+              setting: setting,
+              loadingState: LoadingState.finish(left(unit)),
+            ),
+          );
+        },
+        enableEncrypt: (bool enable) {
+          final update = UpdateCloudConfigPB.create()..enableEncrypt = enable;
+          updateCloudConfig(update);
+          emit(state.copyWith(loadingState: const LoadingState.loading()));
+        },
+      );
+    });
+  }
+
+  Future<void> updateCloudConfig(UpdateCloudConfigPB setting) async {
+    await UserEventSetCloudConfig(setting).send();
+  }
+
+  @override
+  Future<void> close() async {
+    _listener.stop();
+    return super.close();
+  }
+}
+
+@freezed
+class SupabaseCloudSettingEvent with _$SupabaseCloudSettingEvent {
+  const factory SupabaseCloudSettingEvent.initial() = _Initial;
+  const factory SupabaseCloudSettingEvent.didReceiveSetting(
+    CloudSettingPB setting,
+  ) = _DidSyncSupabaseConfig;
+  const factory SupabaseCloudSettingEvent.enableSync(bool enable) = _EnableSync;
+  const factory SupabaseCloudSettingEvent.enableEncrypt(bool enable) =
+      _EnableEncrypt;
+}
+
+@freezed
+class SupabaseCloudSettingState with _$SupabaseCloudSettingState {
+  const factory SupabaseCloudSettingState({
+    required LoadingState loadingState,
+    required SupabaseConfiguration config,
+    required CloudSettingPB setting,
+  }) = _SupabaseCloudSettingState;
+
+  factory SupabaseCloudSettingState.initial(CloudSettingPB setting) =>
+      SupabaseCloudSettingState(
+        loadingState: LoadingState.finish(left(unit)),
+        setting: setting,
+        config: getIt<AppFlowyCloudSharedEnv>().supabaseConfig,
+      );
+}
diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/supabase_cloud_urls_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/supabase_cloud_urls_bloc.dart
new file mode 100644
index 0000000000..5186302c03
--- /dev/null
+++ b/frontend/appflowy_flutter/lib/workspace/application/settings/supabase_cloud_urls_bloc.dart
@@ -0,0 +1,115 @@
+import 'package:appflowy/env/backend_env.dart';
+import 'package:appflowy/env/env.dart';
+import 'package:appflowy/generated/locale_keys.g.dart';
+import 'package:appflowy/startup/startup.dart';
+import 'package:appflowy_backend/dispatch/dispatch.dart';
+import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:dartz/dartz.dart';
+
+import 'appflowy_cloud_setting_bloc.dart';
+
+part 'supabase_cloud_urls_bloc.freezed.dart';
+
+class SupabaseCloudURLsBloc
+    extends Bloc<SupabaseCloudURLsEvent, SupabaseCloudURLsState> {
+  SupabaseCloudURLsBloc() : super(SupabaseCloudURLsState.initial()) {
+    on<SupabaseCloudURLsEvent>((event, emit) async {
+      await event.when(
+        updateUrl: (String url) {
+          emit(state.copyWith(updatedUrl: url));
+        },
+        updateAnonKey: (String anonKey) {
+          emit(state.copyWith(upatedAnonKey: anonKey));
+        },
+        confirmUpdate: () async {
+          if (state.updatedUrl.isEmpty) {
+            emit(
+              state.copyWith(
+                urlError: none(),
+                anonKeyError: none(),
+                restartApp: true,
+              ),
+            );
+            await setSupbaseServer(none(), none());
+          } else {
+            // The anon key can't be empty if the url is not empty.
+            if (state.upatedAnonKey.isEmpty) {
+              emit(
+                state.copyWith(
+                  urlError: none(),
+                  anonKeyError: some(
+                    LocaleKeys.settings_menu_cloudSupabaseAnonKeyCanNotBeEmpty
+                        .tr(),
+                  ),
+                  restartApp: false,
+                ),
+              );
+              return;
+            }
+
+            validateUrl(state.updatedUrl).fold(
+              (error) => emit(state.copyWith(urlError: Some(error))),
+              (_) async {
+                await setSupbaseServer(
+                  Some(state.updatedUrl),
+                  Some(state.upatedAnonKey),
+                );
+
+                add(const SupabaseCloudURLsEvent.didSaveConfig());
+              },
+            );
+          }
+        },
+        didSaveConfig: () {
+          emit(
+            state.copyWith(
+              urlError: none(),
+              anonKeyError: none(),
+              restartApp: true,
+            ),
+          );
+        },
+      );
+    });
+  }
+
+  Future<void> updateCloudConfig(UpdateCloudConfigPB setting) async {
+    await UserEventSetCloudConfig(setting).send();
+  }
+}
+
+@freezed
+class SupabaseCloudURLsEvent with _$SupabaseCloudURLsEvent {
+  const factory SupabaseCloudURLsEvent.updateUrl(String text) = _UpdateUrl;
+  const factory SupabaseCloudURLsEvent.updateAnonKey(String text) =
+      _UpdateAnonKey;
+  const factory SupabaseCloudURLsEvent.confirmUpdate() = _UpdateConfig;
+  const factory SupabaseCloudURLsEvent.didSaveConfig() = _DidSaveConfig;
+}
+
+@freezed
+class SupabaseCloudURLsState with _$SupabaseCloudURLsState {
+  const factory SupabaseCloudURLsState({
+    required SupabaseConfiguration config,
+    required String updatedUrl,
+    required String upatedAnonKey,
+    required Option<String> urlError,
+    required Option<String> anonKeyError,
+    required bool restartApp,
+  }) = _SupabaseCloudURLsState;
+
+  factory SupabaseCloudURLsState.initial() {
+    final config = getIt<AppFlowyCloudSharedEnv>().supabaseConfig;
+    return SupabaseCloudURLsState(
+      updatedUrl: config.url,
+      upatedAnonKey: config.anon_key,
+      urlError: none(),
+      anonKeyError: none(),
+      restartApp: false,
+      config: config,
+    );
+  }
+}
diff --git a/frontend/appflowy_flutter/lib/workspace/application/sidebar/folder/folder_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/sidebar/folder/folder_bloc.dart
index 95a888cfa3..19927043a4 100644
--- a/frontend/appflowy_flutter/lib/workspace/application/sidebar/folder/folder_bloc.dart
+++ b/frontend/appflowy_flutter/lib/workspace/application/sidebar/folder/folder_bloc.dart
@@ -35,7 +35,7 @@ class FolderBloc extends Bloc<FolderEvent, FolderState> {
   Future<void> _setFolderExpandStatus(bool isExpanded) async {
     final result = await getIt<KeyValueStorage>().get(KVKeys.expandedViews);
     final map = result.fold(
-      (l) => {},
+      () => {},
       (r) => jsonDecode(r),
     );
     if (isExpanded) {
@@ -50,7 +50,7 @@ class FolderBloc extends Bloc<FolderEvent, FolderState> {
 
   Future<bool> _getFolderExpandStatus() async {
     return getIt<KeyValueStorage>().get(KVKeys.expandedViews).then((result) {
-      return result.fold((l) => true, (r) {
+      return result.fold(() => true, (r) {
         final map = jsonDecode(r);
         return map[state.type.name] ?? true;
       });
diff --git a/frontend/appflowy_flutter/lib/workspace/application/view/view_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/view/view_bloc.dart
index ac8387346c..f25747903f 100644
--- a/frontend/appflowy_flutter/lib/workspace/application/view/view_bloc.dart
+++ b/frontend/appflowy_flutter/lib/workspace/application/view/view_bloc.dart
@@ -179,7 +179,7 @@ class ViewBloc extends Bloc<ViewEvent, ViewState> {
   Future<void> _setViewIsExpanded(ViewPB view, bool isExpanded) async {
     final result = await getIt<KeyValueStorage>().get(KVKeys.expandedViews);
     final map = result.fold(
-      (l) => {},
+      () => {},
       (r) => jsonDecode(r),
     );
     if (isExpanded) {
@@ -192,7 +192,7 @@ class ViewBloc extends Bloc<ViewEvent, ViewState> {
 
   Future<bool> _getViewIsExpanded(ViewPB view) {
     return getIt<KeyValueStorage>().get(KVKeys.expandedViews).then((result) {
-      return result.fold((l) => false, (r) {
+      return result.fold(() => false, (r) {
         final map = jsonDecode(r);
         return map[view.id] ?? false;
       });
diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/rename_view_dialog.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/rename_view_dialog.dart
index 3856ce02f7..2dffa80360 100644
--- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/rename_view_dialog.dart
+++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/rename_view_dialog.dart
@@ -21,7 +21,7 @@ Future<void> createViewAndShowRenameDialogIfNeeded(
     KVKeys.showRenameDialogWhenCreatingNewFile,
     (value) => bool.parse(value),
   );
-  final showRenameDialog = value.fold((l) => false, (r) => r);
+  final showRenameDialog = value.fold(() => false, (r) => r);
   if (context.mounted && showRenameDialog) {
     NavigatorTextFieldDialog(
       title: dialogTitle,
diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar_user.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar_user.dart
index d726d92806..2d0a20ffdd 100644
--- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar_user.dart
+++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar_user.dart
@@ -89,7 +89,7 @@ class SidebarUser extends StatelessWidget {
                       Log.warn("Can't pop dialog context");
                     }
                   },
-                  didOpenUser: () async {
+                  restartApp: () async {
                     // Pop the dialog using the dialog context
                     Navigator.of(dialogContext).pop();
                     await runAppFlowy();
diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/settings_dialog.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/settings_dialog.dart
index f4321cecb2..c33cf53ac1 100644
--- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/settings_dialog.dart
+++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/settings_dialog.dart
@@ -1,7 +1,6 @@
 import 'package:appflowy/startup/startup.dart';
 import 'package:appflowy/generated/locale_keys.g.dart';
 import 'package:appflowy/workspace/presentation/settings/widgets/settings_notifications_view.dart';
-import 'package:appflowy/workspace/presentation/settings/widgets/setting_cloud_view.dart';
 import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance_view.dart';
 import 'package:appflowy/workspace/presentation/settings/widgets/settings_customize_shortcuts_view.dart';
 import 'package:appflowy/workspace/presentation/settings/widgets/settings_file_system_view.dart';
@@ -14,6 +13,7 @@ import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
+import 'widgets/setting_cloud.dart';
 
 const _dialogHorizontalPadding = EdgeInsets.symmetric(horizontal: 12);
 const _contentInsetPadding = EdgeInsets.fromLTRB(0.0, 12.0, 0.0, 16.0);
@@ -21,13 +21,13 @@ const _contentInsetPadding = EdgeInsets.fromLTRB(0.0, 12.0, 0.0, 16.0);
 class SettingsDialog extends StatelessWidget {
   final VoidCallback dismissDialog;
   final VoidCallback didLogout;
-  final VoidCallback didOpenUser;
+  final VoidCallback restartApp;
   final UserProfilePB user;
   SettingsDialog(
     this.user, {
     required this.dismissDialog,
     required this.didLogout,
-    required this.didOpenUser,
+    required this.restartApp,
     Key? key,
   }) : super(key: ValueKey(user.id));
 
@@ -100,12 +100,14 @@ class SettingsDialog extends StatelessWidget {
           user,
           didLogin: () => dismissDialog(),
           didLogout: didLogout,
-          didOpenUser: didOpenUser,
+          didOpenUser: restartApp,
         );
       case SettingsPage.notifications:
         return const SettingsNotificationsView();
       case SettingsPage.cloud:
-        return SettingCloudView(userId: user.id.toString());
+        return SettingCloud(
+          didResetServerUrl: () => restartApp(),
+        );
       case SettingsPage.shortcuts:
         return const SettingsCustomizeShortcutsWrapper();
       default:
diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart
new file mode 100644
index 0000000000..48828d2dff
--- /dev/null
+++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart
@@ -0,0 +1,259 @@
+import 'package:appflowy/generated/locale_keys.g.dart';
+import 'package:appflowy/workspace/application/settings/appflowy_cloud_setting_bloc.dart';
+import 'package:appflowy/workspace/application/settings/appflowy_cloud_urls_bloc.dart';
+import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
+import 'package:appflowy_backend/dispatch/dispatch.dart';
+import 'package:appflowy_backend/log.dart';
+import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
+import 'package:appflowy_backend/protobuf/flowy-user/user_setting.pb.dart';
+import 'package:dartz/dartz.dart' show Either;
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flowy_infra/size.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
+import 'package:flowy_infra_ui/widget/error_page.dart';
+import 'package:flutter/gestures.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:url_launcher/url_launcher.dart';
+
+class SettingAppFlowyCloudView extends StatelessWidget {
+  final VoidCallback didResetServerUrl;
+  const SettingAppFlowyCloudView({required this.didResetServerUrl, super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return FutureBuilder<Either<CloudSettingPB, FlowyError>>(
+      future: UserEventGetCloudConfig().send(),
+      builder: (context, snapshot) {
+        if (snapshot.data != null &&
+            snapshot.connectionState == ConnectionState.done) {
+          return snapshot.data!.fold(
+            (setting) => _renderContent(setting),
+            (err) => FlowyErrorPage.message(err.toString(), howToFix: ""),
+          );
+        } else {
+          return const Center(
+            child: CircularProgressIndicator(),
+          );
+        }
+      },
+    );
+  }
+
+  BlocProvider<AppFlowyCloudSettingBloc> _renderContent(
+    CloudSettingPB setting,
+  ) {
+    return BlocProvider(
+      create: (context) => AppFlowyCloudSettingBloc(setting)
+        ..add(const AppFlowyCloudSettingEvent.initial()),
+      child: Column(
+        children: [
+          const AppFlowyCloudEnableSync(),
+          const VSpace(40),
+          AppFlowyCloudURLs(didUpdateUrls: () => didResetServerUrl()),
+        ],
+      ),
+    );
+  }
+}
+
+class AppFlowyCloudURLs extends StatelessWidget {
+  final VoidCallback didUpdateUrls;
+  const AppFlowyCloudURLs({
+    required this.didUpdateUrls,
+    super.key,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocProvider(
+      create: (context) =>
+          AppFlowyCloudURLsBloc()..add(const AppFlowyCloudURLsEvent.initial()),
+      child: BlocListener<AppFlowyCloudURLsBloc, AppFlowyCloudURLsState>(
+        listener: (context, state) {
+          if (state.restartApp) {
+            didUpdateUrls();
+          }
+        },
+        child: BlocBuilder<AppFlowyCloudURLsBloc, AppFlowyCloudURLsState>(
+          builder: (context, state) {
+            return Column(
+              children: [
+                const AppFlowySelfhostTip(),
+                CloudURLInput(
+                  title: LocaleKeys.settings_menu_cloudURL.tr(),
+                  url: state.config.base_url,
+                  hint: LocaleKeys.settings_menu_cloudURLHint.tr(),
+                  onChanged: (text) {
+                    context.read<AppFlowyCloudURLsBloc>().add(
+                          AppFlowyCloudURLsEvent.updateServerUrl(
+                            text,
+                          ),
+                        );
+                  },
+                ),
+                const VSpace(20),
+                FlowyButton(
+                  isSelected: true,
+                  useIntrinsicWidth: true,
+                  margin: const EdgeInsets.symmetric(
+                    horizontal: 30,
+                    vertical: 10,
+                  ),
+                  text: FlowyText(
+                    LocaleKeys.settings_menu_restartApp.tr(),
+                  ),
+                  onTap: () {
+                    NavigatorAlertDialog(
+                      title: LocaleKeys.settings_menu_restartAppTip.tr(),
+                      confirm: () => context.read<AppFlowyCloudURLsBloc>().add(
+                            const AppFlowyCloudURLsEvent.confirmUpdate(),
+                          ),
+                    ).show(context);
+                  },
+                ),
+              ],
+            );
+          },
+        ),
+      ),
+    );
+  }
+}
+
+class AppFlowySelfhostTip extends StatelessWidget {
+  final url =
+      "https://docs.appflowy.io/docs/guides/appflowy/self-hosting-appflowy#build-appflowy-with-a-self-hosted-server";
+  const AppFlowySelfhostTip({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return Opacity(
+      opacity: 0.6,
+      child: RichText(
+        text: TextSpan(
+          children: <TextSpan>[
+            TextSpan(
+              text: LocaleKeys.settings_menu_selfHostStart.tr(),
+              style: Theme.of(context).textTheme.bodySmall!,
+            ),
+            TextSpan(
+              text: " ${LocaleKeys.settings_menu_selfHostContent.tr()} ",
+              style: Theme.of(context).textTheme.bodyMedium!.copyWith(
+                    fontSize: FontSizes.s14,
+                    color: Theme.of(context).colorScheme.primary,
+                    decoration: TextDecoration.underline,
+                  ),
+              recognizer: TapGestureRecognizer()..onTap = () => _launchURL(),
+            ),
+            TextSpan(
+              text: LocaleKeys.settings_menu_selfHostEnd.tr(),
+              style: Theme.of(context).textTheme.bodySmall!,
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+
+  Future<void> _launchURL() async {
+    final uri = Uri.parse(url);
+    if (await canLaunchUrl(uri)) {
+      await launchUrl(uri);
+    } else {
+      Log.error("Could not launch $url");
+    }
+  }
+}
+
+@visibleForTesting
+class CloudURLInput extends StatefulWidget {
+  final String title;
+  final String url;
+  final String hint;
+
+  final Function(String) onChanged;
+
+  const CloudURLInput({
+    required this.title,
+    required this.url,
+    required this.hint,
+    required this.onChanged,
+    Key? key,
+  }) : super(key: key);
+
+  @override
+  CloudURLInputState createState() => CloudURLInputState();
+}
+
+class CloudURLInputState extends State<CloudURLInput> {
+  late TextEditingController _controller;
+
+  @override
+  void initState() {
+    super.initState();
+    _controller = TextEditingController(text: widget.url);
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return TextField(
+      controller: _controller,
+      style: const TextStyle(fontSize: 12.0),
+      decoration: InputDecoration(
+        contentPadding: const EdgeInsets.symmetric(vertical: 6),
+        labelText: widget.title,
+        labelStyle: Theme.of(context)
+            .textTheme
+            .titleMedium!
+            .copyWith(fontWeight: FontWeight.w400, fontSize: 16),
+        enabledBorder: UnderlineInputBorder(
+          borderSide:
+              BorderSide(color: Theme.of(context).colorScheme.onBackground),
+        ),
+        focusedBorder: UnderlineInputBorder(
+          borderSide: BorderSide(color: Theme.of(context).colorScheme.primary),
+        ),
+        hintText: widget.hint,
+        errorText: context
+            .read<AppFlowyCloudURLsBloc>()
+            .state
+            .urlError
+            .fold(() => null, (error) => error),
+      ),
+      onChanged: widget.onChanged,
+    );
+  }
+
+  @override
+  void dispose() {
+    _controller.dispose();
+    super.dispose();
+  }
+}
+
+class AppFlowyCloudEnableSync extends StatelessWidget {
+  const AppFlowyCloudEnableSync({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocBuilder<AppFlowyCloudSettingBloc, AppFlowyCloudSettingState>(
+      builder: (context, state) {
+        return Row(
+          children: [
+            FlowyText.medium(LocaleKeys.settings_menu_enableSync.tr()),
+            const Spacer(),
+            Switch(
+              onChanged: (bool value) {
+                context.read<AppFlowyCloudSettingBloc>().add(
+                      AppFlowyCloudSettingEvent.enableSync(value),
+                    );
+              },
+              value: state.setting.enableSync,
+            ),
+          ],
+        );
+      },
+    );
+  }
+}
diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_cloud.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_cloud.dart
new file mode 100644
index 0000000000..94de68f93c
--- /dev/null
+++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_cloud.dart
@@ -0,0 +1,166 @@
+import 'package:appflowy/env/env.dart';
+import 'package:appflowy/generated/flowy_svgs.g.dart';
+import 'package:appflowy/generated/locale_keys.g.dart';
+import 'package:appflowy/workspace/application/settings/cloud_setting_bloc.dart';
+import 'package:appflowy/workspace/presentation/settings/widgets/setting_local_cloud.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'setting_appflowy_cloud.dart';
+import 'setting_supabase_cloud.dart';
+
+class SettingCloud extends StatelessWidget {
+  final VoidCallback didResetServerUrl;
+  const SettingCloud({required this.didResetServerUrl, super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return FutureBuilder(
+      future: getCloudType(),
+      builder: (BuildContext context, AsyncSnapshot<CloudType> snapshot) {
+        if (snapshot.hasData) {
+          final cloudType = snapshot.data!;
+          return BlocProvider(
+            create: (context) => CloudSettingBloc(cloudType),
+            child: BlocBuilder<CloudSettingBloc, CloudSettingState>(
+              builder: (context, state) {
+                return Column(
+                  children: [
+                    Row(
+                      children: [
+                        Expanded(
+                          child: FlowyText.medium(
+                            LocaleKeys.settings_menu_cloudServerType.tr(),
+                          ),
+                        ),
+                        Tooltip(
+                          message:
+                              LocaleKeys.settings_menu_cloudServerTypeTip.tr(),
+                          child: CloudTypeSwitcher(
+                            cloudType: state.cloudType,
+                            onSelected: (newCloudType) {
+                              context.read<CloudSettingBloc>().add(
+                                    CloudSettingEvent.updateCloudType(
+                                      newCloudType,
+                                    ),
+                                  );
+                            },
+                          ),
+                        ),
+                      ],
+                    ),
+                    _viewFromCloudType(state.cloudType),
+                  ],
+                );
+              },
+            ),
+          );
+        } else {
+          return const Center(
+            child: CircularProgressIndicator(),
+          );
+        }
+      },
+    );
+  }
+
+  Widget _viewFromCloudType(CloudType cloudType) {
+    switch (cloudType) {
+      case CloudType.local:
+        return SettingLocalCloud(didResetServerUrl: didResetServerUrl);
+      case CloudType.supabase:
+        return SettingSupabaseCloudView(
+          didResetServerUrl: didResetServerUrl,
+        );
+      case CloudType.appflowyCloud:
+        return SettingAppFlowyCloudView(
+          didResetServerUrl: didResetServerUrl,
+        );
+    }
+  }
+}
+
+class CloudTypeSwitcher extends StatelessWidget {
+  final CloudType cloudType;
+  final Function(CloudType) onSelected;
+  const CloudTypeSwitcher({
+    required this.cloudType,
+    required this.onSelected,
+    super.key,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return AppFlowyPopover(
+      direction: PopoverDirection.bottomWithRightAligned,
+      child: FlowyTextButton(
+        padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 6),
+        titleFromCloudType(cloudType),
+        fontColor: Theme.of(context).colorScheme.onBackground,
+        fillColor: Colors.transparent,
+        onPressed: () {},
+      ),
+      popupBuilder: (BuildContext context) {
+        return ListView.builder(
+          shrinkWrap: true,
+          itemBuilder: (context, index) {
+            return CloudTypeItem(
+              cloudType: CloudType.values[index],
+              currentCloudtype: cloudType,
+              onSelected: onSelected,
+            );
+          },
+          itemCount: CloudType.values.length,
+        );
+      },
+    );
+  }
+}
+
+class CloudTypeItem extends StatelessWidget {
+  final CloudType cloudType;
+  final CloudType currentCloudtype;
+  final Function(CloudType) onSelected;
+
+  const CloudTypeItem({
+    required this.cloudType,
+    required this.currentCloudtype,
+    required this.onSelected,
+    super.key,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return SizedBox(
+      height: 32,
+      child: FlowyButton(
+        text: FlowyText.medium(
+          titleFromCloudType(cloudType),
+        ),
+        rightIcon: currentCloudtype == cloudType
+            ? const FlowySvg(FlowySvgs.check_s)
+            : null,
+        onTap: () {
+          if (currentCloudtype != cloudType) {
+            onSelected(cloudType);
+          }
+          PopoverContainer.of(context).close();
+        },
+      ),
+    );
+  }
+}
+
+String titleFromCloudType(CloudType cloudType) {
+  switch (cloudType) {
+    case CloudType.local:
+      return LocaleKeys.settings_menu_cloudLocal.tr();
+    case CloudType.supabase:
+      return LocaleKeys.settings_menu_cloudSupabase.tr();
+    case CloudType.appflowyCloud:
+      return LocaleKeys.settings_menu_cloudAppFlowy.tr();
+  }
+}
diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_cloud_view.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_cloud_view.dart
deleted file mode 100644
index 45b693df84..0000000000
--- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_cloud_view.dart
+++ /dev/null
@@ -1,196 +0,0 @@
-import 'package:appflowy/env/env.dart';
-import 'package:appflowy/generated/locale_keys.g.dart';
-import 'package:appflowy/workspace/application/settings/setting_supabase_bloc.dart';
-import 'package:appflowy/workspace/presentation/home/toast.dart';
-import 'package:appflowy_backend/dispatch/dispatch.dart';
-import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
-import 'package:appflowy_backend/protobuf/flowy-user/user_setting.pb.dart';
-import 'package:dartz/dartz.dart' show Either;
-import 'package:easy_localization/easy_localization.dart';
-import 'package:flowy_infra/size.dart';
-import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/widget/error_page.dart';
-import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter/services.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-
-class SettingCloudView extends StatelessWidget {
-  final String userId;
-  const SettingCloudView({required this.userId, super.key});
-
-  @override
-  Widget build(BuildContext context) {
-    return FutureBuilder<Either<CloudSettingPB, FlowyError>>(
-      future: UserEventGetCloudConfig().send(),
-      builder: (context, snapshot) {
-        if (snapshot.data != null &&
-            snapshot.connectionState == ConnectionState.done) {
-          return snapshot.data!.fold(
-            (config) {
-              return BlocProvider(
-                create: (context) => CloudSettingBloc(
-                  userId: userId,
-                  config: config,
-                )..add(const CloudSettingEvent.initial()),
-                child: BlocBuilder<CloudSettingBloc, CloudSettingState>(
-                  builder: (context, state) {
-                    return Column(
-                      children: [
-                        const EnableSync(),
-                        // Currently the appflowy cloud is not support end-to-end encryption.
-                        if (!isAppFlowyCloudEnabled) const EnableEncrypt(),
-                        if (isAppFlowyCloudEnabled)
-                          AppFlowyCloudInformationWidget(
-                            url: state.config.serverUrl,
-                          ),
-                      ],
-                    );
-                  },
-                ),
-              );
-            },
-            (err) {
-              return FlowyErrorPage.message(err.toString(), howToFix: "");
-            },
-          );
-        } else {
-          return const Center(
-            child: CircularProgressIndicator(),
-          );
-        }
-      },
-    );
-  }
-}
-
-class AppFlowyCloudInformationWidget extends StatelessWidget {
-  final String url;
-
-  const AppFlowyCloudInformationWidget({required this.url, super.key});
-
-  @override
-  Widget build(BuildContext context) {
-    return Column(
-      children: [
-        Row(
-          children: [
-            Expanded(
-              // Wrap the Opacity widget with Expanded
-              child: Opacity(
-                opacity: 0.6,
-                child: FlowyText(
-                  "${LocaleKeys.settings_menu_cloudURL.tr()}: $url",
-                  maxLines: null, // Allow the text to wrap
-                ),
-              ),
-            ),
-          ],
-        ),
-      ],
-    );
-  }
-}
-
-class EnableEncrypt extends StatelessWidget {
-  const EnableEncrypt({super.key});
-
-  @override
-  Widget build(BuildContext context) {
-    return BlocBuilder<CloudSettingBloc, CloudSettingState>(
-      builder: (context, state) {
-        final indicator = state.loadingState.when(
-          loading: () => const CircularProgressIndicator.adaptive(),
-          finish: (successOrFail) => const SizedBox.shrink(),
-        );
-
-        return Column(
-          children: [
-            Row(
-              children: [
-                FlowyText.medium(LocaleKeys.settings_menu_enableEncrypt.tr()),
-                const Spacer(),
-                indicator,
-                const HSpace(3),
-                Switch(
-                  onChanged: state.config.enableEncrypt
-                      ? null
-                      : (bool value) {
-                          context
-                              .read<CloudSettingBloc>()
-                              .add(CloudSettingEvent.enableEncrypt(value));
-                        },
-                  value: state.config.enableEncrypt,
-                ),
-              ],
-            ),
-            Column(
-              crossAxisAlignment: CrossAxisAlignment.start,
-              mainAxisAlignment: MainAxisAlignment.start,
-              children: [
-                IntrinsicHeight(
-                  child: Opacity(
-                    opacity: 0.6,
-                    child: FlowyText.medium(
-                      LocaleKeys.settings_menu_enableEncryptPrompt.tr(),
-                      maxLines: 13,
-                    ),
-                  ),
-                ),
-                const VSpace(6),
-                SizedBox(
-                  height: 40,
-                  child: FlowyTooltip(
-                    message: LocaleKeys.settings_menu_clickToCopySecret.tr(),
-                    child: FlowyButton(
-                      disable: !(state.config.enableEncrypt),
-                      decoration: BoxDecoration(
-                        borderRadius: Corners.s5Border,
-                        border: Border.all(
-                          color: Theme.of(context).colorScheme.secondary,
-                        ),
-                      ),
-                      text: FlowyText.medium(state.config.encryptSecret),
-                      onTap: () async {
-                        await Clipboard.setData(
-                          ClipboardData(text: state.config.encryptSecret),
-                        );
-                        showMessageToast(LocaleKeys.message_copy_success.tr());
-                      },
-                    ),
-                  ),
-                ),
-              ],
-            ),
-          ],
-        );
-      },
-    );
-  }
-}
-
-class EnableSync extends StatelessWidget {
-  const EnableSync({super.key});
-
-  @override
-  Widget build(BuildContext context) {
-    return BlocBuilder<CloudSettingBloc, CloudSettingState>(
-      builder: (context, state) {
-        return Row(
-          children: [
-            FlowyText.medium(LocaleKeys.settings_menu_enableSync.tr()),
-            const Spacer(),
-            Switch(
-              onChanged: (bool value) {
-                context.read<CloudSettingBloc>().add(
-                      CloudSettingEvent.enableSync(value),
-                    );
-              },
-              value: state.config.enableSync,
-            ),
-          ],
-        );
-      },
-    );
-  }
-}
diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_local_cloud.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_local_cloud.dart
new file mode 100644
index 0000000000..8e2570ba43
--- /dev/null
+++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_local_cloud.dart
@@ -0,0 +1,40 @@
+import 'package:appflowy/generated/locale_keys.g.dart';
+import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flowy_infra_ui/style_widget/button.dart';
+import 'package:flowy_infra_ui/style_widget/text.dart';
+import 'package:flutter/material.dart';
+
+class SettingLocalCloud extends StatelessWidget {
+  final VoidCallback didResetServerUrl;
+  const SettingLocalCloud({
+    required this.didResetServerUrl,
+    super.key,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return Row(
+      children: [
+        FlowyButton(
+          isSelected: true,
+          useIntrinsicWidth: true,
+          margin: const EdgeInsets.symmetric(
+            horizontal: 30,
+            vertical: 10,
+          ),
+          text: FlowyText(
+            LocaleKeys.settings_menu_restartApp.tr(),
+          ),
+          onTap: () {
+            NavigatorAlertDialog(
+              title: LocaleKeys.settings_menu_restartAppTip.tr(),
+              confirm: didResetServerUrl,
+            ).show(context);
+          },
+        ),
+        const Spacer(),
+      ],
+    );
+  }
+}
diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_supabase_cloud.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_supabase_cloud.dart
new file mode 100644
index 0000000000..91e5a6ec06
--- /dev/null
+++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_supabase_cloud.dart
@@ -0,0 +1,356 @@
+import 'package:appflowy/generated/locale_keys.g.dart';
+import 'package:appflowy/workspace/application/settings/supabase_cloud_setting_bloc.dart';
+import 'package:appflowy/workspace/application/settings/supabase_cloud_urls_bloc.dart';
+import 'package:appflowy/workspace/presentation/home/toast.dart';
+import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
+import 'package:appflowy_backend/dispatch/dispatch.dart';
+import 'package:appflowy_backend/log.dart';
+import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
+import 'package:appflowy_backend/protobuf/flowy-user/user_setting.pb.dart';
+import 'package:dartz/dartz.dart' show Either;
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flowy_infra/size.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
+import 'package:flowy_infra_ui/widget/error_page.dart';
+import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
+import 'package:flutter/gestures.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:url_launcher/url_launcher.dart';
+
+class SettingSupabaseCloudView extends StatelessWidget {
+  final VoidCallback didResetServerUrl;
+  const SettingSupabaseCloudView({required this.didResetServerUrl, super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return FutureBuilder<Either<CloudSettingPB, FlowyError>>(
+      future: UserEventGetCloudConfig().send(),
+      builder: (context, snapshot) {
+        if (snapshot.data != null &&
+            snapshot.connectionState == ConnectionState.done) {
+          return snapshot.data!.fold(
+            (setting) {
+              return BlocProvider(
+                create: (context) => SupabaseCloudSettingBloc(
+                  setting: setting,
+                )..add(const SupabaseCloudSettingEvent.initial()),
+                child: Column(
+                  children: [
+                    BlocBuilder<SupabaseCloudSettingBloc,
+                        SupabaseCloudSettingState>(
+                      builder: (context, state) {
+                        return const Column(
+                          children: [
+                            SupabaseEnableSync(),
+                            EnableEncrypt(),
+                          ],
+                        );
+                      },
+                    ),
+                    const VSpace(40),
+                    const SupabaseSelfhostTip(),
+                    SupabaseCloudURLs(
+                      didUpdateUrls: didResetServerUrl,
+                    ),
+                  ],
+                ),
+              );
+            },
+            (err) {
+              return FlowyErrorPage.message(err.toString(), howToFix: "");
+            },
+          );
+        } else {
+          return const Center(
+            child: CircularProgressIndicator(),
+          );
+        }
+      },
+    );
+  }
+}
+
+class SupabaseCloudURLs extends StatelessWidget {
+  final VoidCallback didUpdateUrls;
+  const SupabaseCloudURLs({
+    required this.didUpdateUrls,
+    super.key,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocProvider(
+      create: (context) => SupabaseCloudURLsBloc(),
+      child: BlocListener<SupabaseCloudURLsBloc, SupabaseCloudURLsState>(
+        listener: (context, state) {
+          if (state.restartApp) {
+            didUpdateUrls();
+          }
+        },
+        child: BlocBuilder<SupabaseCloudURLsBloc, SupabaseCloudURLsState>(
+          builder: (context, state) {
+            return Column(
+              children: [
+                SupabaseInput(
+                  title: LocaleKeys.settings_menu_cloudSupabaseUrl.tr(),
+                  url: state.config.url,
+                  hint: LocaleKeys.settings_menu_cloudURLHint.tr(),
+                  onChanged: (text) {
+                    context
+                        .read<SupabaseCloudURLsBloc>()
+                        .add(SupabaseCloudURLsEvent.updateUrl(text));
+                  },
+                  error: state.urlError.fold(() => null, (a) => a),
+                ),
+                SupabaseInput(
+                  title: LocaleKeys.settings_menu_cloudSupabaseAnonKey.tr(),
+                  url: state.config.anon_key,
+                  hint: LocaleKeys.settings_menu_cloudURLHint.tr(),
+                  onChanged: (text) {
+                    context
+                        .read<SupabaseCloudURLsBloc>()
+                        .add(SupabaseCloudURLsEvent.updateAnonKey(text));
+                  },
+                  error: state.anonKeyError.fold(() => null, (a) => a),
+                ),
+                const VSpace(20),
+                FlowyButton(
+                  isSelected: true,
+                  useIntrinsicWidth: true,
+                  margin: const EdgeInsets.symmetric(
+                    horizontal: 30,
+                    vertical: 10,
+                  ),
+                  text: FlowyText(
+                    LocaleKeys.settings_menu_restartApp.tr(),
+                  ),
+                  onTap: () {
+                    NavigatorAlertDialog(
+                      title: LocaleKeys.settings_menu_restartAppTip.tr(),
+                      confirm: () => context
+                          .read<SupabaseCloudURLsBloc>()
+                          .add(const SupabaseCloudURLsEvent.confirmUpdate()),
+                    ).show(context);
+                  },
+                ),
+              ],
+            );
+          },
+        ),
+      ),
+    );
+  }
+}
+
+class EnableEncrypt extends StatelessWidget {
+  const EnableEncrypt({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocBuilder<SupabaseCloudSettingBloc, SupabaseCloudSettingState>(
+      builder: (context, state) {
+        final indicator = state.loadingState.when(
+          loading: () => const CircularProgressIndicator.adaptive(),
+          finish: (successOrFail) => const SizedBox.shrink(),
+        );
+
+        return Column(
+          children: [
+            Row(
+              children: [
+                FlowyText.medium(LocaleKeys.settings_menu_enableEncrypt.tr()),
+                const Spacer(),
+                indicator,
+                const HSpace(3),
+                Switch(
+                  onChanged: state.setting.enableEncrypt
+                      ? null
+                      : (bool value) {
+                          context.read<SupabaseCloudSettingBloc>().add(
+                                SupabaseCloudSettingEvent.enableEncrypt(value),
+                              );
+                        },
+                  value: state.setting.enableEncrypt,
+                ),
+              ],
+            ),
+            Column(
+              crossAxisAlignment: CrossAxisAlignment.start,
+              mainAxisAlignment: MainAxisAlignment.start,
+              children: [
+                IntrinsicHeight(
+                  child: Opacity(
+                    opacity: 0.6,
+                    child: FlowyText.medium(
+                      LocaleKeys.settings_menu_enableEncryptPrompt.tr(),
+                      maxLines: 13,
+                    ),
+                  ),
+                ),
+                const VSpace(6),
+                SizedBox(
+                  height: 40,
+                  child: FlowyTooltip(
+                    message: LocaleKeys.settings_menu_clickToCopySecret.tr(),
+                    child: FlowyButton(
+                      disable: !(state.setting.enableEncrypt),
+                      decoration: BoxDecoration(
+                        borderRadius: Corners.s5Border,
+                        border: Border.all(
+                          color: Theme.of(context).colorScheme.secondary,
+                        ),
+                      ),
+                      text: FlowyText.medium(state.setting.encryptSecret),
+                      onTap: () async {
+                        await Clipboard.setData(
+                          ClipboardData(text: state.setting.encryptSecret),
+                        );
+                        showMessageToast(LocaleKeys.message_copy_success.tr());
+                      },
+                    ),
+                  ),
+                ),
+              ],
+            ),
+          ],
+        );
+      },
+    );
+  }
+}
+
+class SupabaseEnableSync extends StatelessWidget {
+  const SupabaseEnableSync({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocBuilder<SupabaseCloudSettingBloc, SupabaseCloudSettingState>(
+      builder: (context, state) {
+        return Row(
+          children: [
+            FlowyText.medium(LocaleKeys.settings_menu_enableSync.tr()),
+            const Spacer(),
+            Switch(
+              onChanged: (bool value) {
+                context.read<SupabaseCloudSettingBloc>().add(
+                      SupabaseCloudSettingEvent.enableSync(value),
+                    );
+              },
+              value: state.setting.enableSync,
+            ),
+          ],
+        );
+      },
+    );
+  }
+}
+
+@visibleForTesting
+class SupabaseInput extends StatefulWidget {
+  final String title;
+  final String url;
+  final String hint;
+  final String? error;
+  final Function(String) onChanged;
+
+  const SupabaseInput({
+    required this.title,
+    required this.url,
+    required this.hint,
+    required this.onChanged,
+    required this.error,
+    Key? key,
+  }) : super(key: key);
+
+  @override
+  SupabaseInputState createState() => SupabaseInputState();
+}
+
+class SupabaseInputState extends State<SupabaseInput> {
+  late TextEditingController _controller;
+
+  @override
+  void initState() {
+    super.initState();
+    _controller = TextEditingController(text: widget.url);
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return TextField(
+      controller: _controller,
+      style: const TextStyle(fontSize: 12.0),
+      decoration: InputDecoration(
+        contentPadding: const EdgeInsets.symmetric(vertical: 6),
+        labelText: widget.title,
+        labelStyle: Theme.of(context)
+            .textTheme
+            .titleMedium!
+            .copyWith(fontWeight: FontWeight.w400, fontSize: 16),
+        enabledBorder: UnderlineInputBorder(
+          borderSide:
+              BorderSide(color: Theme.of(context).colorScheme.onBackground),
+        ),
+        focusedBorder: UnderlineInputBorder(
+          borderSide: BorderSide(color: Theme.of(context).colorScheme.primary),
+        ),
+        hintText: widget.hint,
+        errorText: widget.error,
+      ),
+      onChanged: widget.onChanged,
+    );
+  }
+
+  @override
+  void dispose() {
+    _controller.dispose();
+    super.dispose();
+  }
+}
+
+class SupabaseSelfhostTip extends StatelessWidget {
+  final url =
+      "https://docs.appflowy.io/docs/guides/appflowy/self-hosting-appflowy-using-supabase";
+  const SupabaseSelfhostTip({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return Opacity(
+      opacity: 0.6,
+      child: RichText(
+        text: TextSpan(
+          children: <TextSpan>[
+            TextSpan(
+              text: LocaleKeys.settings_menu_selfHostStart.tr(),
+              style: Theme.of(context).textTheme.bodySmall!,
+            ),
+            TextSpan(
+              text: " ${LocaleKeys.settings_menu_selfHostContent.tr()} ",
+              style: Theme.of(context).textTheme.bodyMedium!.copyWith(
+                    fontSize: FontSizes.s14,
+                    color: Theme.of(context).colorScheme.primary,
+                    decoration: TextDecoration.underline,
+                  ),
+              recognizer: TapGestureRecognizer()..onTap = () => _launchURL(),
+            ),
+            TextSpan(
+              text: LocaleKeys.settings_menu_selfHostEnd.tr(),
+              style: Theme.of(context).textTheme.bodySmall!,
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+
+  Future<void> _launchURL() async {
+    final uri = Uri.parse(url);
+    if (await canLaunchUrl(uri)) {
+      await launchUrl(uri);
+    } else {
+      Log.error("Could not launch $url");
+    }
+  }
+}
diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_third_party_login.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_third_party_login.dart
index e823fa7550..7d79c30ed6 100644
--- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_third_party_login.dart
+++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_third_party_login.dart
@@ -1,3 +1,4 @@
+import 'package:appflowy/env/env.dart';
 import 'package:appflowy/generated/locale_keys.g.dart';
 import 'package:appflowy/startup/startup.dart';
 import 'package:appflowy/user/application/sign_in_bloc.dart';
@@ -55,7 +56,7 @@ class SettingThirdPartyLogin extends StatelessWidget {
               const VSpace(6),
               promptMessage,
               const VSpace(6),
-              const ThirdPartySignInButtons(),
+              if (isAuthEnabled) const ThirdPartySignInButtons(),
               const VSpace(6),
             ],
           );
diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_menu.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_menu.dart
index e0780b9f46..1113bf1b7e 100644
--- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_menu.dart
+++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_menu.dart
@@ -1,11 +1,8 @@
-import 'package:appflowy/env/env.dart';
 import 'package:appflowy/generated/locale_keys.g.dart';
 import 'package:appflowy/workspace/application/settings/settings_dialog_bloc.dart';
 import 'package:appflowy/workspace/presentation/settings/widgets/settings_menu_element.dart';
-import 'package:appflowy_backend/protobuf/flowy-user/auth.pbenum.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
 
 class SettingsMenu extends StatelessWidget {
   const SettingsMenu({
@@ -19,9 +16,6 @@ class SettingsMenu extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    final bool showSyncSetting = isCloudEnabled &&
-        context.read<SettingsDialogBloc>().state.userProfile.authType !=
-            AuthTypePB.Local;
     return Column(
       children: [
         SettingsMenuElement(
@@ -63,17 +57,14 @@ class SettingsMenu extends StatelessWidget {
           icon: Icons.notifications_outlined,
           changeSelectedPage: changeSelectedPage,
         ),
-        // Only show supabase setting if supabase is enabled and the current auth type is not local
-        if (showSyncSetting) ...[
-          const SizedBox(height: 10),
-          SettingsMenuElement(
-            page: SettingsPage.cloud,
-            selectedPage: currentPage,
-            label: LocaleKeys.settings_menu_cloudSetting.tr(),
-            icon: Icons.sync,
-            changeSelectedPage: changeSelectedPage,
-          ),
-        ],
+        const SizedBox(height: 10),
+        SettingsMenuElement(
+          page: SettingsPage.cloud,
+          selectedPage: currentPage,
+          label: LocaleKeys.settings_menu_cloudSetting.tr(),
+          icon: Icons.sync,
+          changeSelectedPage: changeSelectedPage,
+        ),
         const SizedBox(height: 10),
         SettingsMenuElement(
           page: SettingsPage.shortcuts,
diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_user_view.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_user_view.dart
index cf045d41b7..474ee86948 100644
--- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_user_view.dart
+++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_user_view.dart
@@ -54,7 +54,7 @@ class SettingsUserView extends StatelessWidget {
             mainAxisSize: MainAxisSize.min,
             children: [
               _buildUserIconSetting(context),
-              if (isCloudEnabled && user.authType != AuthTypePB.Local) ...[
+              if (isAuthEnabled && user.authType != AuthTypePB.Local) ...[
                 const VSpace(12),
                 UserEmailInput(user.email),
               ],
@@ -190,7 +190,7 @@ class SettingsUserView extends StatelessWidget {
     BuildContext context,
     SettingsUserState state,
   ) {
-    if (!isCloudEnabled) {
+    if (!isAuthEnabled) {
       return const SizedBox.shrink();
     }
 
diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.lock b/frontend/appflowy_tauri/src-tauri/Cargo.lock
index 2d72eb4b6d..b38b5c3a43 100644
--- a/frontend/appflowy_tauri/src-tauri/Cargo.lock
+++ b/frontend/appflowy_tauri/src-tauri/Cargo.lock
@@ -2148,6 +2148,7 @@ dependencies = [
  "client-api",
  "collab-database",
  "collab-document",
+ "collab-persistence",
  "fancy-regex 0.11.0",
  "flowy-codegen",
  "flowy-derive",
diff --git a/frontend/resources/translations/en.json b/frontend/resources/translations/en.json
index 07d92af9cb..3ad9e271e9 100644
--- a/frontend/resources/translations/en.json
+++ b/frontend/resources/translations/en.json
@@ -266,9 +266,27 @@
       "selfEncryptionLogoutPrompt": "Are you sure you want to log out? Please ensure you have copied the encryption secret",
       "syncSetting": "Sync Setting",
       "cloudSetting": "Cloud Setting",
-      "cloudURL": "Server URL",
       "enableSync": "Enable sync",
       "enableEncrypt": "Encrypt data",
+      "cloudURL": "Base URL",
+      "invalidCloudURLScheme": "Invalid Scheme",
+      "cloudServerType": "Cloud server",
+      "cloudServerTypeTip": "Please note that it might log out your current account after switching the cloud server",
+      "cloudLocal": "Local",
+      "cloudSupabase": "Supabase",
+      "cloudSupabaseUrl": "Supabase URL",
+      "cloudSupabaseAnonKey": "Supabase anon key",
+      "cloudSupabaseAnonKeyCanNotBeEmpty": "The anon key can't be empty if the supabase url is not empty",
+      "cloudAppFlowy": "AppFlowy Cloud",
+      "clickToCopy": "Click to copy",
+      "selfHostStart": "If you don't have a server, please refer to the",
+      "selfHostContent": "document",
+      "selfHostEnd": "for guidance on how to self-host your own server",
+      "cloudURLHint": "Input the base URL of your server",
+      "cloudWSURL": "Websocket URL",
+      "cloudWSURLHint": "Input the websocket address of your server",
+      "restartApp": "Restart",
+      "restartAppTip": "Restart the application for the changes to take effect. Please note that this might log out your current account",
       "enableEncryptPrompt": "Activate encryption to secure your data with this secret. Store it safely; once enabled, it can't be turned off. If lost, your data becomes irretrievable. Click to copy",
       "inputEncryptPrompt": "Please enter your encryption secret for",
       "clickToCopySecret": "Click to copy secret",
diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock
index e7d513bd76..beb8953f56 100644
--- a/frontend/rust-lib/Cargo.lock
+++ b/frontend/rust-lib/Cargo.lock
@@ -1952,6 +1952,7 @@ dependencies = [
  "client-api",
  "collab-database",
  "collab-document",
+ "collab-persistence",
  "fancy-regex 0.11.0",
  "flowy-codegen",
  "flowy-derive",
diff --git a/frontend/rust-lib/dart-ffi/src/env_serde.rs b/frontend/rust-lib/dart-ffi/src/env_serde.rs
index 75a897a073..0c9ff74e74 100644
--- a/frontend/rust-lib/dart-ffi/src/env_serde.rs
+++ b/frontend/rust-lib/dart-ffi/src/env_serde.rs
@@ -25,13 +25,7 @@ impl AppFlowyDartConfiguration {
   pub fn write_env_from(env_str: &str) {
     let configuration = Self::from_str(env_str);
     configuration.cloud_type.write_env();
-    let is_valid = configuration.appflowy_cloud_config.write_env().is_ok();
-    // Note on Configuration Priority:
-    // If both Supabase config and AppFlowy cloud config are provided in the '.env' file,
-    // the AppFlowy cloud config will be prioritized and the Supabase config ignored.
-    // Ensure only one of these configurations is active at any given time.
-    if !is_valid {
-      let _ = configuration.supabase_config.write_env();
-    }
+    configuration.appflowy_cloud_config.write_env();
+    configuration.supabase_config.write_env();
   }
 }
diff --git a/frontend/rust-lib/dart-ffi/src/lib.rs b/frontend/rust-lib/dart-ffi/src/lib.rs
index 7d5c4bd0f7..ab116e1adc 100644
--- a/frontend/rust-lib/dart-ffi/src/lib.rs
+++ b/frontend/rust-lib/dart-ffi/src/lib.rs
@@ -55,14 +55,8 @@ pub extern "C" fn init_sdk(data: *mut c_char) -> i64 {
   let configuration = AppFlowyDartConfiguration::from_str(serde_str);
 
   configuration.cloud_type.write_env();
-  let is_valid = configuration.appflowy_cloud_config.write_env().is_ok();
-  // Note on Configuration Priority:
-  // If both Supabase config and AppFlowy cloud config are provided in the '.env' file,
-  // the AppFlowy cloud config will be prioritized and the Supabase config ignored.
-  // Ensure only one of these configurations is active at any given time.
-  if !is_valid {
-    let _ = configuration.supabase_config.write_env();
-  }
+  configuration.appflowy_cloud_config.write_env();
+  configuration.supabase_config.write_env();
 
   let log_crates = vec!["flowy-ffi".to_string()];
   let config = AppFlowyCoreConfig::new(
diff --git a/frontend/rust-lib/event-integration/tests/util.rs b/frontend/rust-lib/event-integration/tests/util.rs
index c0066bbfd7..c62b58360c 100644
--- a/frontend/rust-lib/event-integration/tests/util.rs
+++ b/frontend/rust-lib/event-integration/tests/util.rs
@@ -213,7 +213,7 @@ impl AFCloudTest {
     test.set_auth_type(AuthTypePB::AFCloud);
     test
       .server_provider
-      .set_authenticator(Authenticator::AFCloud);
+      .set_authenticator(Authenticator::AppFlowyCloud);
 
     Some(Self { inner: test })
   }
diff --git a/frontend/rust-lib/flowy-core/src/config.rs b/frontend/rust-lib/flowy-core/src/config.rs
index 53ed1985c4..eb0a503158 100644
--- a/frontend/rust-lib/flowy-core/src/config.rs
+++ b/frontend/rust-lib/flowy-core/src/config.rs
@@ -35,6 +35,7 @@ impl fmt::Debug for AppFlowyCoreConfig {
     if let Some(config) = &self.cloud_config {
       debug.field("base_url", &config.base_url);
       debug.field("ws_url", &config.ws_base_url);
+      debug.field("gotrue_url", &config.gotrue_url);
     }
     debug.finish()
   }
@@ -43,8 +44,12 @@ impl fmt::Debug for AppFlowyCoreConfig {
 fn migrate_local_version_data_folder(root: &str, url: &str) -> String {
   // Isolate the user data folder by using the base url of AppFlowy cloud. This is to avoid
   // the user data folder being shared by different AppFlowy cloud.
-  let server_base64 = URL_SAFE_ENGINE.encode(url);
-  let storage_path = format!("{}_{}", root, server_base64);
+  let storage_path = if !url.is_empty() {
+    let server_base64 = URL_SAFE_ENGINE.encode(url);
+    format!("{}_{}", root, server_base64)
+  } else {
+    root.to_string()
+  };
 
   // Copy the user data folder from the root path to the isolated path
   // The root path without any suffix is the created by the local version AppFlowy
diff --git a/frontend/rust-lib/flowy-core/src/integrate/server.rs b/frontend/rust-lib/flowy-core/src/integrate/server.rs
index 57aa7ff2a6..666890ea6d 100644
--- a/frontend/rust-lib/flowy-core/src/integrate/server.rs
+++ b/frontend/rust-lib/flowy-core/src/integrate/server.rs
@@ -14,7 +14,6 @@ use flowy_server_config::af_cloud_config::AFCloudConfiguration;
 use flowy_server_config::supabase_config::SupabaseConfiguration;
 use flowy_sqlite::kv::StorePreferences;
 use flowy_user::services::database::{get_user_profile, get_user_workspace, open_user_db};
-use flowy_user_deps::cloud::UserCloudService;
 use flowy_user_deps::entities::*;
 
 use crate::AppFlowyCoreConfig;
@@ -56,8 +55,6 @@ pub struct ServerProvider {
   providers: RwLock<HashMap<ServerType, Arc<dyn AppFlowyServer>>>,
   pub(crate) encryption: RwLock<Arc<dyn AppFlowyEncryption>>,
   pub(crate) store_preferences: Weak<StorePreferences>,
-  pub(crate) cache_user_service: RwLock<HashMap<ServerType, Arc<dyn UserCloudService>>>,
-
   pub(crate) enable_sync: RwLock<bool>,
   pub(crate) uid: Arc<RwLock<Option<i64>>>,
 }
@@ -76,7 +73,6 @@ impl ServerProvider {
       enable_sync: RwLock::new(true),
       encryption: RwLock::new(Arc::new(encryption)),
       store_preferences,
-      cache_user_service: Default::default(),
       uid: Default::default(),
     }
   }
@@ -148,7 +144,7 @@ impl From<Authenticator> for ServerType {
   fn from(auth_provider: Authenticator) -> Self {
     match auth_provider {
       Authenticator::Local => ServerType::Local,
-      Authenticator::AFCloud => ServerType::AFCloud,
+      Authenticator::AppFlowyCloud => ServerType::AFCloud,
       Authenticator::Supabase => ServerType::Supabase,
     }
   }
@@ -158,7 +154,7 @@ impl From<ServerType> for Authenticator {
   fn from(ty: ServerType) -> Self {
     match ty {
       ServerType::Local => Authenticator::Local,
-      ServerType::AFCloud => Authenticator::AFCloud,
+      ServerType::AFCloud => Authenticator::AppFlowyCloud,
       ServerType::Supabase => Authenticator::Supabase,
     }
   }
diff --git a/frontend/rust-lib/flowy-core/src/integrate/trait_impls.rs b/frontend/rust-lib/flowy-core/src/integrate/trait_impls.rs
index 2893e8ea5d..b6b732f20d 100644
--- a/frontend/rust-lib/flowy-core/src/integrate/trait_impls.rs
+++ b/frontend/rust-lib/flowy-core/src/integrate/trait_impls.rs
@@ -113,16 +113,8 @@ impl UserCloudServiceProvider for ServerProvider {
   /// Returns the [UserCloudService] base on the current [ServerType].
   /// Creates a new [AppFlowyServer] if it doesn't exist.
   fn get_user_service(&self) -> Result<Arc<dyn UserCloudService>, FlowyError> {
-    if let Some(user_service) = self.cache_user_service.read().get(&self.get_server_type()) {
-      return Ok(user_service.clone());
-    }
-
     let server_type = self.get_server_type();
     let user_service = self.get_server(&server_type)?.user_service();
-    self
-      .cache_user_service
-      .write()
-      .insert(server_type, user_service.clone());
     Ok(user_service)
   }
 
diff --git a/frontend/rust-lib/flowy-error/Cargo.toml b/frontend/rust-lib/flowy-error/Cargo.toml
index 6a99f0b155..8047ac5119 100644
--- a/frontend/rust-lib/flowy-error/Cargo.toml
+++ b/frontend/rust-lib/flowy-error/Cargo.toml
@@ -27,6 +27,7 @@ r2d2 = { version = "0.8", optional = true }
 url = { version = "2.2", optional = true }
 collab-database = { version = "0.1.0", optional = true }
 collab-document = { version = "0.1.0", optional = true }
+collab-persistence = { version = "0.1.0", optional = true }
 tokio-postgres = { version = "0.7.8", optional = true }
 client-api = { version = "0.1.0", optional = true }
 
@@ -36,7 +37,7 @@ impl_from_dispatch_error = ["lib-dispatch"]
 impl_from_serde = []
 impl_from_reqwest = ["reqwest"]
 impl_from_sqlite = ["flowy-sqlite", "r2d2"]
-impl_from_collab = ["collab-database", "collab-document", "impl_from_reqwest"]
+impl_from_collab = ["collab-database", "collab-document", "impl_from_reqwest", "collab-persistence"]
 impl_from_postgres = ["tokio-postgres"]
 impl_from_url = ["url"]
 impl_from_appflowy_cloud = ["client-api"]
diff --git a/frontend/rust-lib/flowy-error/src/impl_from/collab.rs b/frontend/rust-lib/flowy-error/src/impl_from/collab.rs
index f55c6f5440..f939653ad8 100644
--- a/frontend/rust-lib/flowy-error/src/impl_from/collab.rs
+++ b/frontend/rust-lib/flowy-error/src/impl_from/collab.rs
@@ -1,8 +1,18 @@
 use collab_database::error::DatabaseError;
 use collab_document::error::DocumentError;
+use collab_persistence::PersistenceError;
 
-use crate::FlowyError;
+use crate::{ErrorCode, FlowyError};
 
+impl From<PersistenceError> for FlowyError {
+  fn from(err: PersistenceError) -> Self {
+    match err {
+      PersistenceError::RocksdbCorruption(_) => FlowyError::new(ErrorCode::RocksdbCorruption, err),
+      PersistenceError::RocksdbIOError(_) => FlowyError::new(ErrorCode::RocksdbIOError, err),
+      _ => FlowyError::new(ErrorCode::RocksdbInternal, err),
+    }
+  }
+}
 impl From<DatabaseError> for FlowyError {
   fn from(error: DatabaseError) -> Self {
     FlowyError::internal().with_context(error)
diff --git a/frontend/rust-lib/flowy-server-config/src/af_cloud_config.rs b/frontend/rust-lib/flowy-server-config/src/af_cloud_config.rs
index fb735a9131..d75c30a673 100644
--- a/frontend/rust-lib/flowy-server-config/src/af_cloud_config.rs
+++ b/frontend/rust-lib/flowy-server-config/src/af_cloud_config.rs
@@ -2,7 +2,7 @@ use std::fmt::Display;
 
 use serde::{Deserialize, Serialize};
 
-use flowy_error::{ErrorCode, FlowyError, FlowyResult};
+use flowy_error::{ErrorCode, FlowyError};
 
 pub const APPFLOWY_CLOUD_BASE_URL: &str = "APPFLOWY_CLOUD_ENV_APPFLOWY_CLOUD_BASE_URL";
 pub const APPFLOWY_CLOUD_WS_BASE_URL: &str = "APPFLOWY_CLOUD_ENV_APPFLOWY_CLOUD_WS_BASE_URL";
@@ -60,25 +60,10 @@ impl AFCloudConfiguration {
     })
   }
 
-  pub fn validate(&self) -> Result<(), FlowyError> {
-    if self.base_url.is_empty() || self.ws_base_url.is_empty() || self.gotrue_url.is_empty() {
-      return Err(FlowyError::new(
-        ErrorCode::InvalidAuthConfig,
-        format!(
-          "Invalid APPFLOWY_CLOUD_BASE_URL: {}, APPFLOWY_CLOUD_WS_BASE_URL: {}, APPFLOWY_CLOUD_GOTRUE_URL: {}",
-          self.base_url, self.ws_base_url, self.gotrue_url,
-        )),
-      );
-    }
-    Ok(())
-  }
-
   /// Write the configuration to the environment variables.
-  pub fn write_env(&self) -> FlowyResult<()> {
-    self.validate()?;
+  pub fn write_env(&self) {
     std::env::set_var(APPFLOWY_CLOUD_BASE_URL, &self.base_url);
     std::env::set_var(APPFLOWY_CLOUD_WS_BASE_URL, &self.ws_base_url);
     std::env::set_var(APPFLOWY_CLOUD_GOTRUE_URL, &self.gotrue_url);
-    Ok(())
   }
 }
diff --git a/frontend/rust-lib/flowy-server-config/src/supabase_config.rs b/frontend/rust-lib/flowy-server-config/src/supabase_config.rs
index 6e2a08170b..90dbe39bc5 100644
--- a/frontend/rust-lib/flowy-server-config/src/supabase_config.rs
+++ b/frontend/rust-lib/flowy-server-config/src/supabase_config.rs
@@ -1,6 +1,6 @@
 use serde::{Deserialize, Serialize};
 
-use flowy_error::{ErrorCode, FlowyError, FlowyResult};
+use flowy_error::{ErrorCode, FlowyError};
 
 pub const SUPABASE_URL: &str = "APPFLOWY_CLOUD_ENV_SUPABASE_URL";
 pub const SUPABASE_ANON_KEY: &str = "APPFLOWY_CLOUD_ENV_SUPABASE_ANON_KEY";
@@ -33,21 +33,9 @@ impl SupabaseConfiguration {
     Ok(Self { url, anon_key })
   }
 
-  pub fn validate(&self) -> Result<(), FlowyError> {
-    if self.url.is_empty() || self.anon_key.is_empty() {
-      return Err(FlowyError::new(
-        ErrorCode::InvalidAuthConfig,
-        "Missing SUPABASE_URL or SUPABASE_ANON_KEY",
-      ));
-    }
-    Ok(())
-  }
-
   /// Write the configuration to the environment variables.
-  pub fn write_env(&self) -> FlowyResult<()> {
-    self.validate()?;
+  pub fn write_env(&self) {
     std::env::set_var(SUPABASE_URL, &self.url);
     std::env::set_var(SUPABASE_ANON_KEY, &self.anon_key);
-    Ok(())
   }
 }
diff --git a/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/dto.rs b/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/dto.rs
index cda882766c..4b727e8427 100644
--- a/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/dto.rs
+++ b/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/dto.rs
@@ -57,7 +57,7 @@ pub fn user_profile_from_af_profile(
     openai_key: openai_key.unwrap_or_default(),
     stability_ai_key: stability_ai_key.unwrap_or_default(),
     workspace_id: profile.latest_workspace_id.to_string(),
-    authenticator: Authenticator::AFCloud,
+    authenticator: Authenticator::AppFlowyCloud,
     encryption_type,
     uid: profile.uid,
     updated_at: profile.updated_at,
diff --git a/frontend/rust-lib/flowy-user-deps/src/entities.rs b/frontend/rust-lib/flowy-user-deps/src/entities.rs
index 05642f7da4..3ee7f535cf 100644
--- a/frontend/rust-lib/flowy-user-deps/src/entities.rs
+++ b/frontend/rust-lib/flowy-user-deps/src/entities.rs
@@ -327,7 +327,7 @@ pub enum Authenticator {
   Local = 0,
   /// Currently not supported. It will be supported in the future when the
   /// [AppFlowy-Server](https://github.com/AppFlowy-IO/AppFlowy-Server) ready.
-  AFCloud = 1,
+  AppFlowyCloud = 1,
   /// It uses Supabase as the backend.
   Supabase = 2,
 }
@@ -348,7 +348,7 @@ impl From<i32> for Authenticator {
   fn from(value: i32) -> Self {
     match value {
       0 => Authenticator::Local,
-      1 => Authenticator::AFCloud,
+      1 => Authenticator::AppFlowyCloud,
       2 => Authenticator::Supabase,
       _ => Authenticator::Local,
     }
diff --git a/frontend/rust-lib/flowy-user/src/entities/user_setting.rs b/frontend/rust-lib/flowy-user/src/entities/user_setting.rs
index 810bcba00b..caaf524381 100644
--- a/frontend/rust-lib/flowy-user/src/entities/user_setting.rs
+++ b/frontend/rust-lib/flowy-user/src/entities/user_setting.rs
@@ -244,3 +244,15 @@ impl std::default::Default for NotificationSettingsPB {
     }
   }
 }
+
+#[derive(Default, ProtoBuf)]
+pub struct AppFlowyCloudSettingPB {
+  #[pb(index = 1)]
+  pub base_url: bool,
+
+  #[pb(index = 2)]
+  pub ws_addr: bool,
+
+  #[pb(index = 3)]
+  pub gotrue_url: String,
+}
diff --git a/frontend/rust-lib/flowy-user/src/event_handler.rs b/frontend/rust-lib/flowy-user/src/event_handler.rs
index ab36866300..7b1b3f0d1c 100644
--- a/frontend/rust-lib/flowy-user/src/event_handler.rs
+++ b/frontend/rust-lib/flowy-user/src/event_handler.rs
@@ -2,6 +2,7 @@ use std::sync::Weak;
 use std::{convert::TryInto, sync::Arc};
 
 use serde_json::Value;
+use tracing::event;
 
 use flowy_error::{ErrorCode, FlowyError, FlowyResult};
 use flowy_sqlite::kv::StorePreferences;
@@ -86,7 +87,7 @@ pub async fn get_user_profile_handler(
 ) -> DataResult<UserProfilePB, FlowyError> {
   let manager = upgrade_manager(manager)?;
   let uid = manager.get_session()?.user_id;
-  let mut user_profile = manager.get_user_profile(uid).await?;
+  let mut user_profile = manager.get_user_profile_from_disk(uid).await?;
 
   let weak_manager = Arc::downgrade(&manager);
   let cloned_user_profile = user_profile.clone();
@@ -285,6 +286,7 @@ pub async fn sign_in_with_provider_handler(
   let manager = upgrade_manager(manager)?;
   tracing::debug!("Sign in with provider: {:?}", data.provider.as_str());
   let sign_in_url = manager.generate_oauth_url(data.provider.as_str()).await?;
+  event!(tracing::Level::DEBUG, "Sign in url: {}", sign_in_url);
   data_result_ok(OauthProviderDataPB {
     oauth_url: sign_in_url,
   })
@@ -332,7 +334,7 @@ pub async fn check_encrypt_secret_handler(
 ) -> DataResult<UserEncryptionConfigurationPB, FlowyError> {
   let manager = upgrade_manager(manager)?;
   let uid = manager.get_session()?.user_id;
-  let profile = manager.get_user_profile(uid).await?;
+  let profile = manager.get_user_profile_from_disk(uid).await?;
 
   let is_need_secret = match profile.encryption_type {
     EncryptionType::NoEncryption => false,
@@ -402,7 +404,8 @@ pub async fn set_cloud_config_handler(
   };
 
   send_notification(
-    &session.user_id.to_string(),
+    // Don't change this key. it's also used in the frontend
+    "user_cloud_config",
     UserNotification::DidUpdateCloudConfig,
   )
   .payload(payload)
diff --git a/frontend/rust-lib/flowy-user/src/manager.rs b/frontend/rust-lib/flowy-user/src/manager.rs
index d6200402c6..f90c0ffbcd 100644
--- a/frontend/rust-lib/flowy-user/src/manager.rs
+++ b/frontend/rust-lib/flowy-user/src/manager.rs
@@ -16,6 +16,7 @@ use tracing::{debug, error, event, info, instrument};
 use collab_integrate::collab_builder::AppFlowyCollabBuilder;
 use collab_integrate::RocksCollabDB;
 use flowy_error::{internal_error, ErrorCode, FlowyResult};
+use flowy_server_config::AuthenticatorType;
 use flowy_sqlite::kv::StorePreferences;
 use flowy_sqlite::schema::user_table;
 use flowy_sqlite::ConnectionPool;
@@ -157,7 +158,27 @@ impl UserManager {
     *self.collab_interact.write().await = Arc::new(collab_interact);
 
     if let Ok(session) = self.get_session() {
-      let user = self.get_user_profile(session.user_id).await?;
+      let user = self.get_user_profile_from_disk(session.user_id).await?;
+
+      // Get the current authenticator from the environment variable
+      let current_authenticator = match AuthenticatorType::from_env() {
+        AuthenticatorType::Local => Authenticator::Local,
+        AuthenticatorType::Supabase => Authenticator::Supabase,
+        AuthenticatorType::AppFlowyCloud => Authenticator::AppFlowyCloud,
+      };
+
+      // If the current authenticator is different from the authenticator in the session and it's
+      // not a local authenticator, we need to sign out the user.
+      if user.authenticator != Authenticator::Local && user.authenticator != current_authenticator {
+        event!(
+          tracing::Level::INFO,
+          "Authenticator changed from {:?} to {:?}",
+          user.authenticator,
+          current_authenticator
+        );
+        self.sign_out().await?;
+        return Ok(());
+      }
 
       event!(
         tracing::Level::INFO,
@@ -201,9 +222,7 @@ impl UserManager {
       // Do the user data migration if needed
       event!(tracing::Level::INFO, "Prepare user data migration");
       match (
-        self
-          .database
-          .get_collab_db(session.user_id, &self.user_config.device_id),
+        self.database.get_collab_db(session.user_id),
         self.database.get_pool(session.user_id),
       ) {
         (Ok(collab_db), Ok(sqlite_pool)) => {
@@ -257,7 +276,7 @@ impl UserManager {
   pub fn get_collab_db(&self, uid: i64) -> Result<Weak<RocksCollabDB>, FlowyError> {
     self
       .database
-      .get_collab_db(uid, "")
+      .get_collab_db(uid)
       .map(|collab_db| Arc::downgrade(&collab_db))
   }
 
@@ -477,7 +496,7 @@ impl UserManager {
     let session = self.get_session()?;
     upsert_user_profile_change(session.user_id, self.db_pool(session.user_id)?, changeset)?;
 
-    let profile = self.get_user_profile(session.user_id).await?;
+    let profile = self.get_user_profile_from_disk(session.user_id).await?;
     self
       .update_user(session.user_id, profile.token, params)
       .await?;
@@ -507,7 +526,7 @@ impl UserManager {
   }
 
   /// Fetches the user profile for the given user ID.
-  pub async fn get_user_profile(&self, uid: i64) -> Result<UserProfile, FlowyError> {
+  pub async fn get_user_profile_from_disk(&self, uid: i64) -> Result<UserProfile, FlowyError> {
     let user: UserProfile = user_table::dsl::user_table
       .filter(user_table::id.eq(&uid.to_string()))
       .first::<UserTable>(&*(self.db_connection(uid)?))
@@ -524,14 +543,18 @@ impl UserManager {
 
   #[tracing::instrument(level = "info", skip_all, err)]
   pub async fn refresh_user_profile(&self, old_user_profile: &UserProfile) -> FlowyResult<()> {
-    let now = chrono::Utc::now().timestamp();
+    // If the user is a local user, no need to refresh the user profile
+    if old_user_profile.authenticator.is_local() {
+      return Ok(());
+    }
 
+    let now = chrono::Utc::now().timestamp();
     // Add debounce to avoid too many requests
     if now - self.refresh_user_profile_since.load(Ordering::SeqCst) < 5 {
       return Ok(());
     }
-
     self.refresh_user_profile_since.store(now, Ordering::SeqCst);
+
     let uid = old_user_profile.uid;
     let result: Result<UserProfile, FlowyError> = self
       .cloud_services
@@ -541,27 +564,6 @@ impl UserManager {
 
     match result {
       Ok(new_user_profile) => {
-        // If the authentication type has changed, it indicates that the user has signed in
-        // using a different release package but is sharing the same data folder.
-        // In such cases, notify the frontend to log out.
-        if old_user_profile.authenticator != Authenticator::Local
-          && new_user_profile.authenticator != old_user_profile.authenticator
-        {
-          event!(
-            tracing::Level::INFO,
-            "User login with different authenticator: {:?} -> {:?}",
-            old_user_profile.authenticator,
-            new_user_profile.authenticator
-          );
-
-          send_auth_state_notification(AuthStateChangedPB {
-            state: AuthStatePB::InvalidAuth,
-            message: "User login with different cloud".to_string(),
-          })
-          .send();
-          return Ok(());
-        }
-
         // If the user profile is updated, save the new user profile
         if new_user_profile.updated_at > old_user_profile.updated_at {
           validate_encryption_sign(old_user_profile, &new_user_profile.encryption_type.sign());
@@ -578,6 +580,15 @@ impl UserManager {
             tracing::Level::ERROR,
             "User is unauthorized, sign out the user"
           );
+
+          self.add_historical_user(
+            uid,
+            &self.user_config.device_id,
+            old_user_profile.name.clone(),
+            &old_user_profile.authenticator,
+            self.user_dir(uid),
+          );
+
           self.sign_out().await?;
           send_auth_state_notification(AuthStateChangedPB {
             state: AuthStatePB::InvalidAuth,
@@ -592,7 +603,7 @@ impl UserManager {
 
   #[instrument(level = "info", skip_all)]
   pub fn user_dir(&self, uid: i64) -> String {
-    self.user_paths.user_dir(uid)
+    self.user_paths.user_data_dir(uid)
   }
 
   pub fn user_setting(&self) -> Result<UserSettingPB, FlowyError> {
@@ -713,7 +724,9 @@ impl UserManager {
     &self,
     oauth_provider: &str,
   ) -> Result<String, FlowyError> {
-    self.update_authenticator(&Authenticator::AFCloud).await;
+    self
+      .update_authenticator(&Authenticator::AppFlowyCloud)
+      .await;
     let auth_service = self.cloud_services.get_user_service()?;
     let url = auth_service
       .generate_oauth_url_with_provider(oauth_provider)
@@ -757,7 +770,7 @@ impl UserManager {
     let session = self.get_session()?;
     if session.user_id == user_update.uid {
       debug!("Receive user update: {:?}", user_update);
-      let user_profile = self.get_user_profile(user_update.uid).await?;
+      let user_profile = self.get_user_profile_from_disk(user_update.uid).await?;
       if !validate_encryption_sign(&user_profile, &user_update.encryption_sign) {
         return Ok(());
       }
@@ -778,12 +791,8 @@ impl UserManager {
     old_user: &MigrationUser,
     new_user: &MigrationUser,
   ) -> Result<(), FlowyError> {
-    let old_collab_db = self
-      .database
-      .get_collab_db(old_user.session.user_id, &self.user_config.device_id)?;
-    let new_collab_db = self
-      .database
-      .get_collab_db(new_user.session.user_id, &self.user_config.device_id)?;
+    let old_collab_db = self.database.get_collab_db(old_user.session.user_id)?;
+    let new_collab_db = self.database.get_collab_db(new_user.session.user_id)?;
     migration_anon_user_on_sign_up(old_user, &old_collab_db, new_user, &new_collab_db)?;
 
     if let Err(err) = sync_user_data_to_cloud(
@@ -859,24 +868,26 @@ impl UserPaths {
   fn new(root: String) -> Self {
     Self { root }
   }
-  fn user_dir(&self, uid: i64) -> String {
+
+  /// Returns the path to the user's data directory.
+  fn user_data_dir(&self, uid: i64) -> String {
     format!("{}/{}", self.root, uid)
   }
 }
 
 impl UserDBPath for UserPaths {
   fn user_db_path(&self, uid: i64) -> PathBuf {
-    PathBuf::from(self.user_dir(uid))
+    PathBuf::from(self.user_data_dir(uid))
   }
 
   fn collab_db_path(&self, uid: i64) -> PathBuf {
-    let mut path = PathBuf::from(self.user_dir(uid));
+    let mut path = PathBuf::from(self.user_data_dir(uid));
     path.push("collab_db");
     path
   }
 
   fn collab_db_history(&self, uid: i64, create_if_not_exist: bool) -> std::io::Result<PathBuf> {
-    let path = PathBuf::from(self.user_dir(uid)).join("collab_db_history");
+    let path = PathBuf::from(self.user_data_dir(uid)).join("collab_db_history");
     if !path.exists() && create_if_not_exist {
       fs::create_dir_all(&path)?;
     }
diff --git a/frontend/rust-lib/flowy-user/src/services/database.rs b/frontend/rust-lib/flowy-user/src/services/database.rs
index e9a6a564f1..1ab6d34b28 100644
--- a/frontend/rust-lib/flowy-user/src/services/database.rs
+++ b/frontend/rust-lib/flowy-user/src/services/database.rs
@@ -7,7 +7,7 @@ use parking_lot::RwLock;
 use tracing::{error, event, info, instrument};
 
 use collab_integrate::{PersistenceError, RocksCollabDB, YrsDocAction};
-use flowy_error::{ErrorCode, FlowyError};
+use flowy_error::FlowyError;
 use flowy_sqlite::schema::user_workspace_table;
 use flowy_sqlite::ConnectionPool;
 use flowy_sqlite::{
@@ -50,40 +50,47 @@ impl UserDB {
   ///         attempts to restore the database from the latest backup.
   ///   - If the CollabDB does not exist, it immediately attempts to restore from the latest backup.
   ///
+  #[instrument(level = "debug", skip_all)]
   pub fn backup_or_restore(&self, uid: i64, workspace_id: &str) {
+    // Obtain the path for the collaboration database.
     let collab_db_path = self.paths.collab_db_path(uid);
+
+    // Obtain the history folder path, proceed if successful.
     if let Ok(history_folder) = self.paths.collab_db_history(uid, true) {
+      // Initialize the backup utility for the collaboration database.
+      let zip_backup = CollabDBZipBackup::new(collab_db_path.clone(), history_folder);
+
       if collab_db_path.exists() {
+        // Validate the existing collaboration database.
         let is_ok = validate_collab_db(&collab_db_path, uid, workspace_id);
-        let zip_backup = CollabDBZipBackup::new(collab_db_path, history_folder);
+
         if is_ok {
-          // If the database opens successfully, it attempts to back it up in the background.
+          // If database is valid, update the shared map and initiate backup.
+          // Asynchronous backup operation.
           af_spawn(async move {
-            let _ = tokio::task::spawn_blocking(move || {
-              if let Err(err) = zip_backup.backup() {
-                error!("backup collab db failed, {:?}", err);
-              }
-            })
-            .await;
+            if let Err(err) = tokio::task::spawn_blocking(move || zip_backup.backup()).await {
+              error!("Backup of collab db failed: {:?}", err);
+            }
           });
         } else if let Err(err) = zip_backup.restore_latest_backup() {
-          error!("restore collab db failed, {:?}", err);
-        }
-      } else {
-        let zip_backup = CollabDBZipBackup::new(collab_db_path, history_folder);
-        if let Err(err) = zip_backup.restore_latest_backup() {
-          error!("restore collab db failed, {:?}", err);
+          // If validation fails, attempt to restore from the latest backup.
+          error!("Restoring collab db failed: {:?}", err);
         }
+      } else if let Err(err) = zip_backup.restore_latest_backup() {
+        // If collab database does not exist, attempt to restore from the latest backup.
+        error!("Restoring collab db failed: {:?}", err);
       }
     }
   }
 
+  #[instrument(level = "debug", skip_all)]
   pub fn restore_if_need(&self, uid: i64, workspace_id: &str) {
     if let Ok(history_folder) = self.paths.collab_db_history(uid, false) {
       let collab_db_path = self.paths.collab_db_path(uid);
       let is_ok = validate_collab_db(&collab_db_path, uid, workspace_id);
-      let zip_backup = CollabDBZipBackup::new(collab_db_path, history_folder);
+
       if !is_ok {
+        let zip_backup = CollabDBZipBackup::new(collab_db_path, history_folder);
         if let Err(err) = zip_backup.restore_latest_backup() {
           error!("restore collab db failed, {:?}", err);
         }
@@ -118,15 +125,11 @@ impl UserDB {
     Ok(pool)
   }
 
-  pub(crate) fn get_collab_db(
-    &self,
-    user_id: i64,
-    _device_id: &str,
-  ) -> Result<Arc<RocksCollabDB>, FlowyError> {
+  pub(crate) fn get_collab_db(&self, user_id: i64) -> Result<Arc<RocksCollabDB>, FlowyError> {
     let collab_db = open_collab_db(
-      self.paths.user_db_path(user_id),
+      // self.paths.user_db_path(user_id),
       self.paths.collab_db_path(user_id),
-      self.paths.collab_db_history(user_id, false).ok(),
+      // self.paths.collab_db_history(user_id, false).ok(),
       user_id,
     )?;
     Ok(collab_db)
@@ -175,31 +178,24 @@ pub fn get_user_workspace(
 /// Open a collab db for the user. If the db is already opened, return the opened db.
 ///
 fn open_collab_db(
-  _collab_backup_db_path: impl AsRef<Path>,
   collab_db_path: impl AsRef<Path>,
-  _collab_db_history: Option<PathBuf>,
   uid: i64,
-) -> Result<Arc<RocksCollabDB>, FlowyError> {
+) -> Result<Arc<RocksCollabDB>, PersistenceError> {
   if let Some(collab_db) = COLLAB_DB_MAP.read().get(&uid) {
     return Ok(collab_db.clone());
   }
 
   let mut write_guard = COLLAB_DB_MAP.write();
+  info!(
+    "open collab db for user {} at path: {:?}",
+    uid,
+    collab_db_path.as_ref()
+  );
   let db = match RocksCollabDB::open(&collab_db_path) {
     Ok(db) => Ok(db),
     Err(err) => {
-      tracing::error!("open collab db error, {:?}", err);
-      match err {
-        PersistenceError::RocksdbCorruption(_) => {
-          // try restore from the backup db
-          Err(FlowyError::new(ErrorCode::RocksdbCorruption, err))
-        },
-        PersistenceError::RocksdbIOError(_) => {
-          //
-          Err(FlowyError::new(ErrorCode::RocksdbIOError, err))
-        },
-        _ => Err(FlowyError::new(ErrorCode::RocksdbInternal, err)),
-      }
+      error!("open collab db error, {:?}", err);
+      Err(err)
     },
   }?;
 
@@ -337,12 +333,17 @@ pub(crate) fn validate_collab_db(
   // Attempt to open the collaboration database using the workspace_id. The workspace_id must already
   // exist in the collab database. If it does not, it may be indicative of corruption in the collab database
   // due to other factors.
-  let result = RocksCollabDB::open(&collab_db_path).map(|db| {
-    let read_txn = db.read_txn();
-    read_txn.is_exist(uid, workspace_id)
-  });
-  match result {
-    Ok(is_ok) => is_ok,
+  info!(
+    "open collab db to validate data integration for user {} at path: {:?}",
+    uid,
+    collab_db_path.as_ref()
+  );
+
+  match open_collab_db(&collab_db_path, uid) {
+    Ok(db) => {
+      let read_txn = db.read_txn();
+      read_txn.is_exist(uid, workspace_id)
+    },
     // return false if the error is not related to corruption
     Err(err) => !matches!(
       err,
diff --git a/frontend/rust-lib/flowy-user/src/services/entities.rs b/frontend/rust-lib/flowy-user/src/services/entities.rs
index f42dd84d06..7c779584e5 100644
--- a/frontend/rust-lib/flowy-user/src/services/entities.rs
+++ b/frontend/rust-lib/flowy-user/src/services/entities.rs
@@ -155,7 +155,7 @@ impl From<AuthTypePB> for Authenticator {
     match pb {
       AuthTypePB::Supabase => Authenticator::Supabase,
       AuthTypePB::Local => Authenticator::Local,
-      AuthTypePB::AFCloud => Authenticator::AFCloud,
+      AuthTypePB::AFCloud => Authenticator::AppFlowyCloud,
     }
   }
 }
@@ -165,7 +165,7 @@ impl From<Authenticator> for AuthTypePB {
     match auth_type {
       Authenticator::Supabase => AuthTypePB::Supabase,
       Authenticator::Local => AuthTypePB::Local,
-      Authenticator::AFCloud => AuthTypePB::AFCloud,
+      Authenticator::AppFlowyCloud => AuthTypePB::AFCloud,
     }
   }
 }
diff --git a/frontend/rust-lib/flowy-user/src/services/historical_user.rs b/frontend/rust-lib/flowy-user/src/services/historical_user.rs
index 5b6a51c391..d3f06aa8e0 100644
--- a/frontend/rust-lib/flowy-user/src/services/historical_user.rs
+++ b/frontend/rust-lib/flowy-user/src/services/historical_user.rs
@@ -17,7 +17,10 @@ impl UserManager {
     // Only migrate the data if the user is login in as a guest and sign up as a new user if the current
     // auth type is not [AuthType::Local].
     let session = self.get_session().ok()?;
-    let user_profile = self.get_user_profile(session.user_id).await.ok()?;
+    let user_profile = self
+      .get_user_profile_from_disk(session.user_id)
+      .await
+      .ok()?;
     if user_profile.authenticator == Authenticator::Local && !auth_type.is_local() {
       Some(MigrationUser {
         user_profile,
@@ -44,7 +47,7 @@ impl UserManager {
     uid: i64,
     device_id: &str,
     user_name: String,
-    auth_type: &Authenticator,
+    authenticator: &Authenticator,
     storage_path: String,
   ) {
     let mut logger_users = self
@@ -54,7 +57,7 @@ impl UserManager {
     logger_users.add_user(HistoricalUser {
       user_id: uid,
       user_name,
-      auth_type: auth_type.clone(),
+      auth_type: authenticator.clone(),
       sign_in_timestamp: timestamp(),
       storage_path,
       device_id: device_id.to_string(),