MahaTranscribe Termux v1 — a store-first voice transcription app for Android/Termux. Flask + Waitress + SQLite, one-file installer.
# MahaTranscribe Termux
**v1** — a voice transcription app that runs on your own phone, inside Termux.
One bash file installs it. One word starts it.
```
bash 1-maha-transcribe-v1-termux.sh
termux-setup-storage
transcribe
```
The installer writes a Flask application served by Waitress, backed by SQLite,
and a single page served to the browser. The terminal it runs in becomes the
dashboard.
---
## The three rules
**Capture first, transcribe later.** A recording is written to disk and
committed to the database before transcription is even attempted. Transcription
is a separate, retryable job against a row that already exists. Pull the wifi,
close the lid, kill the process: the audio is still there and the job resumes by
itself. If the network dies mid-upload the row goes back to `queued` with a
growing backoff, never to `failed`. There is no path where audio exists only in
a browser tab.
**SQLite, not memory.** Every piece of state is a row, written with WAL enabled
so the worker thread and the request threads never block each other. Anything
left mid-flight by a crash is swept back into the queue on the next start.
**Waitress, not the Flask dev server.** Thread pooled, pure Python, no compiled
extensions, which is precisely why it was chosen over gunicorn or uWSGI: it
installs and runs unchanged inside Termux on Android.
---
## The terminal is part of the app
The server draws a live dashboard rather than a scroll of log lines, in the same
amber palette as the web UI, and every command is a single keypress.
```
Q quit O open the page again R restart in place
P pause queue K test every key L toggle activity log
C redraw
```
Nothing needs Ctrl-C. When output is not a terminal, for a background or piped
launch, it prints one plain line and serves silently instead.
---
## API keys
**This build ships with none, by design.** Keys are added from Settings and
stored in `~/.maha-transcribe/keys.json` at permissions 600. The browser is only
ever told a masked label, never a key. Per provider you get add, test, test all,
make default, delete, and check models, which asks the provider what it offers
today rather than trusting a list that quietly goes stale. Keys are tried in
order, default first, and a rejection moves to the next key while a dead network
waits instead.
You can record before adding any key. Takes land in the archive and transcribe
themselves the moment a key tests clean.
---
## Audio
Recorded at the best quality the browser will give, then reduced by ffmpeg to
what a speech model actually consumes: mono, 16 kHz, Opus at 24 kbps. 16 kHz
covers the whole frequency range of speech, and published bitrate evaluations
put the accuracy floor for speech well under 32 kbps. Measured on this build a
4.47 MB capture becomes 127 KB, 36x less to upload. Opus, then MP3, then the
original: optimisation can never fail a job.
---
## Files on disk
```
~/.maha-transcribe/server.py
~/.maha-transcribe/static/index.html
~/.maha-transcribe/keys.json chmod 600, the only place keys exist
~/.maha-transcribe/settings.json
~/.maha-transcribe/maha.db SQLite, WAL
~/.maha-transcribe/media/ original audio, plus transient *_opt.*
~/.maha-transcribe/server.log
~/storage/downloads/Maha Transcribe/ exported .txt
```
---
## Repository contents
```
1-maha-transcribe-v1-termux.sh the installer, the whole app
docs/1-maha-transcribe-v1-handoff.md every moving part, for editing without
reading the installer
```
Read the handoff before changing anything. It documents the design rules, the
`server.py` and `index.html` maps, the invariants that must not break, the known
quirks, and the test evidence for this build.
---
Marko Boško · Mantra Productions