A terminal 2FA authenticator. Generates TOTP, HOTP and Steam Guard codes from an encrypted local vault, and imports existing tokens from the apps you already use.
2fatui 3 entries
> GitHub (octocat) 824 916 12s
Google (alice@gmail.com) 109 355 12s
Steam (gamer) FR8RV 12s
enter/c copy code • a add • i import • / filter • ? help • q quit
-
Live codes — TOTP (SHA1/SHA256/SHA512, 6–8 digits, any period), HOTP counters, and Steam Guard's 5-character codes, with per-second countdowns.
-
Encrypted vault — Argon2id key derivation + XChaCha20-Poly1305, atomic writes with a
.bakof the previous state. Nothing is stored in plain text. -
Import from other apps:
Source What to provide otpauth:// URIs paste one, or a file with one URI per line QR code images PNG/JPG screenshot or photo of any setup QR Google Authenticator the "Transfer accounts" QR (image or otpauth-migration://URI)Bitwarden / Vaultwarden unencrypted .jsonexportAegis plain or password-encrypted JSON export 2FAS plain or password-encrypted .2fasbackup1Password .1puxexportMicrosoft Authenticator has no export; add those entries by scanning each site's setup QR (screenshot → QR import) or pasting the setup key.
-
Export — write all entries as an otpauth:// URI file, or show any entry as a QR code right in the terminal for scanning into another app.
-
Clipboard — copies via the native clipboard when available (
xclip/xsel/wl-copy/pbcopy), falling back to OSC52 so copying works over SSH too.
Requires Go 1.25 or newer. The Bubble Tea v2 and golang.org/x/crypto
dependencies each declare go 1.25.0, and the 2FAS importer uses the
stdlib crypto/pbkdf2 added in Go 1.24. On Go 1.21+ the toolchain
downloads what it needs automatically; older versions fail at go.mod
parsing with invalid go version '1.25.0'.
go version # want go1.25 or newerInstalling a current Go on Linux (distro packages are often years behind)
curl -LO https://go.dev/dl/go1.25.12.linux-amd64.tar.gz
sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.25.12.linux-amd64.tar.gz
export PATH=/usr/local/go/bin:$PATH # add to ~/.profile to persistgo install github.com/zorenkonte/2fatui/cmd/2fatui@latestOr from a checkout:
go build -o 2fatui ./cmd/2fatui2fatui # vault at $XDG_CONFIG_HOME/2fatui/vault.enc
2fatui --vault /path/x.enc # explicit vault location
TWOFATUI_VAULT=/path/x.enc 2fatuiFirst run asks you to choose a master password and creates the vault.
| Key | Action |
|---|---|
↑/↓ j/k |
move |
/ |
filter entries |
enter / c |
copy current code |
a / e / d |
add / edit / delete entry |
r |
generate next HOTP code (bumps counter) |
i |
import wizard |
x |
export all entries as otpauth URIs |
Q |
show selected entry as a QR code |
p |
change master password |
? |
toggle full help |
q / ctrl+c |
quit |
Press i, pick a source, then give it a file path — or paste
otpauth:// / otpauth-migration:// URIs directly. Any source also
accepts a PNG/JPG containing a QR code. Encrypted Aegis/2FAS backups
prompt for their export password. Before anything is written you get a
preview where duplicates (same secret + labels) are pre-deselected.
- Google Authenticator: Settings → Transfer accounts → Export
accounts, then screenshot the QR and import the image (or decode it to
an
otpauth-migration://URI and paste that). - Bitwarden: Settings → Vault → Export vault →
.json(unencrypted). Password-protected Bitwarden exports are not supported — re-export unencrypted and delete the file afterwards. - Aegis: Settings → Import & Export → Export. Both the encrypted vault export and the plain JSON export work.
- 2FAS: Settings → 2FAS Backup → Export to file. Works with or without the backup password.
- 1Password: File → Export → 1Password Export Format (
.1pux).
- The vault is encrypted with a key derived from your master password (Argon2id, 64 MiB / 3 iterations) using XChaCha20-Poly1305; the KDF parameters are authenticated so they can't be silently downgraded.
- Exported URI files and QR codes contain your secrets in plain text — treat them like passwords and delete them after use.
- Secrets live in process memory while the app runs; memory is scrubbed best-effort on exit.
go build ./... && go vet ./... && go test ./...The importers are tested against fixtures for each format; OTP generation is pinned to RFC 6238/4226 test vectors and Steam vectors from KeePassXC and steamguard-cli.