> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mycryptoserver.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> Dashboard sign-in, two-factor, team invites, and demo mode

The dashboard uses **email + password** sign-in with **optional email-OTP two-factor authentication**. There is no public sign-up — accounts are created by the owner.

## Creating the owner account

On first launch, visit `/setup` to create the owner account with an email and password. Once an owner exists, the setup page closes and `/login` becomes the entry point.

<Note>
  There is no self-service sign-up. The public `/api/auth/sign-up/email` route is blocked; only the owner-bootstrap and invite flows create accounts.
</Note>

## Two-factor authentication (email OTP)

Two-factor is **opt-in per user** and strongly recommended. Enable it under **Settings → Security**, or during onboarding at the **Security** step.

When 2FA is on, each sign-in is:

1. Enter email + password.
2. A 6-digit code is emailed to you (via [Resend](#email-delivery)).
3. Enter the code to finish signing in.

Backup codes are shown when you enable 2FA — store them somewhere safe.

## Inviting team members

From **Settings → Team**, the owner invites teammates by email and role. Each invite:

* Creates an account with a **temporary password** (emailed to them).
* Forces a **password reset on first login** before they can use the dashboard.

## Password reset

Users who forget their password use **Forgot password?** on the login page (`/forgot-password`) to receive an emailed reset link.

## Email delivery

Sign-in codes, invites, and reset links are sent through the **Resend** integration (configure it under **Integrations → Resend**). If Resend isn't configured, these messages are printed to the server logs instead — useful for local development:

```
[2fa-otp] you@example.com → 256816
[reset-password] you@example.com → https://…/reset-password?token=…
[invite] member@example.com temp password: A1B2C3D4E5F6
```

## Rate limiting

Auth endpoints are rate-limited by better-auth (e.g. sign-in is capped at 3 attempts per 10 seconds, password reset at 3 per minute), and 2FA codes are invalidated after 5 wrong attempts. Counters are stored in the database (the `rate_limit` table) so the limits hold across serverless instances — the in-memory default would reset on every cold start. Rate limiting is active in production automatically; no configuration is needed.

## Demo mode

Set `IS_DEMO_SITE=true` to run a public, throwaway demo. **Never enable this on a real deployment.**

In demo mode:

* A demo merchant is seeded automatically — `admin@demo.com` / `123456` — along with sample products, payment links, and transactions, so the dashboard looks populated. You can also seed manually with `pnpm db:seed:demo`.
* The login page pre-fills the demo credentials and accepts a **fixed confirmation code (`123456`)** instead of real 2FA. No email is sent.
* **Payout wallets are fixed and locked.** They're defined in `src/lib/auth/demo.ts` (`DEMO_PAYOUTS`); the settings API rejects changes to them and the UI renders the fields read-only.

<Warning>
  Demo mode accepts a publicly known password and confirmation code. Only use it for disposable demo environments — never with real funds or a production wallet seed.
</Warning>
