Haven Docs

Tech stack

Platform#

LanguageKotlin (JVM target 17)
Minimum Android8.0, API 26
Target / compile AndroidAPI 36
UIJetpack Compose with Material 3
BuildGradle Kotlin DSL, version catalog, KSP
ModulesOne: :app

The API 26 floor is what the platform APIs Haven relies on require — the JCA signature provider used to verify blocklist updates, and the notification-channel model for the ongoing protection notification.

Libraries, and why each one is present#

AreaLibraryWhy
UICompose, Material 3, Navigation ComposeThe whole interface, including Sam, who is drawn on a canvas rather than shipped as assets
DIHilt (+ Hilt Navigation Compose, Hilt Work)One object graph shared by activities, services, receivers and workers
PersistenceRoomThe block rules, block log, accountability events, financial pause log and learned sites
PreferencesDataStore (Preferences)Fourteen small, separate settings stores
AsyncKotlin Coroutines + FlowReactive settings caching, the DNS loop, background writes
Serializationkotlinx.serializationBlocklist payloads, API bodies, and the JSON blobs stored in DataStore
Background workWorkManagerThe periodic blocklist sync and the widget refresh
NetworkingRetrofit + OkHttp + the kotlinx-serialization converterThe blocklist API and the Supabase APIs
Auth (device)AndroidX Credentials + Google IdentityGoogle sign-in via Credential Manager
LockAndroidX Biometric + FragmentOptional biometric acceleration of the PIN gate
BillingRevenueCat (purchases + purchases-ui)Subscriptions, the paywall and the customer center
WidgetJetpack GlanceThe Sam widget, rendered to RemoteViews
TestingJUnit, MockK, coroutines-test, Turbine70 unit-test files, concentrated on domain/

Deliberately absent: any analytics, crash-reporting, advertising, attribution or remote-config SDK, and any push messaging library. See Data handling.

Cryptography#

All from the platform, with no third-party crypto library:

  • Blocklist authenticity — ECDSA P-256 with SHA-256, verified against a public key pinned in the app. Rotating the server key requires shipping a new app version; that is the intended trust boundary.
  • PIN and password storage — salted PBKDF2. Raw secrets are never persisted.
  • Pairing codesSecureRandom.
  • Google ID tokens — verified on-device against Google's published keys, checking signature, audience, issuer and expiry before the token is trusted.

Build configuration#

Configuration that differs per environment is injected at build time from a file that is kept out of version control. The app degrades gracefully when a value is absent rather than failing at runtime:

AbsentBehaviour
Google sign-in configurationThe Google button reports itself as not configured
Backend account configurationAccounts fall back to local-only accounts
Billing configurationThe app falls back to a local mock paywall

No key material of any kind is present in this documentation, in the repository's tracked files, or in the published site.

Release hardening#

  • minifyEnabled and shrinkResources on for release.
  • allowBackup="false" — the app's data is not included in Android backup.
  • All cleartext traffic denied in the release network-security configuration, with no exceptions. The local-development allowance exists only in the debug variant's own configuration file and is never packaged into a release build.
  • HTTP logging is off in release builds.
  • A build-time guard fails a release build if a test-store billing key is configured, so a shipped app can never bill against a test store.
  • The release build is signed from credentials that live outside version control; without them the build simply stays unsigned, so the project still builds for anyone without the key.

Language handling#

Haven picks its own interface language, independent of the phone's. That has one non-obvious build consequence: Play splits an app bundle per language and installs only the splits matching the device's locales, so a language chosen in-app would silently render in the wrong language on a phone set to something else. Language splitting is therefore disabled and every language ships in the base module.

Strings that are rendered outside any of Haven's own activities — the device-admin dialogs, the widget, and partner emails composed from a background component — explicitly apply the app's chosen language, because those contexts would otherwise follow the phone.