Portable, offline TOTP authenticator that runs from a USB drive. Encrypted vault, CLI + GUI, Bitwarden import.
Repository: https://github.com/GimpyHand/simple-totp
- Encrypted vault (Argon2id + ChaCha20-Poly1305) stored at
vault/vault.enc - GUI for search, copy, and countdown timers
- CLI for scripting and headless use
- Import from Bitwarden Authenticator JSON exports
- No installation required on the USB stick — copy the folder and run
| Platform | Build from source | Run from USB |
|---|---|---|
| Linux | Rust, C compiler, X11/Wayland dev libs | Any x86_64 Linux with OpenGL |
| Windows | Rust, MSVC build tools (to compile) | Windows 10+ (double-click run-windows.bat) |
| Android | JDK 17 + Android SDK (see below) | Android 8+ — install bin/usb-otp-android.apk |
git clone https://github.com/GimpyHand/simple-totp.git
cd simple-totp
./scripts/setup.shsetup.sh makes scripts executable, configures a C compiler (gcc or Zig), and runs tests.
Debian / Ubuntu:
sudo apt install build-essential pkg-config curl \
libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev \
libxkbcommon-dev libssl-devFedora:
sudo dnf install gcc pkg-config libxcb-devel libxkbcommon-devel openssl-develArch:
sudo pacman -S base-devel pkg-config libxcb libxkbcommon opensslIf you cannot install a system compiler, the setup script can download Zig instead:
./scripts/setup-toolchain.shIf cargo is not found: https://rustup.rs
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"# Fast debug build (~30s first time, then instant)
./scripts/build-dev.sh
./target/debug/otp
# Release build (~1 min)
cargo build --release
./target/release/otpbuild-dev.sh updates target/debug/otp. build-usb.sh does not — it only builds usb-otp/usb-otp (release-small, cross-target). After a USB build, run ./usb-otp/usb-otp, not target/debug/otp.
Run without arguments to open the GUI. Pass subcommands for CLI use (init, list, code, copy, import, change-password).
cargo test./scripts/build-usb.sh
./usb-otp/usb-otpOutput folder: usb-otp/
usb-otp/
├── usb-otp # Linux binary — double-click to launch
├── run-linux.sh # optional terminal launcher
├── run-windows.bat # Windows launcher
├── bin/ # Windows binary + Android APK
│ ├── otp-windows.exe
│ └── usb-otp-android.apk
├── vault/ # encrypted data (created on first use)
└── README.txt # end-user instructions
The USB build uses the release-small profile (full LTO, ~2–3 minutes). The link step is quiet — that is normal.
sudo apt install mingw-w64
./scripts/build-usb.shOr build natively on Windows:
cargo build --profile release-small
copy target\release-small\otp.exe usb-otp\bin\otp-windows.exe./scripts/setup-android-toolchain.sh # once — downloads JDK + SDK to .toolchain/
./scripts/build-android.sh # → usb-otp/bin/usb-otp-android.apkbuild-usb.sh builds the APK automatically when the Android toolchain is present.
Android use: Install the APK on your phone. Connect the USB drive via OTG, open the app, choose the usb-otp folder, unlock with your master password. Codes are read from vault/vault.enc in memory only — unplug the drive or delete the vault and they disappear. Nothing is cached on the phone.
USB_DEST=/media/$USER/MYUSB/otp ./scripts/deploy-usb.sh
# or
./scripts/deploy-usb.sh /media/$USER/MYUSB/otpdeploy-usb.sh builds (unless SKIP_BUILD=1), copies the package, and preserves an existing vault/vault.enc.
Linux launch: open the folder in your file manager and double-click usb-otp. On first run, click Run or enable Allow executing.
Windows launch: double-click run-windows.bat.
Note:
.desktopfiles on exFAT USB drives do not work reliably on GNOME/Ubuntu. Double-click theusb-otpbinary directly instead.
See packaging/README.txt for end-user documentation shipped on the USB.
./target/release/otp init --password '...' --confirm-password '...'
./target/release/otp import bitwarden export.json --password '...'
./target/release/otp list --password '...'
./target/release/otp code "Amazon" --password '...'
./target/release/otp copy "Amazon" --password '...'
./target/release/otp change-password \
--password 'old' --new-password 'new' --confirm-new-password 'new'Import expects a Bitwarden Authenticator export (JSON with otpauth:// URIs), not a password-vault-only export.
src/
cli/ CLI commands
gui/ egui interface
vault/ encryption and entries
totp/ code generation
import/ Bitwarden JSON import
android/ Kotlin/Compose Android app (reads vault via USB OTG)
scripts/
setup.sh first-time setup after clone
build-dev.sh fast debug builds
build-usb.sh production USB package
build-android.sh Android APK for usb-otp/bin/
setup-android-toolchain.sh JDK + SDK bootstrap
deploy-usb.sh copy package to a USB path
packaging/ files copied into usb-otp/
MIT — see LICENSE.