all systems operationalv0.17.10
stech/

Magic-link sign-in

Passwordless sign-in via a one-time link emailed to the user. Lives alongside the original email + password flow — both work, pick whichever you prefer per sign-in. No account migration; the same user can use either path interchangeably.

Email delivery is via Resend. If the RESEND_API_KEY env is unset, the magic link tab still renders but send attempts fail with a clear error — the password tab keeps working.

Sign in #

/sign-in shows two tabs: password (default) and magic link. On the magic-link tab, enter your email and hit send link:

  1. The api emails a one-time link from [email protected].
  2. You click the link — it lands at /auth/verify?token=….
  3. The verify page exchanges the token with the api, the api sets a session cookie, and you bounce to wherever you were headed (or / if you came in fresh).

Tokens expire 15 minutes after send and are single-use — clicking twice consumes the token on the first click and 410s the second. Sign-up works the same way: /sign-up has the same two tabs.

Rate limits #

Two independent counters; first to trip wins:

  • 3 requests per email per 15 minutes — the honest-user-with-stuck-inbox retry budget.
  • 10 requests per IP per hour — bulk-abuse cap.

A 429 response renders inline on the form with a retry-after hint when the api includes one.

Opting out #

There's no STECH_NO_MAGIC_LINK env. The password tab is always available — if you'd rather use email + password, just stay on the default tab.

Troubleshooting #

Didn't get the email — check spam; the From is [email protected]. SPF/DKIM/DMARC are configured on the sender subdomain so deliverability should be clean. If it still doesn't show after 2–3 minutes, request a new link (the old one stays valid until used or expired).

Link expired — tokens live 15 minutes. Just request a new one from /sign-in.

Already used — links are single-use. Request a new one.

Rate limited — wait out the window shown in the inline error. If you're hitting the per-IP limit (10/hour) from a shared NAT, switch to the password tab as a workaround.

  • Invitation emails (invitations.md) reuse the same Resend setup; if magic-link emails work, invitation emails will too.
  • Webhooks — same encrypted-at-rest posture (SecretsCrypto) for the per-endpoint signing secret.