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 byHomepageHero.tsx)HeroMockCards.tsx,TrustStrip.tsx(only used by legacyHero.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. |
1.4 Related non-marketing content pulled into /developers¶
| 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 underfonts/) - 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 onauth.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
/developersdocs 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/inlast1id-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. Recommended extraction approach¶
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)¶
- Create
last1id-webrepo — Vite + React + Tailwind, copy marketing subset listed in §1. - Marketing-only app shell —
HelmetProvider,ThemeProvider, wouter routes for 11 pages only. No Supabase, React Query, or OAuth resumer. - Static auth URLs — env var
VITE_AUTH_ORIGIN=https://auth.last1.idfor all CTAs. - Build —
npm run build→dist/; Cloudflare Pages build commandnpm run build, outputdist. - SPA routing —
public/_redirects:/* /index.html 200(or equivalent_routes.json). - Cloudflare Pages project —
last1id-web, connect repo, preview deploy, verify all 11 routes. - DNS — point
last1.idapex (+wwwif used) to Pages. Confirm Railway fallback 404 is gone. - Optional redirects — apex → auth paths per §5.3.
- Monolith trim (L3) — remove marketing routes from
last1.idApp.tsx or gate behind env; reduce apex confusion if anything still hits auth host for/. - 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.idmonolith - 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.