Skip to content

Last1 ID Marketing Surface Extraction Audit (L1)

Status: Audit complete. Awaiting Ryan review before extraction (L2+).
Date: 2026-08-27
Audited repo: Last-1-Enterprises/last1.id (main, shallow clone 2026-08-27)
Path: Path B — extract marketing surface from monolith into Cloudflare Pages at https://last1.id
Constraint: Do not fix the live apex 404 during this audit. Temporary 404 is acceptable until extraction is reviewed and executed.


Executive summary

Marketing content in the last1.id monolith is small, mostly static, and feasible to extract to Cloudflare Pages without backend APIs. Eleven public routes share a common layout and do not POST to server endpoints. Contact is mailto: only.

Primary coupling risk: marketing CTAs and navbar state depend on useAuthOptional() from the full Supabase auth stack (AuthProvider, SupabaseConfigProvider, QueryClientProvider). That pulls @supabase/supabase-js, @tanstack/react-query, and optionally GET /api/config into every page load today.

Recommended approach: create a dedicated last1id-web Cloudflare Pages project (same pattern as last1org-web, onset-web), ship a lean marketing-only Vite SPA with static CTAs pointing to https://auth.last1.id, add SPA fallback + optional prerender for the 11 routes, then point apex DNS to Pages. Keep all auth, OAuth, dashboard, and dynamic public profile routes on auth.last1.id.

Live state note: as of 2026-08-27, https://last1.id returns Railway fallback 404 JSON (x-railway-fallback: true). https://auth.last1.id is healthy. Internal docs that mark apex marketing as "Pages Live" are ahead of production until this extraction ships.


1. Inventory

1.1 Marketing routes (entry points)

All routes are defined in client/src/App.tsx (wouter Switch). Marketing routes use MarketingLayout and are indexed in client/public/sitemap.xml.

Route Page file Load strategy SEO in App.tsx
/ client/src/pages/landing.tsx Eager Yes (JSON-LD Corporation)
/how-it-works client/src/pages/how-it-works.tsx Lazy Yes
/ecosystem client/src/pages/ecosystem.tsx Lazy Yes (JSON-LD apps)
/why client/src/pages/why.tsx Lazy Yes
/privacy client/src/pages/privacy.tsx Lazy Yes
/security client/src/pages/security.tsx Lazy Yes
/transparency client/src/pages/transparency.tsx Lazy Yes
/developers client/src/pages/developers.tsx Lazy Yes (JSON-LD)
/faq client/src/pages/faq.tsx Lazy Yes
/terms client/src/pages/terms.tsx Lazy Yes
/security/responsible-disclosure client/src/pages/responsible-disclosure.tsx Lazy Yes

Total marketing routes: 11 (matches sitemap.xml exactly).

1.2 Marketing components (client/src/components/marketing/)

25 files total. 19 are active (imported by pages or by other active components). 6 are legacy/unused (safe to omit from extraction unless Ryan wants parity).

Active (used by marketing pages):

File Used by
MarketingLayout.tsx All 11 pages
MarketingNavbar.tsx MarketingLayout
MarketingFooter.tsx MarketingLayout
Section.tsx Most content pages
CTA.tsx why.tsx
TrustCard.tsx privacy, security, how-it-works, responsible-disclosure
DiagramBlock.tsx how-it-works, ecosystem
Reveal.tsx landing
HomepageHero.tsx landing
HomepageProblem.tsx landing
HomepageShift.tsx landing
HomepageHowItWorks.tsx landing
HomepagePrinciples.tsx landing
HomepageUseCases.tsx landing
HomepageArchitecture.tsx landing
HomepageTrustPositioning.tsx landing
HomepageEcosystem.tsx landing
HomepageFinalCta.tsx landing

Legacy (not imported anywhere in client/src/):

  • Hero.tsx (superseded by HomepageHero.tsx)
  • HeroMockCards.tsx, TrustStrip.tsx (only used by legacy Hero.tsx)
  • FeatureGrid.tsx, EcosystemPreview.tsx, PositioningBand.tsx, StepsPreview.tsx

1.3 Landing copy module (client/src/landing/)

File Status
client/src/landing/constants.ts Dead code — not imported anywhere. Current homepage uses Homepage* components with inline copy. Optional cleanup in monolith; omit from extraction.
File Role
client/src/components/docs/DeveloperDocsContent.tsx (~346 lines) Static integration docs embedded on public /developers page. No runtime API calls. Contains stale OIDC URLs pointing at https://last1.id instead of https://auth.last1.id (see §7).

1.5 Shared support files (required for extraction)

Path Purpose
client/src/components/BrandIdentity.tsx Navbar wordmark
client/src/components/SEO.tsx Per-route meta (react-helmet-async)
client/src/lib/seo.ts Site URL, OG defaults
client/src/lib/utils.ts cn() helper
client/src/lib/marketingAnalytics.ts Client-side gtag/plausible/dataLayer only
client/src/components/ui/button.tsx (+ Radix slot deps) CTAs
client/src/components/ThemeProvider.tsx Dark theme default (next-themes)
client/src/index.css Tailwind + design tokens
client/tailwind.config.js, client/postcss.config.js Styling pipeline
client/index.html Shell
client/public/* Static assets (see below)

1.6 Public static assets (client/public/)

Ship with Pages build:

  • Brand: favicon.png, last1-mark.png, last1-id-symbol.png, last1-id-wordmark.png, logo.png, og-image.png, logo-drafts/
  • Fonts: fonts/fonts.css (+ font files under fonts/)
  • SEO: robots.txt, sitemap.xml
  • Security: .well-known/security.txt (static contact/disclosure pointer; not OIDC discovery)

Do not serve from marketing apex post-extraction:

  • OIDC discovery (/.well-known/openid-configuration, /.well-known/jwks.json) — lives on auth.last1.id (Express server in monolith today).

1.7 Routes explicitly out of marketing scope (stay on auth.last1.id)

These remain in the monolith app behind the auth host:

Category Routes
Auth /login, /signup, /forgot-password, /reset-password, /onboarding
OAuth / partner /authorize, /oauth/authorize, /oauth-authorize, /oauth/account-entry
Dashboard / account /dashboard, /dashboard/*, /profile, /credentials, /settings, /export, etc.
Developer portal (authenticated) /developer/apps, /developer/integrations, /developer/docs
Dynamic public /id/:username, /identity/:id, /proof/:credentialId, /present/:id, /proof-requests/:id

2. Dependency graph

2.1 Imports from auth / dashboard / API directories

Marketing surface Imports from auth/dashboard/API? Details
11 page files Indirect via auth context only landing.tsx, why.tsx, developers.tsx import useAuthOptional from @/contexts/AuthContext
MarketingNavbar.tsx Yes useAuthOptional for Login / Get Started / Dashboard labels and hrefs
MarketingLayout, footer, homepage sections No Pure presentation
developers.tsx Yes (auth + docs) Auth for CTA state; DeveloperDocsContent is static
App shell (today) Yes Every route wrapped in QueryClientProvider → SupabaseConfigProvider → AuthProvider → OAuthHandshakeResumer

No marketing page imports dashboard components, ProtectedRoute, apiRequest, or server route handlers directly.

2.2 Backend API calls from marketing pages

Call Trigger Marketing impact
GET /api/config SupabaseConfigProvider when VITE_* env vars missing App shell only; not used by page content
GET /api/identity/me AuthProvider.fetchIdentity when Supabase session exists Navbar/CTA personalization only
POST / forms None on marketing pages —

Marketing page components themselves contain zero fetch, useQuery, or form POSTs.

2.3 Third-party dependencies (marketing-relevant subset)

From monolith package.json, the marketing surface actually exercises:

Package Usage
react, react-dom UI
wouter Client routing
framer-motion Navbar mobile menu, Reveal animations
lucide-react Menu icons in navbar
react-helmet-async SEO component
next-themes Dark theme provider
@radix-ui/react-slot shadcn Button
class-variance-authority, clsx, tailwind-merge Button + cn()
tailwindcss, tailwindcss-animate Styling

Pulled in today only because of auth shell (should drop from marketing build):

  • @supabase/supabase-js
  • @tanstack/react-query

Not used on marketing pages:

  • react-turnstile (signup/login only)
  • date-fns (dashboard)
  • Express server deps

Analytics (marketingAnalytics.ts): optional client-side hooks only (window.gtag, window.plausible, window.dataLayer). No third-party script tags in index.html today; analytics is inert until tags are injected at deploy time.


3. Static-ability check

3.1 Can marketing pages be pre-rendered as static HTML?

Yes. All 11 routes are compile-time content:

  • No feature flags
  • No CMS or remote config
  • No waitlist or contact forms
  • FAQ/legal copy is inline JSX
  • /developers docs are a static React component

Client-side only behaviors (compatible with SSG or SPA):

  • Framer Motion scroll/reveal animations (hydrate after load)
  • Navbar mobile menu toggle
  • Homepage hero "preview state" toggle (local React state)
  • Footer copyright year via new Date().getFullYear()

3.2 Runtime data needs today (extraction should remove)

Need Required for marketing? Recommendation
Supabase session No for content Drop AuthProvider from marketing app; use static CTAs
/api/config bootstrap No Omit SupabaseConfigProvider
Auth-aware CTA labels ("Dashboard" vs "Get Started") Nice-to-have Replace with static "Sign in" / "Get started" → auth.last1.id
Analytics env Optional Add gtag/plausible in Pages env if desired; not blocking

3.3 Rendering strategy recommendation

Phase Approach Rationale
L2 (MVP) Vite SPA + Cloudflare Pages _redirects (/* /index.html 200) Fastest path to replace apex 404; SEO already handled client-side via react-helmet-async
L2+ (follow-up) Prerender 11 routes at build time (vite-ssg, @cloudflare/vite-plugin, or post-build snapshot) Stronger first paint and crawler guarantees for /, /developers, legal pages

Either approach is valid; MVP SPA is sufficient to unblock apex.


4. Existing build config

4.1 Current tooling (monolith)

Piece Location Notes
Vite 8 + @vitejs/plugin-react vite.config.ts Root = client/, out = dist/public/
Combined build script/build.ts Vite client then esbuild server → dist/index.cjs
Dev server npm run dev → tsx server/index.ts Proxies /api to :5003
Production Docker / Railway Node serves Express + static dist/public
Railway railway.toml Dockerfile build, healthcheck /api/health

4.2 Split-build precedent in last1.id repo

None. Single Vite config, single combined artifact, no wrangler.toml, no marketing-specific npm script.

4.3 Precedent in sibling repos (M1 Pages track)

Repo Pattern
last1org-web Standalone Vite React app, Cloudflare Pages, optional Pages Functions for forms (/api/send-email, newsletter)
onset-web Standalone marketing on Pages (clinic app separate)

last1org-web uses CLOUDFLARE_PAGES_ENV.md for deploy secrets. last1.id marketing does not need Pages Functions for L2 (no forms).

4.4 Extraction build options

Option Pros Cons
A. New last1id-web repo (recommended) Matches M1 pattern; smallest deploy surface; no risk to auth monolith builds; independent preview URLs Code duplication or need shared package for BrandIdentity/docs
B. Dual Vite target in monorepo (vite.marketing.config.ts, npm run build:marketing) Single repo; shared components Still couples CI; easy to accidentally bundle server deps; nonstandard for other Last1 marketing sites
C. Copy subset into existing last1org-web Reuses Pages project infra Wrong product boundary; do not do this

Recommendation: Option A — Last-1-Enterprises/last1id-web, Cloudflare Pages project name last1id-web.


5. Auth flow integration points

5.1 Current CTA routing (monolith, same-origin)

Surface Control Logged out Logged in, not onboarded Logged in, onboarded
MarketingNavbar Login /login hidden hidden
MarketingNavbar Primary /signup /onboarding /dashboard
landing.tsx / why.tsx Primary CTA /signup /onboarding /dashboard
developers.tsx Admin sign in /login?entry=admin same same
developers.tsx Builder / console /signup /signup /developer/integrations
developers.tsx Docs portal link — — /developer/docs (if logged in)

All paths are relative today, assuming apex serves both marketing and auth.

5.2 Post-extraction routing (recommended)

Primary rule: all auth and app deep links use absolute URLs on https://auth.last1.id.

CTA Post-extraction target
Sign in / Login https://auth.last1.id/login
Get started / Sign up https://auth.last1.id/signup
Continue setup https://auth.last1.id/onboarding
Dashboard https://auth.last1.id/dashboard
Company admin sign in https://auth.last1.id/login?entry=admin
Integrations console https://auth.last1.id/developer/integrations
Developer docs portal https://auth.last1.id/developer/docs

Do not host a marketing-local login page on apex unless Ryan explicitly wants a branded landing that only redirects. Direct to auth host is simpler and matches Tier 2 split (auth.last1.id = identity, last1.id = marketing).

5.3 Optional apex redirects (Cloudflare)

Add redirect rules on the Pages project or zone for bookmarks and old links:

/login*          → https://auth.last1.id/login$1   302
/signup*         → https://auth.last1.id/signup$1  302
/dashboard*      → https://auth.last1.id/dashboard$1 302
/oauth/*         → https://auth.last1.id/oauth/$1 302
/developer/*     → https://auth.last1.id/developer/$1 302
/id/*            → https://auth.last1.id/id/$1 302
/proof/*         → https://auth.last1.id/proof/$1 302

Marketing internal links (/developers, /privacy, etc.) stay on apex.

5.4 OAuth handshake note

OAuthHandshakeResumer must not ship on marketing Pages. It belongs only on auth.last1.id. Partner OAuth flows already target the auth host after Tier 2.


6. Waitlist / contact / form endpoints

6.1 Marketing forms audit

Mechanism Location Backend?
Contact MarketingFooter → mailto:support@last1.id No
Security contact security.tsx, terms.tsx, responsible-disclosure.tsx → mailto:security@last1.id No
Waitlist Not present —
Newsletter Not present —
Contact page (/contact) Referenced in dead landing/constants.ts only; no route in App.tsx —

Conclusion: no marketing POST endpoints. No Pages Functions required for L2.

6.2 Post-extraction API placement (if forms added later)

If Ryan adds waitlist or contact forms later, follow last1org-web pattern:

  • Implement as Cloudflare Pages Functions under functions/api/ in last1id-web
  • Secrets in Pages project env (SendGrid, Supabase if needed)
  • Do not route marketing form POSTs back to the auth monolith on R940

7. Content corrections required during extraction (not in L1 scope)

These are not blockers for feasibility but must be fixed in L2:

Item Current Should be
OIDC issuer in DeveloperDocsContent.tsx https://last1.id https://auth.last1.id
Discovery URL in docs https://last1.id/.well-known/openid-configuration https://auth.last1.id/.well-known/openid-configuration
JWKS URL in docs https://last1.id/.well-known/jwks.json https://auth.last1.id/.well-known/jwks.json
Logo URL in docs https://last1.id/last1-mark.png Keep on apex marketing CDN (https://last1.id/last1-mark.png) — valid after Pages ships
Tier 2 deferred item iOS issuer still https://last1.id in some clients Separate issuer migration track

8.1 Target architecture

https://last1.id          → Cloudflare Pages (last1id-web) — static marketing SPA
https://auth.last1.id     → R940 auth-last1id VM — monolith app (Express + Vite client for auth/dashboard/OAuth)

8.2 L2 execution plan (after Ryan approves audit)

  1. Create last1id-web repo — Vite + React + Tailwind, copy marketing subset listed in §1.
  2. Marketing-only app shell — HelmetProvider, ThemeProvider, wouter routes for 11 pages only. No Supabase, React Query, or OAuth resumer.
  3. Static auth URLs — env var VITE_AUTH_ORIGIN=https://auth.last1.id for all CTAs.
  4. Build — npm run build → dist/; Cloudflare Pages build command npm run build, output dist.
  5. SPA routing — public/_redirects: /* /index.html 200 (or equivalent _routes.json).
  6. Cloudflare Pages project — last1id-web, connect repo, preview deploy, verify all 11 routes.
  7. DNS — point last1.id apex (+ www if used) to Pages. Confirm Railway fallback 404 is gone.
  8. Optional redirects — apex → auth paths per §5.3.
  9. Monolith trim (L3) — remove marketing routes from last1.id App.tsx or gate behind env; reduce apex confusion if anything still hits auth host for /.
  10. Prerender pass (L3+) — snapshot 11 HTML files for SEO if metrics warrant.

8.3 Feasibility verdict

Criterion Assessment
Scope size Small — 11 routes, ~19 active components, 1 docs embed
Backend dependency None for marketing
Auth decoupling effort Low — replace useAuthOptional with static links
Build complexity Low — standard Vite → Pages
Risk to identity stack Low if DNS/auth host unchanged
Time to MVP ~1–2 days engineering + DNS cutover review

Overall: extraction is feasible and aligned with Tier 2 intent. The current apex 404 is purely "marketing not deployed yet," not a structural blocker.


9. Risks and open questions for Ryan

# Question / risk Default recommendation
1 New repo vs monorepo dual build? New last1id-web repo
2 Auth-aware navbar vs static CTAs? Static CTAs to auth.last1.id (simpler, smaller bundle)
3 Include unused legacy marketing components? Omit unless needed for design parity
4 Analytics on marketing? Defer; wire gtag/plausible via Pages HTML injection when ready
5 www.last1.id Confirm whether www → apex redirect needed
6 Apex redirects for /login, etc.? Yes — add Cloudflare redirects for bookmark compatibility
7 Who updates consolidation doc "Pages Live" status? Update after L2 deploy + probe passes, not before

10. Explicit non-goals (L1)

  • No code changes in last1.id monolith
  • No DNS or Pages project creation
  • No fix for live apex 404 (temporary until L2)
  • No HIPAA, Tier 3+, or unrelated infra work

11. References

  • Monolith routes: client/src/App.tsx
  • M1 Pages track: docs/architecture/LAST1_INFRASTRUCTURE_CONSOLIDATION_PLAN.md
  • Tier 2 closeout: docs/architecture/LAST1_ID_MIGRATION_PLAN.md, W7 reports
  • Railway teardown: docs/architecture/LAST1_ID_WEEK7_W7-4_REPORT.md
  • Sibling precedent: Last-1-Enterprises/last1org-web (CLOUDFLARE_PAGES_ENV.md, Vite standalone)

Next step: Ryan reviews this audit. On approval, open L2: scaffold + deploy last1id-web to Cloudflare Pages and cut apex DNS.