Add a marketing landing page for inputhalo.freno.me with a hero section
(theme-aware app icon, glitch title, tagline), feature highlights, and a
download section offering a macOS DMG via the tRPC signed-S3 URL endpoint
plus an App Store link.
Extract the download orchestration into a pure, side-effect-free helper
(query/performInputHaloDownload) so the asset-name and redirect contract is
unit-tested in isolation, mirroring the existing site-aware page-head /
nav-config split. Include dark and default app-icon exports in the public
folder.
Consolidate privacy policies per-subdomain:
- Migrate Gaze and Life and Lineage privacy policies verbatim from the
legacy /privacy-policy/* routes to their subdomain paths
(gaze.freno.me/privacy, lineage.freno.me/privacy), and replace the old
routes with 308 permanent redirects to preserve SEO equity and stable
link resolution.
- Add net-new privacy policies for the InputHalo and Nessa subdomains.
- Update the contact page link to point at the Lineage subdomain privacy
policy.
Move the Gaze marketing home from the legacy /marketing/gaze route to
a dedicated gaze subdomain landing page (src/routes/gaze). The new page
adds a richer hero, feature highlights, and a direct .dmg download flow
that resolves a signed S3 URL via the tRPC downloads.getDownloadUrl
endpoint.
Extract the download-resolution logic into a shared, testable
downloadAsset helper (src/lib/download-asset) so current and future
subdomain landing pages (InputHalo, Lineage, …) share one code path.
The helper is pure over an injected DownloadApi + redirect sink, keeping
it unit-testable without importing solid-js / CSRF cookie code.
Replace the old marketing/gaze route with a permanent 308 redirect to
https://gaze.freno.me so existing inbound links keep resolving to the
canonical home.
Add unit tests covering the tRPC call shape, redirect sink, and error
handling.
Move the marketing landing page content to the new route group
served at , and convert the legacy
route into a 308 permanent
redirect to the new subdomain.
- Add hosting the migrated marketing page.
- Extract shared landing content and the redirect target into
, with unit tests asserting the
redirect destination.
Add the Nessa subdomain landing page served at nessa.freno.me/.
- index.tsx: hero, feature highlights, CTA band, and footer themed via
useSite() brandColor and useDarkMode(); deliberately avoids freno.me
web-auth UI (Nessa uses Clerk) and uses a non-fabricated 'Coming soon'
CTA since there's no app-store presence yet.
- meta.ts: deterministic PageHeadProps (title, description, OG) as a pure
module mirroring the page-head-meta testability pattern; feature copy is
derived from real nessaCommunityRouter capabilities (clubs, challenges,
social.feed, events).
- meta.test.ts: resolves metadata over SITE_CONFIG.nessa + pathname '/' to
assert title suffix composition, canonical, OG image fallback, and
description content.
Refactor sitemap.xml route to read the Host header, determine the active
site, and produce a sitemap scoped to that site's routes with canonical
URLs. Extracts route definitions and XML generation into shared modules.
- src/server/nessa-auth.ts: replace jose HS256 sign/verify with Clerk
session JWT verification via @clerk/backend verifyToken (RS256/JWKS).
signNessaToken removed — frontend now supplies Clerk session tokens.
- src/server/api/utils.ts: createTRPCContext verifies Clerk JWT, resolves
ctx.nessaUserId via SELECT id FROM users WHERE clerkUserId=? on the
shared NessaConnectionFactory. Lookup miss throws typed UNAUTHORIZED
(webhook has not run yet). Invalid/expired tokens are swallowed; the
enforceNessaUser middleware rejects null nessaUserId.
- src/server/api/routers/nessa-community-authz.test.ts: add clerkUserId
lookup tests (seeded match, missing row, mismatched id, local≠clerk).
- src/server/nessa-auth.test.ts: verifyNessaToken unit tests with mocked
@clerk/backend (valid sub, missing sub, malformed/expired/wrong-signature
rejection) plus static audit that signNessaToken is gone.
- src/server/clerk-user-webhook.ts + src/routes/api/clerk-webhook.ts:
Clerk user.created/user.updated webhook handler (Svix signature
verification, idempotent upsert by clerkUserId, lazy ALTER TABLE
migration) with full test suite.
- src/server/api/routers/nessa.ts: remove legacy register/login/google/
apple sign-in mutations (Clerk is now the sole identity provider).
- src/env/server.ts: add NESSA_CLERK_SECRET, NESSA_CLERK_JWT_ISSUER,
NESSA_CLERK_WEBHOOK_SECRET; NESSA_JWT_SECRET moved to optional.
- package.json: add @clerk/backend, svix; lineage/auth.test.ts and
nessa-ownership.test.ts: add Clerk env vars to env mocks.
- .env.example: document Clerk config vars and rotation.
- delete nessa-google-oauth.test.ts (Google auth removed).
ctx.nessaUserId remains the local users.id — router bodies are untouched.