CallVault โ a project by Ahsan Mahmood
Offline-first Android app that records your own calls and backs them up to storage and a database you control.
I did not build a way around Android's call-recording limits.
Android closed that door to ordinary applications after Android 10, and pretending otherwise produces software that records silence and a user who finds out afterwards. So CallVault treats capture as an abstraction with two honest strategies behind it instead.
A RecordingSource interface, two implementations.
One is a microphone foreground service that needs no root. The other is a folder watcher that ingests an external recorder's output on a rooted device. Which one you get depends on the phone in your hand rather than on a promise made on a page.
Neither of them is silent, and that is deliberate.
Recording runs behind a live banner and a persistent notification, so the person holding the phone can see it happening. An application that hides a recording is a different product with a different purpose, and this one is for recording your own calls.
The library is offline-first, and SQLite is the source of truth.
A Drift database on the device holds the record of what exists. Sync is a separate, idempotent, multi-stage state machine with retry and backoff, which means a failed upload is a state to resume from rather than a row that quietly vanishes.
That distinction is the whole reliability story.
A design where the server is the source of truth and the device holds a cache is a design where a bad afternoon on a train costs you a recording. Turning it round costs storage on the handset and buys a system that cannot lose something it already has.
Where the audio goes is worth reading twice.
Recordings upload to self-hosted FilesHub storage. Only metadata syncs to your own Supabase project, behind Postgres row-level security. There is no third-party analytics in the application at all.
The server is yours.
That is more setup than signing into somebody's account, and it is the only version of this that is defensible for a recording of a phone call, because the alternative is a private conversation sitting on infrastructure neither party to it has ever heard of.
Around that sit the ordinary things a library needs.
Playback with seek, skip and speed. Search across the number, the contact and your own notes, with a stats dashboard over the top. Notes are searchable because a call is usually easier to find by what was said in it than by the number that made it. An optional biometric lock on the application, and a Wi-Fi-only control for uploads.
The admin dashboard is the same codebase compiled for the web, which is the reason there is one at all.
The Flutter side is Riverpod and go_router with Material 3 on top, freezed for the models and just_audio for playback, with GitHub Actions doing the builds. One codebase, two outputs.
What it cost is distribution.
It is sideloaded by design, because the Play Store does not permit silent call recorders, so this is an application somebody installs deliberately rather than one they come across. It is Android-only. It is in development rather than finished, and saying so is cheaper than having it discovered.
That last sentence is on the page for the same reason the banner is on the screen.
It is at callvault.aoneahsan.com.
Whether recording a particular call is lawful where you are standing is a question about your jurisdiction and not about this software, and no application is able to answer it for you.
What it does, and what that costs to build
- Transparent call recording with a live banner and persistent notification
- Pluggable capture engine (no-root mic service or rooted folder watcher)
- Offline-first library where SQLite is the source of truth
- Idempotent multi-stage sync pipeline with retry and backoff
- Your-server-only storage: audio to FilesHub, metadata to your Supabase
- Audio playback with seek, skip, and speed controls
- Search by number, contact, or notes plus a stats dashboard
- Optional biometric app lock and Wi-Fi-only upload controls
Built with
- Flutter
- Dart
- Riverpod v2
- go_router
- Drift (SQLite)
- Kotlin
- Supabase
- FilesHub
- Material 3
- freezed
- just_audio
- GitHub Actions
Worth knowing
- call-recording
- android
- flutter
- offline-first
- supabase
- self-hosted
- privacy
- riverpod
Who built CallVault?
Ahsan Mahmood built it, in Flutter and Dart with a Kotlin capture engine underneath, and it is at callvault.aoneahsan.com. Riverpod holds the state, go_router the navigation, Drift the on-device database, and the same codebase compiles to a Flutter Web admin dashboard so there is no second application to keep in step. It is in development rather than finished, and it is Android-only. The person who wrote the capture engine is the person who wrote the sync pipeline it feeds.
Does it record without the caller knowing?
It does not record without YOU knowing: a recording runs behind a live banner and a persistent notification, which is a deliberate design decision rather than a platform requirement somebody worked around. What the person on the other end of the call sees is not something the application controls, and nothing here claims to have told them. An application that hides a recording is a different product with a different purpose, and this one exists to record your own calls. Whether a given recording is lawful where you are is a question about your jurisdiction rather than about the software, and no application can answer it on your behalf.
Where do the recordings actually go?
To your own servers, and nowhere else. Audio uploads to self-hosted FilesHub storage, while only metadata syncs to your own Supabase project behind Postgres row-level security, so the recording and the record of it live in two places you control. There is no third-party analytics in the application at all. That arrangement costs more to set up than a vendor account would, and it is the only arrangement that is defensible for a recording of a phone call.
What happens when an upload fails?
Nothing is lost, because SQLite on the device is the source of truth rather than a cache of the server. Sync is a separate idempotent multi-stage state machine with retry and backoff, so a failed upload is a state to resume from rather than a row that quietly disappears. A Wi-Fi-only setting exists for anyone who would rather uploads waited for a network they are not paying for by the megabyte. Being offline-first here is a data-integrity decision more than a convenience.
Does CallVault work on iPhone?
No. It is Android-only, and the capture engine is Kotlin sitting on Android APIs, so there is nothing here that could be pointed at another platform without being rewritten. There is no iOS release behind any of my work in any case, because there is no Apple Developer account here and nothing I build has shipped to the App Store. It is also sideloaded by design rather than installed from a store, because the Play Store does not permit silent call recorders.
https://aoneahsan.com/projects/com.aoneahsan.callvault