ATHELOS
Settings

Two-Factor Authentication

TOTP enrollment, recovery codes, and login flow.

Two-Factor Authentication

ATHELOS supports TOTP as a second factor. Once enabled, login requires both your password and a 6-digit code from your authenticator app.

What TOTP is

TOTP (Time-based One-Time Password) is a standard defined by RFC 6238. It generates a 6-digit code every 30 seconds, derived from a shared secret and the current time.

Your authenticator app and ATHELOS both compute the same code from the same secret. No network call — the code is derived locally.

Any TOTP-compatible app works:

  • Aegis Authenticator (Android, open source)
  • Raivo OTP (iOS, open source)
  • 1Password (paid, includes TOTP)
  • Bitwarden (paid, includes TOTP)
  • Google Authenticator (works, minimal)
  • Authy (works, has cloud sync — less private)

For a self-hosted, privacy-first setup, Aegis or Raivo are the best choices.

Enrolling TOTP

Go to Settings → Security. Find the Two-factor authentication card.

If TOTP is not enabled, you see a Set up 2FA button. Click it.

The setup wizard shows:

  1. QR code — scan with your authenticator app
  2. Manual entry key — a long string of characters, for manual setup
  3. Verification field — where you enter the first code

Step 1 — Scan the QR code

Open your authenticator app. Choose Add account or Scan QR code. Point your phone at the QR.

The app adds an entry labeled "ATHELOS" with your account email.

Step 2 — Or enter manually

If you can't scan, use the manual entry key. Paste it into the app's "add manually" flow. Choose TOTP as the type, and SHA-1 as the algorithm. 6 digits, 30-second period.

Step 3 — Verify

Your app generates a 6-digit code. Enter it into the verification field in ATHELOS.

Click Verify and enable.

If the code is correct, TOTP is enabled. You're shown 10 recovery codes.

Recovery codes

Recovery codes are single-use backup codes for when you don't have your authenticator. Each code works once.

They're shown only once. Copy them immediately.

Options:

  • Copy individually — click the copy icon next to each code
  • Copy all — one click, all ten codes as text
  • Download as file — saves athelos-recovery-codes.txt

Store them:

  • In your password manager as a secure note
  • Printed on paper, in a safe
  • Both, for redundancy

Do not store them in a plain file on the same machine, or in an email draft, or in a note-taking app.

Login flow with TOTP enabled

When you sign in:

  1. Enter email and password — normal
  2. If the password is correct, a second prompt appears
  3. Enter the 6-digit code from your authenticator
  4. Or, click Use recovery code and enter a recovery code

If the password is wrong, you never see the second prompt. This prevents information leakage about whether an account exists.

Using a recovery code

On the TOTP prompt, click Use recovery code.

Enter one of your ten recovery codes. Each code is single-use — after use, it's marked consumed.

The system tells you how many unused codes remain. If you're running low, generate new ones from Settings.

Regenerating recovery codes

From Settings → Security, click Regenerate recovery codes.

You'll be asked for your password. Enter it and confirm.

Ten new codes are generated. The old codes stop working immediately.

Use this when:

  • You've used several codes and want to refresh
  • You suspect your old codes were compromised
  • You want to rotate them on a schedule

Disabling TOTP

From Settings → Security, click Disable 2FA.

You'll be asked for your password. Enter it and confirm.

TOTP is disabled immediately. Your recovery codes are invalidated. Your next login is password-only.

Disable only if you're switching to WebAuthn, or if you've lost your authenticator and recovery codes and have no other way in.

Combining with WebAuthn

TOTP and WebAuthn can both be enabled. When both are on, ATHELOS offers either as a second factor at login:

  • Enter a TOTP code
  • Or use a registered WebAuthn device (YubiKey, Touch ID)

The user picks. Both are valid second factors.

See WebAuthn for details.

Behind the scenes

The TOTP secret is encrypted at rest with AES-256-GCM. The encryption key is TOTP_ENCRYPTION_KEY, set in apps/web/.env.local.

This key is write-once. If you change it after enrollment, existing secrets can't be decrypted and TOTP stops working. Back it up to your password manager along with the other secrets.

See Reference: environment variables for the full list.

Security properties

TOTP has these properties:

  • Works offline — no network required
  • Single-use codes — each code expires in 30 seconds
  • Resistant to replay — a used code can't be reused
  • Not phishing-resistant — you can be tricked into entering the code on a fake site

The last point is why WebAuthn exists. If phishing resistance matters, use WebAuthn.

Common issues

Codes always rejected

Check the clock on your machine. TOTP depends on accurate time. If your clock is off by more than 30 seconds, codes fail. Enable NTP sync.

sudo systemctl status systemd-timesyncd

Lost the QR code before scanning

Cancel enrollment, then start over. A new secret is generated.

Lost the authenticator, still have recovery codes

Sign in with a recovery code. Then either re-enroll TOTP (with a new device) or disable it.

Lost both authenticator and recovery codes

See Troubleshooting: common issues for recovery via direct database access.

"Too many attempts" during verification

Wait 15 minutes. Login attempts are rate-limited.

Where to go next