Skip to main content

Milestone 5.8 — Auth-required product surface

Status: Enabled on ehxlabs.xyz (2026-05-21)
Flags: EHX_AUTH_REQUIRED=1 (API), NEXT_PUBLIC_AUTH_REQUIRED=1 (web build)
Tracking: ehx-web#10
Phase: 5 — SaaS Beta (enforcement slice)

Problem

Phase 5 preview (M5.2) allows anonymous browser principals for chat, generate, checkout, and plan binding. That creates:

  • Anonymous checkout and principal-only subscriptions that are hard to reconcile with accounts
  • Confusing link codes (users think it is team invite; Auth0 login on each device is usually enough)
  • Split identity paths (guest principal vs signed-in account)
  • Extra support burden (“where is my plan?” on a second browser)

M5.6 (Auth0) and M5.7 (recovery email + checkout guard) fixed identity and paid safety for logged-in users, but the product still works without sign-in.

Goal

Move from preview auth to account-required product use:

  1. Sign in required to use product features (free tier and paid).
  2. Marketing stays public — home, pricing, docs, read-only catalog browse.
  3. No anonymous checkout — checkout only with a valid session (account_id).
  4. Account-only entitlements — remove principal-only paid resolution paths.
  5. Deprecate link codes — Auth0/social login on each browser links the principal automatically; remove generate/redeem UI and document migration.
  6. Linked browsers remains read-only session visibility (IP, country, browser label, unlink) — not a separate onboarding path.

In scope

Web (gates)

SurfaceTarget behavior
/chatRedirect to /account/login?returnTo=… when no session
/generateSame
/checkoutSame; no checkout session without session token
/account/plan, /account/activityAlready gated — keep
Marketing (/, /pricing, /docs, /templates, /modules, /kb)Stay public

API

AreaTarget behavior
Chat, generate, usageRequire Authorization: Bearer session (401 without)
Checkout session create/confirmRequire session; bind user_id from authenticated account principal
EntitlementsResolve paid/free tier from account_id only
Link codesDeprecate POST /auth/link-codes and redeem (or 410 after migration window)

UX copy

  • Replace “link another device” onboarding with: “Sign in on each browser with Google/GitHub.”
  • Linked browsers list = security/visibility only.

Out of scope (later milestones)

TopicMilestone
Team invites / seat allocation / org workspacePhase 5+ or Phase 6 (see roadmap “shared projects + seat model”)
Email invite for teammatesNot M5.8
HD addresses, renewal webhooksM5.2 follow-up

Acceptance criteria

  • Unauthenticated users cannot send chat messages, run generate, or create checkout sessions (API 401 + web redirect) when flags enabled.
  • Free tier requires sign-in when flags enabled; quotas bind to account_id.
  • Checkout confirm creates/updates subscription on account only when auth-required (no anonymous principal subscription path).
  • Link-code UI hidden from /account when auth-required; API link-code endpoints return 410.
  • feature-tier-registry.md updated: auth_required enforcement mode documented.
  • M5.2 doc annotated: preview anonymous/link-code flows superseded by M5.8.
  • Migration UX for in-flight anonymous checkout (CheckoutRecoveryBanner + checkout entitlement guard).

Depends on

Supersedes (preview behavior)

From M5.2 auth preview:

  • Anonymous browser:uuid principal for product APIs
  • Cross-device link codes
  • Checkout without session
  • Principal-only subscription / checkout_stub entitlements for unsigned users

Enabled on ehxlabs.xyz

Set in ehx-web/.env (gitignored) and applied via docker compose build web && docker compose up -d --force-recreate web api.

Smoke (verified): unsigned POST /checkout/sessions401 session_required; signed-out /checkout shows sign-in gate; POST /auth/link-codes410 link_codes_deprecated; /pricing stays public.

Task registry

Granular issues ehx-web #19–21 and ehx-api #8–11 are closed. They appear under Shipped → M5.8 in roadmap-task-registry.md (not in the P0 open table).