---
title: Account security
description: Lock down your Half Off Dev dashboard with two-factor sign-in, recovery codes and session control, and see exactly what data is stored.
---

Two things guard your account. Your email and password open the dashboard, where your balance, keys and logs live. Your API keys let code spend that balance. This page covers the first and points to the habits that protect the second. Unless noted, every setting below is in [Settings](/app/settings).

## Two-factor authentication {#two-factor-authentication}

With two-factor authentication (2FA) turned on, signing in also asks for a 6-digit code from an authenticator app. Any app that generates time-based codes (TOTP) works: Google Authenticator, 1Password, Authy and others.

:::steps
### Start setup {#start-setup}

Under **Two-factor authentication** in [Settings](/app/settings), press **Set up two-factor**.

### Add Half Off Dev to your authenticator {#add-to-your-app}

Scan the QR code, or type the setup key printed beside it into the app.

### Prove it works {#confirm-a-code}

Type the 6-digit code from the app and press **Verify & enable**. The status switches to **On**.

### Store the recovery codes {#save-recovery-codes}

You receive ten one-time recovery codes that look like `k7mq-2xpd`. Each can stand in for an authenticator code once. They are never shown again: press **Copy codes**, put them in your password manager, then press **I saved them**.
:::

After that, signing in takes your email, your password and a fresh code.

> [!WARNING] Without codes you're locked out
> If you lose your authenticator and have no recovery code left, you can't get into the dashboard. Keep the codes somewhere other than the phone that runs the app.

Codes keep failing? Make sure your phone sets its clock automatically. A code is accepted roughly 30 seconds before or after its window.

### Sign in with a recovery code {#recovery-code-sign-in}

When the code prompt appears and your phone is gone, enter a recovery code instead. Case and the dash don't matter. Each code works once, and Settings shows how many unused ones remain.

### Replace your recovery codes {#new-recovery-codes}

Next to **Generate new codes**, enter a current authenticator code. Ten new codes replace the old set, which stops working immediately. Do this when you're running low or suspect someone saw them.

### Turn two-factor off {#turn-off-two-factor}

Next to **Turn off two-factor**, enter an authenticator code or a recovery code and press **Turn off**. The setup key and recovery codes are deleted. If you turn 2FA back on you'll get a new QR code, so delete the old entry from your app.

### What it does and doesn't cover {#what-two-factor-covers}

2FA guards dashboard sign-in, and with it your balance, billing, keys and logs. API requests never ask for a second factor: a key works on its own. Treat each key with the same care as a password.

## Verify your email address {#verify-your-email}

If the dashboard shows a reminder to verify your email, open the link we sent you when you signed up. It works once, for 48 hours; **Send the link again** in the reminder gets you a new one. We use the verified address to tell you about changes to your account.

## Change your password {#change-your-password}

Under **Change password**, type your current password and a new one of 8 characters or more, then press **Update password**. Every other browser signed in to your account is signed out. The browser you're using stays signed in.

## Sessions {#sessions}

A sign-in lasts 30 days on that browser. **Sign out**, at the bottom of the dashboard sidebar, ends it there.

The **Sessions** card in [Settings](/app/settings) counts the other browsers and devices signed in to your account. **Sign out other sessions** ends all of them and keeps you signed in where you are. Use it after using a shared computer, or whenever you suspect someone else has access.

## Keep API keys safe {#keep-api-keys-safe}

- Issue one key per app and environment, with only the limits and models it needs. See [API keys](/docs/account/api-keys).
- Load keys from environment variables or a secrets manager. Never commit them or ship them in client-side code.
- Rotate keys on a schedule, and revoke one at once if it may have leaked; revocation applies from the next request.
- Scan [Logs](/app/logs) for traffic you don't recognize. Every request shows which key sent it.

[Authentication](/docs/authentication) covers more.

## What Half Off Dev stores {#what-we-store}

| Data | How it's kept |
| --- | --- |
| API keys | A hash, plus the first few characters for display. The full key is shown once at creation and can't be recovered. |
| Password | A salted, deliberately slow hash (PBKDF2-SHA256). The password itself is never saved. |
| Dashboard sessions | A random token in a secure, HTTP-only cookie. The server keeps only a hash of it. |
| Two-factor secret | Kept so your codes can be checked. Deleted when you turn 2FA off. |
| Recovery codes | A hash of each unused code. Used codes are deleted. |
| Request logs | Metadata only: model, key, token counts, charge, timing and status. Prompts and responses are not stored. Kept for 90 days. |

## Language {#language}

Choose your language under **Language** in [Settings](/app/settings), or with the language selector in the sidebar or the docs header. The choice covers the dashboard and the docs in that browser. Until you pick one, Half Off Dev uses your browser's language.
