hosman20 turns Mike into a paid product for Middle East law firms

Phase one of a rebrand swaps the prototype's bring-your-own-API-key model for a subscription, paywall, and trial - staged so any piece can be rolled back.

brandinginfrastructure

The fork drops the screen where customers paste in their own AI provider keys and routes every model call through Vercel's AI Gateway - a billing-and-routing layer that lets the firm pay one bill and tag spend per user and per organisation. Signup now demands a firm name from the first field, reframing the product as B2B rather than a developer toy.

On top of that sits a Stripe subscription with three tiers, a 14-day auto-trial on signup, and a paywall that blocks chat the moment a trial expires or a token budget runs out - bouncing users to a pricing page with the reason attached. The visual layer is rebuilt around a quieter "warm paper and ink" palette, with a narrower navigation rail and a new Playbooks section flagged for later. @hosman20 is explicit that mobile navigation, seat limits, and the live streaming path against the real gateway are still open work.

So what Worth a look for anyone watching how open-source legal-AI projects make the jump from prototype to something a firm can actually buy.

View this fork on GitHub →

Spotted something wrong? Or know the PR text has fresher detail than the writeup above?

Commits in this thread

12 commits from hosman20/mike-2.0, oldest first. Source extracted verbatim from the harvested git log.

SHA Subject Author Date
3da5dd95 docs(mike-2.1): add design tokens and frontend inventory specs z 2026-05-13 ↗ GitHub
commit body
Batch 0a + 0b - pre-implementation reference docs.

- docs/mike-2.1-design-tokens.md: bespoke unprefixed token family
  (B:* nitro and F:* shadcn .pen kits are reference only, the
  unprefixed tokens are production).
- docs/mike-2.0-frontend-inventory.md: catalog of existing primitives
  + auth pages + chrome shell that Phase 1 migrates.

Agent IDs: a431cf5ab43329570 (tokens), ad8271abf2f042093 (inventory).
7b4dccea feat(design): apply Mike 2.1 tokens and redesign 4 shadcn primitives z 2026-05-13 ↗ GitHub
commit body
Batch 1 - replace globals.css @theme with bespoke Mike 2.1 token
family, rebuild Button/Input/Badge/Dropdown against the new tokens,
add vitest + 3 smoke tests.

- frontend/src/app/globals.css: bespoke unprefixed tokens (production
  family). --color-blue/--color-azure preserved as faded blue for
  legacy .usc-section / .cfr-section CSS in legal-doc viewer.
  --font-eb-garamond next/font wiring kept for legacy .font-eb-garamond
  utility.
- frontend/src/components/ui/{button,input,badge,dropdown-menu}.tsx:
  rebuilt against Mike 2.1 tokens.
- frontend/vitest.config.ts + vitest.setup.ts: test runner config.
- frontend/src/components/ui/__tests__/primitives.smoke.test.tsx:
  3 smoke tests. All pass.

Agent ID: a4c766b6e2d6a274e (general-purpose).
3c8ffa8f feat(auth): migrate login and signup to Mike 2.1 chrome z 2026-05-13 ↗ GitHub
commit body
Batch 2 - port login + signup pages from .pen frames
(S9ZQTA/tbg1h/gtQ5F login, V3Q1xu signup) to Mike 2.1 design.

- Login + signup forms rebuilt against new tokens + primitives.
- site-logo updated to Mike 2.1 mark.
- Organisation field made REQUIRED (B2B product).
- Confirm-password field preserved (functionality preservation).
- WorkOS-SSO and magic-link buttons NOT lifted (no wired primitive).
- 2 smoke tests in app/__tests__/auth-pages.smoke.test.tsx.

Agent ID: a98a89fd5a4635549 (general-purpose).
571af72d feat(chrome): split AppSidebar into 64px IconRail and 224px SecondaryNav z 2026-05-13 ↗ GitHub
commit body
Batch 3 - replace the legacy collapsible AppSidebar with the Mike 2.1
two-column nav shell (IconRail 64w + SecondaryNav 224w).

- frontend/src/components/chrome/icon-rail.tsx: top-level icon column.
- frontend/src/components/chrome/secondary-nav.tsx: contextual nav,
  includes a new Playbooks entry.
- frontend/src/app/(pages)/layout.tsx: switch to new chrome.
- frontend/src/components/chrome/__tests__/chrome.smoke.test.tsx.

.pen frame references: wF9tL, x72bX, FdYC1, yEGYQ, QxArF, CzlFI.

Notes:
- AppSidebar.tsx left on disk (removed in batch 7 cleanup commit).
- Mobile (<md) currently hides both columns; no drawer yet.
- TrialBanner mount in this layout lands in batch 6 (billing UI).

Agent ID: a36447602e8db8572 (general-purpose).
07a46ceb feat(llm): replace direct provider SDKs with Vercel AI Gateway z 2026-05-13 ↗ GitHub
commit body
Batch 4 - route all LLM traffic through the Vercel AI Gateway via
AI SDK v6. Tokens are now paid by the platform, not the customer.

Backend:
- New gateway adapter at backend/src/lib/llm/gateway.ts (uses
  ai@^6.0.182, "provider/model" strings auto-route via the bundled
  gateway provider).
- 12 unit tests in backend/src/lib/llm/__tests__/gateway.test.ts.
- Deleted backend/src/lib/llm/{claude,gemini,openai,tools}.ts and
  backend/src/lib/userApiKeys.ts - the per-provider SDKs and the
  customer-key vault are gone.
- backend/src/lib/llm/index.ts now re-exports from gateway and routes
  streamChatWithTools / completeText through it.
- backend/src/lib/llm/types.ts: added StreamChatAttribution
  ({ userId?, orgId? }) for gateway cost-slicing; UserApiKeys kept
  as @deprecated alias so existing call-sites still typecheck.
- backend/src/lib/userSettings.ts: drop per-user api_keys lookup.
- backend/src/lib/chatTools.ts: runLLMStream now forwards attribution
  instead of apiKeys and surfaces aggregate usage.totalTokens.
- backend/src/routes/{chat,projectChat,tabular}.ts: attribution
  ({ userId }) replaces apiKeys; legacy queryGemini/title helpers
  refactored to take userId.
- backend/src/routes/user.ts: deleted GET/PUT /user/api-keys.
- backend/.env.example: replaced GEMINI_/ANTHROPIC_/OPENAI_API_KEY
  and USER_API_KEYS_ENCRYPTION_SECRET with AI_GATEWAY_API_KEY.

Frontend:
- Deleted frontend/src/app/(pages)/account/models/page.tsx - the
  user-facing API key UI no longer applies.
- frontend/src/app/(pages)/account/layout.tsx: removed the
  "Models & API Keys" tab (Billing tab arrives in batch 6).

Intermediate state notes:
- frontend mikeApi.ts still exports getApiKeyStatus + saveApiKey
  against now-404 routes; UserProfileContext still imports them.
  Both are cleaned up in batches 6/8.
- public.user_api_keys table is not dropped here - orphaned but
  harmless; can be removed in a follow-up migration.

Agent ID: a5511de9cafb2355a (vercel:ai-architect).
ba00445b feat(billing): add Stripe subscriptions and paywall middleware z 2026-05-13 ↗ GitHub
commit body
Batch 5 - gate AI chat endpoints behind an active subscription
(or active 14-day trial) and record token usage against the
subscription's monthly limit.

Database:
- backend/migrations/001_add_subscriptions.sql: idempotent
  (CREATE IF NOT EXISTS), RLS-enabled, auto-provisions a trial
  row on every new auth.users insert via trigger.
- backend/schema.sql: mirror of the migration appended.

Backend:
- backend/src/lib/stripe.ts: Stripe SDK wiring.
- backend/src/lib/billing/usage.ts: recordTokenUsage() fire-and-
  forget bookkeeping after stream completion (never blocks the
  hot path, never throws).
- backend/src/routes/billing.ts: checkout/portal/webhook endpoints.
  Handles checkout.session.completed and
  customer.subscription.{created,updated,deleted}.
- backend/src/middleware/requireActiveSubscription.ts: paywall.
- backend/src/index.ts: mount raw-body parser ahead of express.json
  for the Stripe webhook signature check; gate POST /chat,
  /projects/:id/chat, /tabular-review/:id/chat, and
  /tabular-review/:id/generate with requireAuth + paywall.
- backend/.env.example: STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET,
  and STRIPE_PRICE_{STARTER,PROFESSIONAL,ENTERPRISE}.
- backend/package.json: stripe@^22.1.1 + vitest test runner.
- routes/{chat,projectChat,tabular}.ts: void recordTokenUsage(...)
  after each successful stream.

Frontend:
- frontend/src/lib/billing.ts: tier metadata + helpers.
- frontend/src/app/lib/mikeApi.ts: HTTP 402 interceptor redirects
  the browser to /pricing?reason=... on paywall rejection.

Tests:
- 6 middleware tests + 4 webhook tests (10 new tests total).

Agent ID: aa83ddc67d4dfac03 (general-purpose).
d3956d04 feat(billing-ui): pricing page, billing settings, and trial banner z 2026-05-13 ↗ GitHub
commit body
Batch 6 - surface the new subscription state in the product:
public pricing page, in-app billing settings tab, and a top-of-
main trial countdown banner.

- frontend/src/app/pricing/page.tsx: public /pricing route, lands
  logged-out users redirected from a 402. Enterprise CTA stubs out
  to mailto:sales@mike.ai (placeholder).
- frontend/src/app/(pages)/account/billing/page.tsx: in-app billing
  settings - current plan, trial status, token usage, manage button
  (Stripe customer portal).
- frontend/src/components/chrome/trial-banner.tsx: countdown banner
  for trialing users, mounted at top of <main> in (pages)/layout.tsx.
- frontend/src/app/(pages)/account/layout.tsx: re-add the "Billing"
  tab (replaces the "Models & API Keys" tab removed in batch 4).
- frontend/src/app/lib/mikeApi.ts: SubscriptionTier/Status/Info
  types + subscription field on UserProfile.
- frontend/src/contexts/UserProfileContext.tsx: thread subscription
  through the profile context.
- backend/src/routes/user.ts: return the subscription block on the
  /user/profile response.
- 5 new tests (3 pricing + 2 billing).

Agent ID: ad65cb0ceb7e70da6 (general-purpose).
9974612f chore: remove dead code (AppSidebar, CreditsExhaustedModal, getApiKeyStatus) z 2026-05-13 ↗ GitHub
commit body
Batch 7 - refactor-cleaner sweep. Three call-site-less files
removed after the Mike 2.1 chrome migration:

- frontend/src/app/components/shared/AppSidebar.tsx (311 LOC):
  superseded by IconRail + SecondaryNav in batch 3.
- frontend/src/app/components/modals/credits-exhausted-modal.tsx
  (81 LOC): credit-system modal, no remaining importers after the
  subscription/paywall flow in batches 5-6.
- frontend/src/app/lib/mikeApi.ts getApiKeyStatus() (4 LOC):
  hits a route that no longer exists since batch 4.

Left alone (still has active importers): ApiKeyMissingModal,
modelAvailability helpers.

Agent ID: aca933ce4c3383624 (refactor-cleaner).
2034b855 docs(mike-2.1): add deploy checklist and run log z 2026-05-13 ↗ GitHub
commit body
Batch 8 - final smoke + paper trail. The deploy checklist verifies
backend + frontend builds, paywall coverage, presence of all
generated files, and lists 9 known follow-ups parked for the Phase 1
hotfix backlog. The run log is the per-batch audit trail referenced
above with the SHAs of commits 1-8.

Agent ID: a6969272e17743cb6 (general-purpose).
85348e4c feat(dev): add NODE_ENV-gated auth + paywall bypass for local previews z 2026-05-13 ↗ GitHub
commit body
Adds NEXT_PUBLIC_DEV_AUTH_BYPASS (frontend) and DEV_AUTH_BYPASS (backend)
flags. Both require non-production NODE_ENV. When on:
- Frontend skips Supabase auth, injects stub user + Professional-tier
  subscription, and shows an amber "dev mode" banner.
- Backend skips JWT verification and paywall middleware.

Production deployments ignore these flags by NODE_ENV check.
3d1b1b69 docs: record batch 10 commit SHA in run log z 2026-05-13 ↗ GitHub
82d36cca fix(ui): add /playbooks page, normalize pricing borders, migrate not-found to Mike 2.1 tokens z 2026-05-14 ↗ GitHub
commit body
- Add stub /playbooks route with empty state so the IconRail nav entry
  resolves (resolves deploy-checklist follow-up #6).
- Normalize all three pricing cards to the universal hairline
  `border border-border`; drop `border-2 border-border-active` on the
  Professional card. "Most popular" is now signaled only by the badge.
- Migrate not-found.tsx off legacy tokens (font-eb-garamond,
  text-gray-500, raw bg-gray-900 anchor) to bg-bg-canvas / foreground /
  muted-foreground + Button asChild + next/link.

Capture this thread into my fork

Download a single Markdown prompt that tells Claude how to port every commit above into your working tree — adapting paths and structure to match your repo. Run it via claude -p < capture-thread-455.md from inside the repo you want the changes in.

⬇ Download capture-thread-455.md