Native Android app for managing Fastmail masked emails. Create, view, edit, and delete masked email addresses with Material 3 design.
  • Kotlin 99.6%
  • Python 0.3%
  • Shell 0.1%
Find a file
Paweł Orzech 60b798c251
Some checks failed
Build / build (push) Has been cancelled
commit
2026-09-05 01:37:51 +02:00
.claude/commands chore: ignore .DS_Store and untrack existing ones 2026-04-27 10:00:13 +02:00
.github Update: 1.12.0 — new icon, Masked Email API contract, Billing 8 and update fixes 2026-08-26 23:06:42 +02:00
app commit 2026-09-05 01:37:51 +02:00
docs Update: 1.12.0 — new icon, Masked Email API contract, Billing 8 and update fixes 2026-08-26 23:06:42 +02:00
gradle/wrapper Update: 1.12.0 — new icon, Masked Email API contract, Billing 8 and update fixes 2026-08-26 23:06:42 +02:00
marketing Update: 1.12.0 — new icon, Masked Email API contract, Billing 8 and update fixes 2026-08-26 23:06:42 +02:00
Plans Update: 1.12.0 — new icon, Masked Email API contract, Billing 8 and update fixes 2026-08-26 23:06:42 +02:00
.gitattributes Initial commit 2026-01-31 01:15:54 +01:00
.gitignore Update: 1.12.0 — new icon, Masked Email API contract, Billing 8 and update fixes 2026-08-26 23:06:42 +02:00
AGENTS.md Align docs with consolidated main 2026-08-02 18:44:51 +02:00
AUDIT_REPORT.md docs: update audit reports, UX recommendations, and changelog for v1.10.2 2026-08-09 13:34:12 +02:00
build.gradle.kts Chore(deps): bump com.google.gms.google-services from 4.4.2 to 4.5.0 (#48) 2026-08-04 14:58:27 +01:00
CHANGELOG.md Update: 1.12.0 — new icon, Masked Email API contract, Billing 8 and update fixes 2026-08-26 23:06:42 +02:00
CHANGELOG_AGENT.md docs: update audit reports, UX recommendations, and changelog for v1.10.2 2026-08-09 13:34:12 +02:00
CLAUDE.md Align docs with consolidated main 2026-08-02 18:44:51 +02:00
CONTRIBUTING.md Add templates, splash screen, and UI enhancements 2026-01-31 02:04:54 +01:00
gradle.properties Fix: stop pinning one laptop's JDK path in a public repo 2026-07-27 13:43:55 +02:00
gradlew Chore(deps): bump gradle-wrapper from 8.11.1 to 9.6.1 (#47) 2026-08-04 14:58:31 +01:00
gradlew.bat Chore(deps): bump gradle-wrapper from 8.11.1 to 9.6.1 (#47) 2026-08-04 14:58:31 +01:00
IMPROVEMENTS_REPORT.md Update: audit pass — detail screen stale-while-revalidate, list recomposition fixes, demo bugs 2026-08-24 00:34:40 +02:00
ISA.md Align docs with consolidated main 2026-08-02 18:44:51 +02:00
LICENSE Initial commit 2026-01-31 01:15:54 +01:00
README.md Update: 1.12.0 — new icon, Masked Email API contract, Billing 8 and update fixes 2026-08-26 23:06:42 +02:00
SECURITY.md Release v1.5: Security hardening 2026-04-27 09:40:55 +02:00
settings.gradle.kts Initial commit 2026-01-31 01:15:54 +01:00
UX_RECOMMENDATIONS.md docs: update audit reports, UX recommendations, and changelog for v1.10.2 2026-08-09 13:34:12 +02:00

FastMask app icon

FastMask

A privacy-first Android client for Fastmail Masked Email.
Create, find, edit, pause, archive, and restore masked addresses without opening Fastmail in a browser.

Build status Latest packaged release MIT License Android 8.0 or newer Kotlin 2.2.20

Features · Getting started · Privacy · Build · Architecture

Note

FastMask is an independent, open-source project. It is not affiliated with or endorsed by Fastmail.

What it does

FastMask talks directly to Fastmail's JMAP API using a token limited to the Masked Email permission. The current source tree builds app version 1.12.0 (versionCode 25); downloadable GitHub releases may trail the source.

Core features

  • Browse masks in a searchable list, sorted by latest activity.
  • Filter by All, Active, Off, or Archived, with live counts.
  • Create a random address or choose an optional prefix, site/domain, note, URL, and initial state.
  • Copy an address from the list or detail screen.
  • Edit metadata and switch a mask between Active and Off.
  • Archive a mask so new mail bounces, then restore it with Undo.
  • Keep reading the last successful, encrypted account snapshot while offline.
  • Share a link or text to FastMask to open a pre-filled creation form.
  • Create a mask from a Quick Settings tile or long-press launcher shortcut; the address is copied to the clipboard and the confirmation notification offers Undo.
  • Explore the complete UI in a local demo mode without a Fastmail token. Demo changes are never saved.
  • Use the app in 20 languages, including RTL support, with an in-app language picker.
  • Follow the system light/dark theme with an accessible warm-ink design system.

FastMask Pro

FastMask Pro is an optional one-time Google Play purchase. Existing core functionality stays free; Pro adds:

  • five accent themes: Amber, Ink, Sage, Plum, and Cobalt;
  • an optional biometric/device-credential app lock;
  • CSV export of all masks through Android's system share sheet.

The Pro surface can be disabled at build time with the monetization kill switch. A signed release build also requires a Play licensing public key so purchase signatures cannot be accepted without verification.

Getting started

Requirements

  • Android 8.0 (API 26) or newer;
  • a Fastmail account with access to Masked Email;
  • a Fastmail API token with the Masked Email permission.

Create a Fastmail API token

  1. Sign in to Fastmail on the web.
  2. Open Settings → Privacy & Security.
  3. In Connected apps & API tokens, choose Manage API tokens.
  4. Create a new token and select only Masked Email.
  5. Copy the token before closing the Fastmail dialog.

FastMask's sign-in screen includes the same walkthrough, a direct link to Fastmail's token settings, and a paste action. Tokens without the required scope are rejected with a specific error.

Install an APK

  1. Open GitHub Releases.
  2. Download the APK attached to the release.
  3. Allow installation from that source if Android asks.
  4. Install FastMask and paste the API token.

The packaged release can lag behind the version on main. To run the current source, build a debug APK locally.

Privacy and security

FastMask is designed around a narrow data path:

FastMask on your device ── JMAP over HTTPS ── Fastmail
          │
          └── optional crash diagnostics ── Firebase Crashlytics
  • No intermediary backend: mask operations go directly to Fastmail.
  • Encrypted local secrets: the API token uses EncryptedSharedPreferences; the offline snapshot uses EncryptedFile. Both are backed by Android Keystore.
  • Account-scoped offline data: cached masks are bound to the account and removed on sign-out.
  • No cloud backup: Android backup and device-transfer backup are disabled for app data.
  • Hardened transport: cleartext traffic is disabled, Fastmail API traffic trusts system certificate authorities only, and server-provided JMAP URLs are restricted to Fastmail hosts before receiving the token.
  • Protected release UI: release builds block screenshots, screen recording, Recents previews, and obscured-touch interactions.
  • Sensitive clipboard data: Android 13+ is told that copied masked addresses are sensitive.
  • No ads or behavioural analytics: there is no analytics SDK or screen/event tracking in production.
  • Optional crash reports: configured release builds can send technical crash diagnostics to Firebase Crashlytics. Collection is on by default, can be disabled in Settings → Crash reports, never runs in debug builds, and the app has no API for attaching masks, descriptions, domains, email addresses, or tokens to reports.
  • Explicit plaintext export: CSV export is a Pro action with a privacy confirmation. Exports live in app cache, are shared with a one-time URI grant, and are cleaned up after one hour.

See the full privacy policy and security policy. Please report vulnerabilities privately using the process in SECURITY.md, not a public issue.

Building the project

Prerequisites

  • Android Studio with Android SDK 36;
  • JDK 17–21. Android Studio's bundled JBR is suitable;
  • no Firebase project for a normal local or CI build.

The project supports JDK 17–21; in particular, the Gradle 8.11.1 wrapper does not run on Java 26. If your system Java is 22 or newer, point JAVA_HOME at a supported JDK or put org.gradle.java.home in your personal ~/.gradle/gradle.properties. Never commit a machine-specific JDK path.

git clone https://github.com/pawelorzech/FastMask.git
cd FastMask

# Fast pre-commit gate
./gradlew testDebugUnitTest lintDebug

# Debug APK
./gradlew assembleDebug

# Minified, unsigned release smoke build
./gradlew assembleRelease

APK outputs are written to app/build/outputs/apk/.

app/google-services.json is intentionally absent from the repository. When it is missing, the Firebase Gradle plugins are skipped and crash reporting is inert; the rest of the app builds and runs normally. Maintainers can add their own Firebase configuration outside version control for instrumented release builds.

Tests

# JVM unit tests
./gradlew testDebugUnitTest

# Android lint
./gradlew lintDebug

# Instrumented tests on a booted emulator/device
./gradlew connectedDebugAndroidTest

# Full Gradle test lifecycle
./gradlew test

CI runs unit tests, lint, and a minified release build for every pull request. The test suite covers JMAP mapping, authentication and scope validation, repositories, offline encryption flows, share routing, Quick Mask and Undo policies, billing verification, CSV hardening, translations, privacy boundaries, ViewModels, accessibility semantics, and end-to-end Compose flows.

Signed release configuration

Release signing material belongs outside the repository. FastMask reads it from environment variables or personal Gradle properties:

Purpose Environment variable Gradle property
Keystore path FASTMASK_KEYSTORE fastmask.keystore
Store password FASTMASK_STORE_PWD fastmask.storePassword
Key alias FASTMASK_KEY_ALIAS fastmask.keyAlias
Key password FASTMASK_KEY_PWD fastmask.keyPassword
Play licensing public key FASTMASK_PLAY_LICENSE_KEY fastmask.playLicenseKey

A release without signing configuration is left unsigned for external signing. A signed assembleRelease or bundleRelease fails early when the Play licensing key is missing.

Architecture

FastMask is a single-module Android application using layered Clean Architecture and MVVM:

app/src/main/java/com/fastmask/
├── data/       JMAP/Retrofit API, encrypted storage, billing, crash reporting,
│               demo data, and repository implementations
├── domain/     models, repository contracts, use cases, share/crash policies
├── ui/         Compose screens, navigation, ViewModels, theme, accessibility
├── quickmask/  Quick Settings tile, launcher shortcut, notifications, Undo
└── di/         Hilt dependency graph and dispatcher bindings
Area Implementation
Language/toolchain Kotlin 2.2.20, Java 17 bytecode, Gradle 8.11.1, AGP 8.10.1
UI Jetpack Compose, Material 3 primitives, adaptive layouts, custom design system
State/navigation ViewModels, Kotlin Coroutines and Flow, Navigation Compose
Dependency injection Hilt 2.58
Networking Retrofit 3, OkHttp 4, Kotlinx Serialization, Fastmail JMAP
Persistence AndroidX Security Crypto, DataStore Preferences
Monetization Google Play Billing 8.3.0 with RSA purchase verification
Diagnostics Optional Firebase Crashlytics; Firebase Analytics is not included

The server is the source of truth. Local storage is limited to the API token, preferences, verified Pro entitlement, temporary exports, and the encrypted last-known-good mask snapshot.

Contributing

Contributions are welcome. Before opening a pull request:

  1. Read CONTRIBUTING.md.
  2. Branch from main.
  3. Keep user-facing text localized across all supported languages.
  4. Run ./gradlew testDebugUnitTest lintDebug.
  5. Run instrumented tests when changing navigation, Compose semantics, storage encryption, or device integrations.

For user-visible changes, include screenshots or a short recording and explain what was tested on a real device or emulator.

Project documentation

License

FastMask is available under the MIT License.


Built with Kotlin, Jetpack Compose, and the Fastmail JMAP API.