Download
Español
Add to Chrome
SECURITY
What protects your secrets, and what doesn't.
2FAu is a local-first authenticator: there is no account, no server and no sync target you don't run yourself. This page describes the vault format, the key handling, and the attacks it deliberately does not defend against.
VAULT VERSION
1
KDF ID
1
LICENCE
MIT
VAULT FORMAT
One sealed blob on disk.
The whole vault — accounts, tombstones and revision metadata — is a single JSON document, sealed as one AES-256-GCM ciphertext and rewritten on every mutation.
2FAU
magic · 4 B
01
ver · 1 B
01
kdf · 1 B
salt
16 B
nonce
12 B
34-byte header · bound as associated data
ciphertext
n B + 16 B GCM tag
JSON VaultDocument · encrypted
Key derivation
PBKDF2-HMAC-SHA256
Iterations
600,000
Derived key
32 bytes
Cipher
AES-256-GCM
Salt
16 bytes, per vault
Nonce
12 bytes, per write
Authenticated data
full 34-byte header
Key material
zeroized on drop
Why PBKDF2 and not Argon2id?
Argon2id is the stronger choice on paper, but it needs threads and a large memory budget. 2FAu runs the same Rust core natively and as WebAssembly in a browser tab, where neither is reliably available. PBKDF2-HMAC-SHA256 behaves identically on all four targets.
The kdf_id byte is versioned, so Argon2id can be added later without invalidating a single existing vault.
THREAT MODEL
Local-first moves the risk. It doesn't delete it.
Removing the server removes a whole class of attack and hands you a different one: your device is now the only thing standing between an attacker and your secrets.
Protected against
A stolen vault file
vault.dat on its own is inert. Change the version, kdf id, salt or nonce and the GCM tag check fails before a single byte is decrypted.
A server breach
There is no server, no account and no cloud copy. There is nothing to breach on our side because there is no our side.
Network observation
Codes are derived on the device from a local secret. Generating one sends nothing anywhere.
The interface itself
Secrets never reach the UI model. The frontend asks for a code by account id and gets six digits back.
A stale device
Merge is newest-wins with tombstones, so an old device syncing late can never resurrect an account you deleted.
Not protected against
Malware running as you
Anything with your user privileges can read vault.dat, and once the vault is unlocked, the decrypted document in memory.
A weak passphrase
600,000 PBKDF2 iterations make an offline guess expensive. They do not rescue a passphrase that was already guessable.
Remembering the passphrase
Enabling "remember" stores it in the OS keyring. From then on your vault is exactly as strong as that keyring.
Unsigned installers
Windows and macOS builds are not code-signed yet. Verify checksums, or build from source.
The issuer's copy
TOTP is a shared secret by design. Whoever issued the seed can still generate the same codes you can.
KEYS & UNLOCK
The key exists only while you're unlocked.
There is no Secure Enclave equivalent across four platforms, so the vault is passphrase-derived. The key is never written to disk — only the salt it was derived from.
01
Unlock
You type the passphrase, or the OS keyring hands it over on a device where you chose to remember it. Nothing leaves the machine either way.
02
Derive
PBKDF2-HMAC-SHA256 runs 600,000 iterations over the vault's own 16-byte salt and produces a 32-byte AES key.
03
Hold
The key lives in memory behind a lock and is zeroized on drop. An idle watchdog locks the vault after a timeout you set.
VAULT PATH
app_data_dir()/vault.dat
KEYRING SERVICE
dev.artkost.2fau
KEYRING USER
vault-passphrase
REPORTING
Found something? Tell us before you tell the internet.
Report vulnerabilities privately through a GitHub security advisory. We aim to acknowledge within 72 hours and to ship a fix before any public disclosure. The code is MIT licensed — read it, audit it, and open an issue if the implementation disagrees with this page.
IN SCOPE
The vault format, KDF and sealing in twofau-core
The desktop shell, keyring handling and IPC surface
The browser extension and its WASM boundary
The localhost sync bridge and merge semantics
Out of scope: attacks that require an already-compromised device, and the absence of code signing, which is tracked openly.
© 2026 2FAu · MIT licensed
How it works
Security
Docs
GitHub
Built with Rust and Tauri 2