diff --git a/README.md b/README.md
index 13c2a07..32be7ae 100644
--- a/README.md
+++ b/README.md
@@ -1,80 +1,67 @@
-# FlashMail
+# kirakira
-FlashMail is a Mobile application designed to provide users with a temporary email solution powered by the mail.tm platform. With FlashMail, users can receive and view emails conveniently while maintaining their privacy. The app utilizes Riverpod as its state management solution for efficient and organized data handling.
+A mobile client for [Smol Mail](../smolmail), the minimalist end-to-end encrypted mail protocol: one Ed25519 identity, five operations, Noise_NX transport, sealed and signed messages. The sibling of the reference CLI client (`../smolmail`) and the browser client (`../gsmol`) — everything they do, in an Android app.
-This repository contains the source code for FlashMail developed using the Flutter framework. The app is designed to provide various features and functionalities to enhance user experience. Below, you will find information about how to download the app, its features, dependencies, license, and more.
+## How it works
-## Download
+```
+app (all crypto, all keys) ⇄ smolmaild (TCP :1961, Noise_NX)
+```
-You can download the latest version of the app from the [GitHub Releases](https://github.com/sarthakkimtani/flash-mail/releases) page. Look for the most recent release and download the corresponding APK or installer file for your device.
+There is no bridge and no server-side account: the Noise session, sealing, signing, pinning and trust-on-first-use all run on the device. The seed lives in Hive app storage — the phone's app sandbox is the trust boundary, as with the CLI client's `identity.key` file.
-## Requirements
+## First use
-- Any Operating System (ie. MacOS X, Linux, Windows)
-- Any IDE with Flutter SDK installed (ie. IntelliJ, Android Studio, VSCode, etc)
-- A little knowledge of Dart and Flutter
+1. Create an identity (or restore from a seed) and back the seed up — it is the only secret.
+2. Pin your home server's public key, obtained from the operator through a trusted channel (SPEC.md §4: registration and fetching refuse unpinned servers).
+3. Register an address, then fetch. Restoring a seed on a new device? Enter the address alongside the seed — or "Recall" from settings — and it rebinds by RESOLVE + key check, without re-registering.
-## Features
+## What it implements
-- Instantly create temporary email addresses to receive messages without revealing your personal email.
-- Enjoy a seamless user experience with an intuitive and user-friendly interface, making it easy to navigate through your emails effortlessly.
-- Access and download attachments with ease, allowing you to view and save important files directly from your emails.
-- Access and read your emails effortlessly with a seamless viewing experience.
-- Benefit from a compact APK size of just 22 MB, ensuring that FlashMail takes up minimal storage space on your device.
+- **Identity** (§2): one 32-byte seed; the X25519 agreement keys are derived from the Ed25519 keypair. Superseded seeds are kept after rotation, since mail sealed to them is readable with nothing else.
+- **Addressing** (§3): short `user@host[:1961]` and self-certifying `smol://user@host/key` addresses; base32 fingerprints.
+- **Trust** (§4, §8): server keys pinned explicitly; mismatch aborts the handshake; registration and fetching require a pin.
+- **Mail** (§5): sealed and signed envelopes with 1 KiB padding, flat frontmatter bodies, sent copies sealed to self. Fetch verifies id and signature before acknowledging — anything unreadable stays on the server.
+- **Contacts**: outgoing mail carries a signed `Reply-To` field with the sender's full `smol://` address (an "anonymous" switch omits it); a first-contact claim binds only when its key matches the message's signer, and never over an address already pinned to a different key. The contacts screen shows each bound key with its trust badge and the keys it displaced — the only local record that a contact rotated — and a re-resolve applies §8: a chain-validated rotation is accepted and announced, an unexplained key change is refused until verified out of band. Trust warnings (unpinned sessions, rotations) persist on screen until dismissed, as the spec's §4/§8 messages demand.
+- **Rotation** (§7): rotate with a signed certificate; contacts accept the change from the chain.
+- **Backup**: settings can export the inbox, sent mail, contacts and server pins to one shareable file (and import it back) — the same JSON shape as gsmol's export, so backups move between the two clients. It never contains the seed, which has its own reveal-and-copy flow. Import never overwrites a pin or contact that already differs locally; malformed entries are skipped and counted.
-## Screenshots
+## Layout
-
-
+```
+lib/smol/crypto.dart SHA-2, HKDF, ChaCha20-Poly1305, X25519, Ed25519, §2 conversions
+lib/smol/noise.dart Noise_NX_25519_ChaChaPoly_SHA256 initiator
+lib/smol/proto.dart addresses, seal/unseal, frontmatter, rotation, op framing
+lib/smol/transport.dart TCP byte pipe (dart:io)
+lib/smol/store.dart Hive: identity, pins, contacts, sealed mail, export/import
+lib/smol/config.dart deploy-time preset server pin (--dart-define)
+lib/smol/client.dart connect/fetch/send/register/rotate flows
+lib/presentation/ the UI (Riverpod + auto_route)
+test/smol_test.dart byte-exact vectors + protocol cases
+test/store_test.dart contact key history, import
+test/widget_test.dart UI smoke test
+test/e2e_test.dart live round-trip against smolmaild
+```
-## Installation
+The crypto is pure Dart, mirroring gsmol's dependency-free modules, so `test/vectors.json` — generated from the reference stack (PyNaCl, noiseprotocol) by `../gsmol/test/gen_vectors.py` — pins every operation byte for byte.
-To run the app locally and make modifications, follow these steps:
+## Run and verify
-1. Ensure you have Flutter SDK installed on your machine. You can download it from the official Flutter website: https://flutter.dev.
-2. Clone this repository to your local machine using the following command:
- ```bash
- git clone https://github.com/sarthakkimtani/flash-mail.git
- ```
-3. Navigate to the project directory:
- ```bash
- cd flash-mail
- ```
-4. Fetch the app's dependencies by running the following command:
- ```bash
- flutter pub get
- ```
-5. Connect your device or start an emulator.
-6. Run the app using the following command:
- ```bash
- flutter run
- ```
+```
+devbox run analyze
+devbox run test
+```
-## Dependencies
+Then `flutter run` with a device or emulator attached. `test/e2e_test.dart` additionally runs a live round-trip (register, send to self, fetch, unseal, drain) against `../smolmail/smolmaild.py` on `127.0.0.1:1961`, and self-skips when no server is listening.
-| Name | Usage |
-| ------------------------------------------------------------------------- | --------------------------- |
-| [**Auto Route**](https://pub.dev/packages/auto_route) | Navigation & Routing |
-| [**Dio**](https://pub.dev/packages/dio) | HTTP Requests |
-| [**External Path**](https://pub.dev/packages/external_path) | External Storage Path |
-| [**Flash**](https://pub.dev/packages/flash) | Alerts & Dialogs |
-| [**Flutter Downloader**](https://pub.dev/packages/flutter_downloader) | Download Files |
-| [**Flutter InAppWebView**](https://pub.dev/packages/flutter_inappwebview) | In-App WebView |
-| [**Flutter Riverpod**](https://pub.dev/packages/flutter_riverpod) | Global State Management |
-| [**Freezed**](https://pub.dev/packages/freezed) | Code Generation |
-| [**Hive**](https://pub.dev/packages/hive) | Local Database |
-| [**Intl**](https://pub.dev/packages/intl) | Internationalization |
-| [**Shimmer**](https://pub.dev/packages/shimmer) | Shimmer for Loading Screens |
-| [**URL Launcher**](https://pub.dev/packages/url_launcher) | URL Launcher |
+Not verified here: clicking through the app on a real device — the logic under every button is what the tests exercise, as with gsmol.
-## Contributing
+## Notes and limits
-Contributions to this app are welcome! If you find any issues or have ideas for improvements, please open an issue or submit a pull request. Make sure to follow the repository's guidelines for contributing.
+- No server push or notifications in v1 (SPEC.md §13): tap fetch.
+- The seed sits in app storage: a compromised device is game over, same as a stolen `identity.key` file for the CLI client.
+- Rotation is not revocation (§7): a stolen key can rotate onward and the chain validates. The settings screen surfaces rotations instead of applying them invisibly; out-of-band re-verification is the only defence.
## License
-This app is distributed under the [MIT License](https://github.com/sarthakkimtani/flash-mail/blob/main/LICENSE). Feel free to modify and use it as per your requirements.
-
-## Disclaimer
-
-FlashMail is an independent project and is not affiliated with or endorsed by the mail.tm platform. Please review and comply with the terms of service of mail.tm when using this application.
+Distributed under the MIT License; see LICENSE.md.
diff --git a/analysis_options.yaml b/analysis_options.yaml
index b07c1de..0fc3ec6 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -2,8 +2,7 @@ include: package:flutter_lints/flutter.yaml
analyzer:
exclude:
- - lib/data/models/*/*.g.dart
- - lib/data/models/*/*.freezed.dart
+ - lib/presentation/routes/app_router.gr.dart
linter:
rules:
diff --git a/android/app/build.gradle b/android/app/build.gradle
deleted file mode 100644
index a1b61ba..0000000
--- a/android/app/build.gradle
+++ /dev/null
@@ -1,88 +0,0 @@
-def localProperties = new Properties()
-def localPropertiesFile = rootProject.file('local.properties')
-if (localPropertiesFile.exists()) {
- localPropertiesFile.withReader('UTF-8') { reader ->
- localProperties.load(reader)
- }
-}
-
-def flutterRoot = localProperties.getProperty('flutter.sdk')
-if (flutterRoot == null) {
- throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
-}
-
-def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
-if (flutterVersionCode == null) {
- flutterVersionCode = '1'
-}
-
-def flutterVersionName = localProperties.getProperty('flutter.versionName')
-if (flutterVersionName == null) {
- flutterVersionName = '1.0'
-}
-
-apply plugin: 'com.android.application'
-apply plugin: 'kotlin-android'
-apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
-
-def keystoreProperties = new Properties()
-def keystorePropertiesFile = rootProject.file('key.properties')
-if (keystorePropertiesFile.exists()) {
- keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
-}
-
-android {
- namespace "com.example.flash_mail"
- compileSdkVersion 34
- ndkVersion flutter.ndkVersion
-
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
-
- kotlinOptions {
- jvmTarget = '1.8'
- }
-
- sourceSets {
- main.java.srcDirs += 'src/main/kotlin'
- }
-
- defaultConfig {
- applicationId "com.app.flash_mail"
- minSdkVersion flutter.minSdkVersion
- targetSdkVersion flutter.targetSdkVersion
- versionCode flutterVersionCode.toInteger()
- versionName flutterVersionName
-
- signingConfigs {
- release {
- keyAlias keystoreProperties['keyAlias']
- keyPassword keystoreProperties['keyPassword']
- storeFile file(keystoreProperties['storeFile'])
- storePassword keystoreProperties['storePassword']
- }
- }
-
- buildTypes {
- release {
- signingConfig signingConfigs.release
- }
- }
- }
-
- buildTypes {
- release {
- signingConfig signingConfigs.debug
- }
- }
-}
-
-flutter {
- source '../..'
-}
-
-dependencies {
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
-}
diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts
new file mode 100644
index 0000000..db7cfc9
--- /dev/null
+++ b/android/app/build.gradle.kts
@@ -0,0 +1,40 @@
+plugins {
+ id("com.android.application")
+ id("kotlin-android")
+ // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
+ id("dev.flutter.flutter-gradle-plugin")
+}
+
+android {
+ namespace = "com.example.smol_mail"
+ compileSdk = flutter.compileSdkVersion
+ ndkVersion = "27.0.12077973"
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+
+ kotlinOptions {
+ jvmTarget = JavaVersion.VERSION_11.toString()
+ }
+
+ defaultConfig {
+ applicationId = "com.app.smol_mail"
+ minSdk = flutter.minSdkVersion
+ targetSdk = flutter.targetSdkVersion
+ versionCode = flutter.versionCode
+ versionName = flutter.versionName
+ }
+
+ buildTypes {
+ release {
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig = signingConfigs.getByName("debug")
+ }
+ }
+}
+
+flutter {
+ source = "../.."
+}
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index a32e4d5..3cd8ccf 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
@@ -24,33 +23,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/android/app/src/main/kotlin/com/example/flash_mail/MainActivity.kt b/android/app/src/main/kotlin/com/example/smol_mail/MainActivity.kt
similarity index 75%
rename from android/app/src/main/kotlin/com/example/flash_mail/MainActivity.kt
rename to android/app/src/main/kotlin/com/example/smol_mail/MainActivity.kt
index 9ffc340..ba1c9bf 100644
--- a/android/app/src/main/kotlin/com/example/flash_mail/MainActivity.kt
+++ b/android/app/src/main/kotlin/com/example/smol_mail/MainActivity.kt
@@ -1,4 +1,4 @@
-package com.example.flash_mail
+package com.example.smol_mail
import io.flutter.embedding.android.FlutterActivity
diff --git a/android/build.gradle b/android/build.gradle
deleted file mode 100644
index f7eb7f6..0000000
--- a/android/build.gradle
+++ /dev/null
@@ -1,31 +0,0 @@
-buildscript {
- ext.kotlin_version = '1.7.10'
- repositories {
- google()
- mavenCentral()
- }
-
- dependencies {
- classpath 'com.android.tools.build:gradle:7.3.0'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- }
-}
-
-allprojects {
- repositories {
- google()
- mavenCentral()
- }
-}
-
-rootProject.buildDir = '../build'
-subprojects {
- project.buildDir = "${rootProject.buildDir}/${project.name}"
-}
-subprojects {
- project.evaluationDependsOn(':app')
-}
-
-tasks.register("clean", Delete) {
- delete rootProject.buildDir
-}
diff --git a/android/build.gradle.kts b/android/build.gradle.kts
new file mode 100644
index 0000000..f63265d
--- /dev/null
+++ b/android/build.gradle.kts
@@ -0,0 +1,33 @@
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+// NDK 28 (flutter.ndkVersion default) is not installed; override all library
+// modules to use the installed NDK 27.
+subprojects {
+ afterEvaluate {
+ if (plugins.hasPlugin("com.android.library")) {
+ configure {
+ ndkVersion = "27.0.12077973"
+ }
+ }
+ }
+}
+
+val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get()
+rootProject.layout.buildDirectory.value(newBuildDir)
+
+subprojects {
+ val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
+ project.layout.buildDirectory.value(newSubprojectBuildDir)
+}
+subprojects {
+ project.evaluationDependsOn(":app")
+}
+
+tasks.register("clean") {
+ delete(rootProject.layout.buildDirectory)
+}
diff --git a/android/gradle.properties b/android/gradle.properties
index 94adc3a..9b650e9 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -1,3 +1,2 @@
-org.gradle.jvmargs=-Xmx1536M
+org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
-android.enableJetifier=true
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
index 3c472b9..74b269f 100644
--- a/android/gradle/wrapper/gradle-wrapper.properties
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-all.zip
diff --git a/android/nix-android-sdk/flake.lock b/android/nix-android-sdk/flake.lock
new file mode 100644
index 0000000..23c99f2
--- /dev/null
+++ b/android/nix-android-sdk/flake.lock
@@ -0,0 +1,136 @@
+{
+ "nodes": {
+ "android-nixpkgs": {
+ "inputs": {
+ "devshell": "devshell",
+ "flake-utils": "flake-utils",
+ "nixpkgs": "nixpkgs"
+ },
+ "locked": {
+ "lastModified": 1768249546,
+ "narHash": "sha256-cpNxcSNveCBSDmocYlPrayaP8CX8ik0SKR4aNbrxOqA=",
+ "owner": "tadfisher",
+ "repo": "android-nixpkgs",
+ "rev": "001ffcb40e86888d38acf7b851f9a1bfac7d3a81",
+ "type": "github"
+ },
+ "original": {
+ "owner": "tadfisher",
+ "ref": "stable",
+ "repo": "android-nixpkgs",
+ "type": "github"
+ }
+ },
+ "devshell": {
+ "inputs": {
+ "nixpkgs": [
+ "android-nixpkgs",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1764011051,
+ "narHash": "sha256-M7SZyPZiqZUR/EiiBJnmyUbOi5oE/03tCeFrTiUZchI=",
+ "owner": "numtide",
+ "repo": "devshell",
+ "rev": "17ed8d9744ebe70424659b0ef74ad6d41fc87071",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "devshell",
+ "type": "github"
+ }
+ },
+ "flake-utils": {
+ "inputs": {
+ "systems": "systems"
+ },
+ "locked": {
+ "lastModified": 1731533236,
+ "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "flake-utils_2": {
+ "inputs": {
+ "systems": "systems_2"
+ },
+ "locked": {
+ "lastModified": 1731533236,
+ "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1768127708,
+ "narHash": "sha256-1Sm77VfZh3mU0F5OqKABNLWxOuDeHIlcFjsXeeiPazs=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "ffbc9f8cbaacfb331b6017d5a5abb21a492c9a38",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "android-nixpkgs": "android-nixpkgs",
+ "flake-utils": "flake-utils_2"
+ }
+ },
+ "systems": {
+ "locked": {
+ "lastModified": 1681028828,
+ "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+ "owner": "nix-systems",
+ "repo": "default",
+ "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default",
+ "type": "github"
+ }
+ },
+ "systems_2": {
+ "locked": {
+ "lastModified": 1681028828,
+ "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+ "owner": "nix-systems",
+ "repo": "default",
+ "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default",
+ "type": "github"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/android/nix-android-sdk/flake.nix b/android/nix-android-sdk/flake.nix
new file mode 100644
index 0000000..f7bcb08
--- /dev/null
+++ b/android/nix-android-sdk/flake.nix
@@ -0,0 +1,27 @@
+{
+ description = "A Flake for the Android SDK";
+
+ inputs = {
+ flake-utils.url = "github:numtide/flake-utils";
+ android-nixpkgs.url = "github:tadfisher/android-nixpkgs/stable";
+ };
+
+ outputs = { self, flake-utils, android-nixpkgs }:
+ flake-utils.lib.eachSystem ["x86_64-linux" "x86_64-darwin" "aarch64-darwin"] (system: {
+ packages.android-sdk = android-nixpkgs.sdk."${system}" (sdkPkgs: with sdkPkgs; [
+ cmdline-tools-latest
+ build-tools-33-0-0
+ build-tools-34-0-0
+ build-tools-35-0-0
+ build-tools-36-1-0
+ cmake-3-22-1
+ platform-tools
+ platforms-android-33
+ platforms-android-34
+ platforms-android-35
+ platforms-android-36
+ emulator
+ ndk-27-0-12077973
+ ]);
+ });
+}
diff --git a/android/settings.gradle b/android/settings.gradle
deleted file mode 100644
index 44e62bc..0000000
--- a/android/settings.gradle
+++ /dev/null
@@ -1,11 +0,0 @@
-include ':app'
-
-def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
-def properties = new Properties()
-
-assert localPropertiesFile.exists()
-localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
-
-def flutterSdkPath = properties.getProperty("flutter.sdk")
-assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
-apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts
new file mode 100644
index 0000000..43394ed
--- /dev/null
+++ b/android/settings.gradle.kts
@@ -0,0 +1,25 @@
+pluginManagement {
+ val flutterSdkPath = run {
+ val properties = java.util.Properties()
+ file("local.properties").inputStream().use { properties.load(it) }
+ val flutterSdkPath = properties.getProperty("flutter.sdk")
+ require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
+ flutterSdkPath
+ }
+
+ includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
+
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+
+plugins {
+ id("dev.flutter.flutter-plugin-loader") version "1.0.0"
+ id("com.android.application") version "8.9.1" apply false
+ id("org.jetbrains.kotlin.android") version "2.1.0" apply false
+}
+
+include(":app")
diff --git a/devbox.json b/devbox.json
new file mode 100644
index 0000000..7eb26b3
--- /dev/null
+++ b/devbox.json
@@ -0,0 +1,24 @@
+{
+ "$schema": "https://raw.githubusercontent.com/jetify-com/devbox/main/packages/devbox.schema.json",
+ "packages": [
+ "flutter@3.41.9-sdk-links",
+ "nixpkgs#android-tools",
+ "nixpkgs#openjdk17",
+ "path:./android/nix-android-sdk#android-sdk"
+ ],
+ "shell": {
+ "init_hook": [
+ "flutter config --jdk-dir \"$(dirname \"$(dirname \"$(command -v java)\")\")\" > /dev/null",
+ "flutter --version"
+ ],
+ "scripts": {
+ "analyze": "flutter analyze",
+ "test": "flutter test",
+ "link-android-tools": "if [ -n \"$ANDROID_HOME\" ] && [ ! -d \"$ANDROID_HOME/cmdline-tools/latest\" ] && [ -d \"$ANDROID_HOME/cmdline-tools/8.0\" ]; then ln -s \"$ANDROID_HOME/cmdline-tools/8.0\" \"$ANDROID_HOME/cmdline-tools/latest\"; fi"
+ }
+ },
+ "env": {
+ "ANDROID_SDK_ROOT": "$DEVBOX_PACKAGES_DIR/share/android-sdk",
+ "ANDROID_HOME": "$DEVBOX_PACKAGES_DIR/share/android-sdk"
+ }
+}
\ No newline at end of file
diff --git a/devbox.lock b/devbox.lock
new file mode 100644
index 0000000..d61a7b7
--- /dev/null
+++ b/devbox.lock
@@ -0,0 +1,65 @@
+{
+ "lockfile_version": "1",
+ "packages": {
+ "flutter@3.41.9-sdk-links": {
+ "last_modified": "2026-05-21T08:15:18Z",
+ "resolved": "github:NixOS/nixpkgs/4a29d733e8a7d5b824c3d8c958a946a9867b3eb2#flutter",
+ "source": "devbox-search",
+ "version": "3.41.9-sdk-links",
+ "systems": {
+ "aarch64-darwin": {
+ "outputs": [
+ {
+ "name": "out",
+ "path": "/nix/store/mp9x3mn0x5hs11m8z4m20w8gndkkccb7-flutter-wrapped-3.41.9-sdk-links",
+ "default": true
+ }
+ ],
+ "store_path": "/nix/store/mp9x3mn0x5hs11m8z4m20w8gndkkccb7-flutter-wrapped-3.41.9-sdk-links"
+ },
+ "aarch64-linux": {
+ "outputs": [
+ {
+ "name": "out",
+ "path": "/nix/store/aidhap5ilmmf4fzyr0gwxxls0d7b9h7z-flutter-wrapped-3.41.9-sdk-links",
+ "default": true
+ }
+ ],
+ "store_path": "/nix/store/aidhap5ilmmf4fzyr0gwxxls0d7b9h7z-flutter-wrapped-3.41.9-sdk-links"
+ },
+ "x86_64-darwin": {
+ "outputs": [
+ {
+ "name": "out",
+ "path": "/nix/store/27gbcgwqbaaf81i792zssg862xcwzwzg-flutter-wrapped-3.41.9-sdk-links",
+ "default": true
+ }
+ ],
+ "store_path": "/nix/store/27gbcgwqbaaf81i792zssg862xcwzwzg-flutter-wrapped-3.41.9-sdk-links"
+ },
+ "x86_64-linux": {
+ "outputs": [
+ {
+ "name": "out",
+ "path": "/nix/store/fqcjikcpdcn123csd805bmg05jv75szj-flutter-wrapped-3.41.9-sdk-links",
+ "default": true
+ }
+ ],
+ "store_path": "/nix/store/fqcjikcpdcn123csd805bmg05jv75szj-flutter-wrapped-3.41.9-sdk-links"
+ }
+ }
+ },
+ "github:NixOS/nixpkgs/nixpkgs-unstable": {
+ "last_modified": "2026-08-27T07:16:00Z",
+ "resolved": "github:NixOS/nixpkgs/c27cdad491a991b11ed731760aa2ef8db0cb0410?lastModified=1787814960&narHash=sha256-PYZq1qzCJXC2zGI0mH07vrZBsw6DRBAOX0jN1pPtqOQ%3D"
+ },
+ "nixpkgs#android-tools": {
+ "last_modified": "1970-01-01T00:00:00Z",
+ "resolved": "path:/nix/store/v8ynlbrldn7k8byb1kx0j0wka8dsbrw9-source?narHash=sha256-BZWCi9ZRJiARTuKTbbtvFTj7t1TK4G3UEckT3HyNfRg%3D#android-tools"
+ },
+ "nixpkgs#openjdk17": {
+ "last_modified": "1970-01-01T00:00:00Z",
+ "resolved": "path:/nix/store/v8ynlbrldn7k8byb1kx0j0wka8dsbrw9-source?narHash=sha256-BZWCi9ZRJiARTuKTbbtvFTj7t1TK4G3UEckT3HyNfRg%3D#openjdk17"
+ }
+ }
+}
diff --git a/lib/data/api/api_client.dart b/lib/data/api/api_client.dart
deleted file mode 100644
index 4bad7f6..0000000
--- a/lib/data/api/api_client.dart
+++ /dev/null
@@ -1,123 +0,0 @@
-import "dart:convert";
-
-import "package:dio/dio.dart";
-import "package:flash_mail/data/providers/providers.dart";
-import "package:flutter_riverpod/flutter_riverpod.dart";
-
-import "package:flash_mail/data/models/account/account.dart";
-import "package:flash_mail/data/models/message/message.dart";
-import "package:flash_mail/data/models/message_detail/message_detail.dart";
-import "package:flash_mail/shared/utils/random_string.dart";
-
-class ApiClient {
- final Dio dio;
- final Ref ref;
-
- ApiClient(this.dio, this.ref);
-
- Future generateToken(String address, String password) async {
- final tokenResponse = await dio.post(
- "/token",
- data: jsonEncode({"address": address, "password": password}),
- );
- final Map tokenData =
- tokenResponse.data as Map;
-
- ref.read(tokenProvider.notifier).state = tokenData["token"];
- }
-
- Future createAccount() async {
- final response = await dio.get("/domains");
- final Map data = response.data as Map;
- final List