Magic-link auth + onboarding flow. Go backend, React Native (Expo) frontend.
Users enter their email, get a login link, tap it, and land in the app. First-time users see a short onboarding flow with bottom sheets: one asks if they're enjoying the app, then branches to either a feedback form or an app store redirect. Feedback gets stored and published to Slack (mocked for now, logs to console).
backend/ Go API server (stdlib net/http, SQLite, Ed25519 JWTs)
mobile/ React Native app (Expo Router, TypeScript)
Makefile Shortcuts for keygen, run, test, clean
- Go 1.25+
- Node.js 18+
- OpenSSL (for key generation)
- Expo Go on your phone (if testing on a physical device)
make keygenThis creates an Ed25519 keypair in backend/keys/. You only need to do this once.
make runStarts on localhost:8080. SQLite database gets created automatically on first run. Magic links print to the console instead of sending real emails.
cd mobile
npm install
npx expo startScan the QR code with Expo Go, or press w for the web version.
If you're testing on a physical device, you'll need to edit mobile/src/lib/constants.ts and swap localhost for your machine's LAN IP so the phone can reach the backend. Also open the firewall port:
sudo firewall-cmd --add-port=8080/tcp- Enter any email on the sign-in screen
- Check the
make runterminal for the magic link token - Paste the token into the dev input field on the "Check your email" screen
- You're in
Deep links follow the format rizontask:///verify?token=TOKEN. In Expo Go, the scheme is different:
# Warm start (app already open)
adb shell am start -a android.intent.action.VIEW \
-d "exp://YOUR_IP:8081/--/verify?token=TOKEN"
# Cold start (kill the app first)
adb shell am force-stop host.exp.exponent
adb shell am start -a android.intent.action.VIEW \
-d "exp://YOUR_IP:8081/--/verify?token=TOKEN"make testThe backend reads these from the environment (all optional, sane defaults):
| Variable | Default | What it does |
|---|---|---|
PORT |
8080 | Server port |
DATABASE_PATH |
rizon.db | SQLite file path |
JWT_PRIVATE_KEY_PATH |
keys/ed25519.key | Signing key |
JWT_PUBLIC_KEY_PATH |
keys/ed25519.pub | Verification key |
BASE_URL |
http://localhost:8080 | Used in magic link URLs |
ACCESS_TOKEN_TTL |
900 | Access token lifetime (seconds) |
REFRESH_TOKEN_TTL |
604800 | Refresh token lifetime (seconds) |
MAGIC_LINK_TTL |
15m | How long magic links stay valid |
- Global rate limit: 10 req/s per IP (token bucket, burst 20)
- Magic link rate limit: 3 per email per 10 minutes
- Tokens are single-use and time-limited (15 min default)
- Tokens are SHA256-hashed before storage
- Generic error messages to prevent email enumeration