Accounts

Accounts, saved projects, and billing

Tracey uses Better Auth for identity and Postgres for logged-in project storage. These are real backend features, not client-only account state.

Current behavior

  • Better Auth handles passwordless email OTP login, email verification, logout, and sessions.
  • Direct email sign-in sends a one-time code to the account email address. Social sign-in relies on provider-verified email state.
  • GitHub, Google, and Apple login can be enabled by setting provider client ids and secrets.
  • Saved projects appear on the Account page and can be reopened in the studio.
  • Account settings live on a separate Settings page, where profile/preferences are separated from sign-in and security.
  • Settings can update the account display name, show the sign-in email and OTP-based auth posture, and store default designer attribution for new fonts.
  • Signed-in studio changes autosave to the current cloud font project after edits; the first save creates the cloud project immediately after import before the user leaves the editor.
  • Inspector edits, repair results, reassignment, import merges, fidelity apply, and project metadata edits use narrow authenticated mutation routes, with full project autosave retained as a compatibility fallback.
  • The editor shows whether changes are saved locally, syncing to cloud, queued for retry, blocked by a cloud conflict, or fully synced. Local recovery drafts are scoped per cloud project so multiple saved fonts do not overwrite one another in IndexedDB.
  • Cloud edits use project-version checks. If the same saved project changes elsewhere before this browser syncs, Tracey pauses autosave and offers a reload action instead of silently overwriting newer cloud data.
  • Account project rows can be opened, duplicated, archived, restored, or permanently deleted from archive.
  • Permanent delete queues owned object-storage asset cleanup in Postgres and retries bucket deletion on later account/project traffic until it succeeds or the cleanup job exhausts retries.
  • Unsigned local drafts use the same document model in IndexedDB, including multiple analyzed source uploads and detected source squares. Signing in changes the persistence boundary to Postgres and protected asset storage; it does not change what the document is.
  • Logged-in storage is Postgres-backed. Run migrations before testing account flows.
  • Project metadata, document-level font metrics, import assumptions, glyph outlines, glyph metrics, review issues, source uploads, and detected source squares are stored in structured Postgres tables.
  • Uploaded scan previews, source-cell crops, original clean masks, repaired masks, and glyph reference images are stored as authenticated assets. Project, source-square, and glyph records keep protected /api/assets references instead of embedding image data in JSON or exposing bucket URLs.
  • Better Auth memory mode is blocked unless TRACEY_ALLOW_MEMORY_AUTH=true is set for a throwaway local UI demo.
  • Export complexity is measured from traced glyph count, path detail, and unresolved review issues.
  • Exports above the free complexity limit require a one-off font export entitlement or active membership.
  • Stripe Checkout is used when live keys and price ids are configured.
  • Local development uses port 3300 so auth callback URLs stay stable.

Setup

  1. Create a Postgres database.
  2. Set DATABASE_URL in the environment for the runtime app.
  3. Set DATABASE_MIGRATION_URL if the runtime database user is not allowed to create tables.
  4. Run yarn db:migrate.
  5. Deploy only after migrations have run; normal request handlers do not create or alter database tables.
  6. Keep Vercel Functions close to Postgres. This project currently pins functions to syd1 in vercel.json because the database is in DigitalOcean Sydney.
  7. Set Better Auth secret and URL values.
  8. Configure S3-compatible object storage for uploaded scans and generated raster artifacts.
  9. Run yarn db:backfill-assets after migrating an older database that may still have embedded scan-preview data URLs.
  10. Add email and social provider credentials as needed.
  11. Start the app and create a real account from /login.

Environment

  • DATABASE_URL for Better Auth and Tracey project storage.
  • DATABASE_MIGRATION_URL for schema migrations when the runtime database user cannot create tables.
  • DATABASE_APP_ROLE for granting runtime table access when migrations run as a different owner role.
  • PGSSLMODE when local TLS needs disable or no-verify.
  • PGSSLROOTCERT, PGSSLROOTCERT_B64, or PGSSLROOTCERT_PEM when Postgres requires a trusted CA certificate.
  • TRACEY_ALLOW_MEMORY_AUTH=false for normal development and production.
  • TRACEY_ASSET_S3_* values for private object storage.
  • TRACEY_MAX_ASSET_BYTES for the maximum accepted stored image asset size.
  • BETTER_AUTH_SECRET for encrypted/signed auth state.
  • BETTER_AUTH_URL or NEXT_PUBLIC_APP_URL for auth callbacks and email code origin URLs.
  • TRACEY_AUTH_ALLOWED_HOSTS when production or preview auth runs on custom domains.
  • BETTER_AUTH_TRUSTED_ORIGINS when a deployment needs explicit full-origin allowlist entries.
  • RESEND_API_KEY and TRACEY_EMAIL_FROM for email verification and OTP email delivery. TRACEY_EMAIL_FROM must be on a domain verified in Resend.
  • GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET to enable GitHub login.
  • GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET to enable Google login.
  • APPLE_CLIENT_ID, APPLE_CLIENT_SECRET, and optionally APPLE_APP_BUNDLE_IDENTIFIER to enable Apple login.
  • STRIPE_SECRET_KEY or STRIPE_API_KEY for Stripe Checkout.
  • STRIPE_MODE plus STRIPE_SANDBOX_* or STRIPE_LIVE_* keys for explicit billing environments.
  • STRIPE_WEBHOOK_SECRET for entitlement webhooks.
  • STRIPE_FONT_EXPORT_PRICE_ID for one-off exports.
  • STRIPE_MEMBERSHIP_PRICE_ID for membership checkout.

Known limits

  • Project deletion and account data export are not implemented.
  • One-off export entitlements are bound to saved project ids, so paid complex exports should be saved first.
  • The studio uses full project snapshots only to create the initial cloud project and catch up changes made while that create is in flight. Existing saved projects sync through narrow glyph and metadata mutations, failed client mutations are queued locally for retry, and stale mutations are blocked by project-version checks with an explicit reload recovery path. The next storage pass is browser tests for failed-mutation replay and conflict handling.
  • Asset storage must use the S3-compatible driver. Keep the bucket private; Tracey serves assets through authenticated /api/assets routes. Any legacy asset rows saved to local serverless storage need a fresh scan upload.
  • Membership cancellation handling depends on Stripe webhook delivery.
  • There is no organization/team model yet.

Useful links