Rebrand runbook: FitKit → Taikan
Status: code rename done on chore/rebrand-taikan (main repo + mobile repo). Everything
below is what the code now assumes exists, and the order in which to make it true.
Already applied 2026-08-22 (agent, via API/MCP): Vercel projects renamed to taikan-web / taikan-marketing /
taikan-admin / taikan-minisites; PostHog project → “Taikan”, dashboard → “Taikan Coach Observability”,
authorized URLs now include usetaikan.com + app.taikan.fit.
Also applied 2026-08-22 after approval: GitHub repos renamed to desmotech/taikan + desmotech/taikan-mobile
(homepage/description set; local remotes in the main checkouts updated); Railway project → Taikan, services →
@taikan/api / @taikan/api-scheduler; Sentry projects → taikan-backend / taikan-frontend / taikan-mobile
(DSNs unchanged — ids 4510894678540368 / 4510895035449424 / 4511416743362640 verified identical, so no
DSN edits anywhere); SENTRY_API_PROJECT=taikan-backend set on Railway prod+preview (API + scheduler). The Sentry
org slug is still fitkit1 (dashboard-only) — sentry-release.yml and a new Vercel SENTRY_ORG=fitkit1 env
pin it until you rename the org, then flip both to taikan.
Target map:
| Concern | Old | New |
|---|---|---|
| Marketing / company | fitkit.fit | usetaikan.com |
| Web app | app.fitkit.fit | app.taikan.fit |
| API | api.fitkit.fit | api.taikan.fit |
| Admin | admin.fitkit.fit | admin.taikan.fit |
| Clerk frontend API | clerk.fitkit.fit (+ accounts.) | clerk.taikan.fit (+ accounts.) |
| Per-org minisites | <slug>.fitkit.fit | <slug>.taikan.fit (legacy suffix still resolves) |
| Docs | docs.fitkit.fit / fitkit-docs.pages.dev | docs.usetaikan.com / taikan-docs.pages.dev |
| Outbound email | * <…@fitkit.fit> | Taikan <support@usetaikan.com>, noreply@usetaikan.com |
| Inbound (conversation replies) | contact+<id>@inbound.fitkit.fit | contact+<id>@inbound.usetaikan.com |
| Media / CDN / forms | cdn. media. forms.fitkit.fit | same hosts on taikan.fit |
| npm package (mobile) | @desmotech/fitkit-shared | @desmotech/taikan-shared |
| Workspace scope | @fitkit/* | @taikan/* |
| GitHub repos | desmotech/FitKit, desmotech/FitKit-mobile | desmotech/taikan, desmotech/taikan-mobile |
| Sentry | org fitkit1, projects fitkit-{backend,frontend,mobile} | org taikan, projects taikan-{backend,frontend,mobile} |
| Mobile app | fit.fitkit.app, slug fitkit, scheme fitkit:// | new app fit.taikan.app, slug taikan, scheme taikan:// (+ fitkit:// kept) |
| Local DBs | fitkit_dev, fitkit_test | taikan_dev, taikan_test |
| CI seed secret | fitkit-e2e-secret | taikan-e2e-secret |
Deliberately not renamed (vendor-issued or identity-bearing — renaming breaks things for no gain):
FITKITPAYMENT* Cardcom module ids · Sentry short-ids FITKIT-BACKEND-nn in docs · Firebase project fitkit-7b46b
(replaced by a new project, see §6) · the legacy iOS app id in apple-app-site-association (listed next to the new one) ·
ICS UID:…@fitkit.fit in apps/web/src/components/course-player/ics.ts (calendar identity) ·
Railway host fitkitapi-preview.up.railway.app (re-provisioned in §4) · Linear FIT- issue prefix.
0. Domains — DONE 2026-08-22
Registered on Namecheap, delegated to Cloudflare (joan/osmar.ns.cloudflare.com), zones taikan.fit (610d48c6…) and
usetaikan.com (d18f3ef7…) created, SSL mode Full. Records written (§1 table is now the live state), Email Routing enabled on
usetaikan.com with contact@ → leads-worker + catch-all, HTTP→HTTPS and www→root redirect rules copied. Vercel: app./admin./*.taikan.fit,
taikan.fit (308 → app), usetaikan.com + www attached and verified. Railway: api.taikan.fit custom domain created (CNAME target
u0cel7ja.up.railway.app). Cloudflare Pages taikan-docs created with docs.usetaikan.com attached.
Production cutover executed 2026-08-22 15:00 IDT: Clerk on clerk.taikan.fit (pk pk_live_Y2xlcmsudGFpa2FuLmZpdCQ=), Railway prod vars + Vercel web/admin env flipped via scripts/rebrand-cutover-prod.sh, api.taikan.fit validated on Railway via _railway-verify.api TXT and re-proxied (SSL Full). app.taikan.fit, admin.taikan.fit, api.taikan.fit, usetaikan.com all live (still FitKit-branded code until merge).
Still open from this block: (a) Cloudflare Access app “docs” must also cover docs.usetaikan.com + taikan-docs.pages.dev
(Zero Trust → Applications → docs → add the hostnames; the API update was refused — max 5 destinations, drop *.fitkit-docs.pages.dev);
(b) first docs deploy to taikan-docs happens on the next deploy-docs.yml run after merge; (c) _vercel TXT not needed — Vercel verified by team ownership.
0b. (historical) Before anything: buy + park the domains
- Register
usetaikan.comandtaikan.fit. - Add both as zones in the existing Cloudflare account (
FitKit, id74e1f4e4…) and move NS at the registrar. - Rename the Cloudflare account display name to Taikan (dashboard → Manage Account).
Nothing in §1–§8 can be completed before this. Code can ship behind it: every new host is only referenced as a default/fallback or via env.
1. Cloudflare DNS (mirror of today’s fitkit.fit zone, split across two zones)
taikan.fit zone:
| Name | Type | Target | Proxy |
|---|---|---|---|
@ | CNAME | cname.vercel-dns.com | off — only if you want bare taikan.fit to redirect to app; otherwise point at marketing |
* | CNAME | cname.vercel-dns.com | off (minisites wildcard) |
app | CNAME | value Vercel gives for taikan-web | off |
admin | CNAME | value Vercel gives for taikan-admin | off |
api | CNAME | 7u2r2buw.up.railway.app | on, SSL mode Full (same as today — never grey-cloud it) |
_railway-verify.api | TXT | from Railway custom-domain dialog | — |
clerk, accounts, clkmail, clk._domainkey, clk2._domainkey | CNAME | values from Clerk (§5) | off |
_vercel | TXT | from Vercel domain verification | — |
usetaikan.com zone:
| Name | Type | Target | Proxy |
|---|---|---|---|
@ | CNAME | cname.vercel-dns.com (marketing, taikan-marketing) | off |
www | CNAME | usetaikan.com | on (WWW→root redirect rule, copy from fitkit.fit) |
docs | CNAME | taikan-docs.pages.dev | on |
@ | MX ×3 | route1/2/3.mx.cloudflare.net (Email Routing) | — |
@ | TXT | v=spf1 include:_spf.mx.cloudflare.net include:amazonses.com ~all | — |
_dmarc | TXT | v=DMARC1; p=quarantine; rua=mailto:support@usetaikan.com | — |
resend._domainkey, send MX + TXT | from Resend after adding the domain (§7) | — | |
inbound | MX | Resend inbound MX (§7) | — |
google-site-verification | TXT | new value from Search Console | — |
- Email Routing on
usetaikan.com: enable, re-create rulecontact@usetaikan.com → Worker leads-worker, catch-all →saarku@gmail.com. - Keep the
fitkit.fitzone alive for ≥ 12 months: HTTPS redirect rule*fitkit.fit/*→https://usetaikan.com/$1for apex/www,app.fitkit.fit/*→https://app.taikan.fit/$1,api.fitkit.fitnot redirected until the mobile install base is gone (old binaries hardcode it). Keep*.fitkit.fitwildcard → Vercel so legacy minisite hosts keep working (the API accepts both suffixes). - Keep MX + inbound on
fitkit.fituntil no open conversation threads carry@inbound.fitkit.fitreply-to headers (checkconversationstable, ~90 days). - Cloudflare Pages: projects can’t be renamed. Create
taikan-docs, attachdocs.usetaikan.com;.github/workflows/deploy-docs.ymlalready targetstaikan-docs. - R2: buckets can’t be renamed. Prod today:
R2_BUCKET_NAME=vibrafit-exports,R2_COMPLIANCE_BUCKET_NAME=fitkit-compliance, backupsAWS_S3_BUCKET=fitkit-db-backups+BACKUP_FILE_PREFIX=fitkit-prod. Either keep them and setR2_BUCKET_NAME,R2_COMPLIANCE_BUCKET_NAME,R2_BUCKET(Railway, GH secrets, pg-s3-backups) to the old names — recommended, zero data movement — or createtaikan-*,rclone sync, swap the env, and keep the old buckets read-only until stored object URLs are re-signed..env.examplenow saystaikan-dev; the real value is whatever you choose. - Worker
leads-worker: no rename needed (name is internal).
2. Vercel (team pandawi's projects)
- Projects renamed to
taikan-web/taikan-marketing/taikan-admin/taikan-minisites(done via API;*.vercel.apppreview hosts changed accordingly). - Add domains:
app.taikan.fit→ web,usetaikan.com+www→ marketing,admin.taikan.fit→ admin,*.taikan.fit→ minisites. Keep the old domains attached until redirects are live. - Env vars to update on each project (names unchanged):
NEXT_PUBLIC_API_URL,NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY(new key from §5),RESEND_FROM_ADDRESS. Note: the web project has noSENTRY_ORG/SENTRY_WEB_PROJECTvars, sonext.config.jsdefaults apply — after this branch merges they aretaikan/taikan-frontend, which only work once the Sentry org slug is renamed (§5). AddSENTRY_ORG=fitkit1explicitly if you merge before that.VITE_API_URL,VITE_CLERK_PUBLISHABLE_KEY,PUBLIC_API_URL(marketing/minisites),RESEND_FROM_ADDRESS(web feedback),VERCEL_MINISITES_PROJECT_IDunchanged. - Minisite backfill: for every row in
minisite_contentwithsubdomain, call the Vercel domains API once for<subdomain>.taikan.fit(the API only registers the new suffix on the next publish). One-off script:SELECT subdomain FROM minisite_content WHERE subdomain IS NOT NULL AND published=true→POST /v9/projects/$VERCEL_MINISITES_PROJECT_ID/domains {"name": "<slug>.taikan.fit"}. Then re-save each org’s website URL (minisite-website-syncrewrites*.fitkit.fit→*.taikan.fiton publish). - Re-link Git after the GitHub rename (§8) — Vercel follows redirects but the project settings show the old slug.
3. Railway (project FitKit)
- Project and services renamed (
Taikan,@taikan/api,@taikan/api-scheduler). Railway’s auto var is nowRAILWAY_SERVICE__TAIKAN_API_URL— nothing in code referenced the old name. - Custom domain: add
api.taikan.fitto the prod API service, put the verify TXT in DNS (§1), keepapi.fitkit.fitattached. - Preview service: generate a new public domain (
taikanapi-preview.up.railway.app) and updatefitkit-mobile/eas.jsonEXPO_PUBLIC_API_URL/WS_URLin thedevelopment+previewprofiles (still hardcoded tofitkitapi-preview…). - Env vars (prod + preview):
APP_URL/WEB_URL/CORS_ORIGINS→ new hosts,RESEND_FROM_ADDRESS="Taikan <support@usetaikan.com>",INBOUND_EMAIL_DOMAIN=usetaikan.com(prod currently uses the barefitkit.fit, notinbound.),CLERK_SECRET_KEY+CLERK_PUBLISHABLE_KEY(§5),SENTRY_ORG=taikan,SENTRY_PROJECT=taikan-backend,TEST_SEED_SECRET=taikan-e2e-secret(preview only), R2 names per §1,PUBLIC_MINISITE_SUFFIXif you externalise it. -
pg-s3-backups: bucket name per §1 decision; Better Stack heartbeat URL unchanged.
4. Clerk (gates everything else — do in a maintenance window)
Production instance domain change = new frontend API host = new publishable keys everywhere and every signed-in session is dropped.
- Dashboard → Domains → change production domain to
taikan.fit. Clerk issues new CNAMEs forclerk,accounts,clkmail,clk._domainkey,clk2._domainkey→ add to DNS (§1), wait for verification. - Copy the new
pk_live_…/sk_live_…into: Vercel web/admin, Railway API (prod + preview), EAS secrets (EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY), GitHub Actions secretsCLERK_PUBLISHABLE_KEY/CLERK_SECRET_KEY. - Allowed origins / redirect URLs: add
https://app.taikan.fit,https://admin.taikan.fit,https://usetaikan.com,taikan://, keep old for the transition. - OAuth providers (Google etc.): register the new Clerk callback URL (
https://clerk.taikan.fit/v1/oauth_callback) in each provider’s console. - Email templates / branding inside Clerk: app name “Taikan”, logo, sender
noreply@usetaikan.com(Clerk’s own DKIM records above). - Invite flow: tickets redirect to
https://app.taikan.fit/sign-up?__clerk_ticket=…— the mobile intent filters and AASA already list that host.
5. Sentry (org fitkit1, DE region)
- Org settings → rename org to Taikan, slug
taikan(dashboard only), then setSENTRY_ORG=taikanon Railway (prod+preview) + Vercel web +sentry-release.yml. DSNs are id-based and survive;fitkit1.sentry.iolinks in old docs die. - Project slugs renamed. Future short-ids become
TAIKAN-BACKEND-nn; old ones still resolve by id. - Set
SENTRY_ORG=taikan+ project envs on Vercel/Railway/EAS in the same change —next.config.js,sentry-release.ymlandapp.config.tsdefaults already saytaikan/taikan-*(this also fixes the stalefitnx-*slugs the release workflow was silently failing on). - Allowed domains for the JS SDK: add
app.taikan.fit,usetaikan.com. - GitHub integration: re-add the repo after the rename (§8) so commit association works.
6. Mobile: new app fit.taikan.app
This is a brand-new store listing; the FitKit app stays published until you sunset it.
- Expo: create project
taikanunder the same account →eas initinfitkit-mobile→ replaceprojectIdand theupdates.urlinapp.config.ts(both currently carry the FitKit id with aREBRAND:comment). Set EAS secrets (EXPO_PUBLIC_*,SENTRY_AUTH_TOKEN,SENTRY_ORG=taikan,SENTRY_PROJECT=taikan-mobile). - Apple: App Store Connect → new app, bundle id
fit.taikan.app, teamP25F278UF8; enable Associated Domains + Push; new APNs key or reuse the team key; updateeas.jsonsubmit.ios.ascAppId. - Google: new Play app, package
fit.taikan.app; new upload key (or Play App Signing) → new SHA-256 fingerprints → updateapps/web/public/.well-known/assetlinks.json(currently still lists the FitKit fingerprints under the new package name). - Firebase: new project (
fitkit-7b46bis bound to the old package) → downloadgoogle-services.jsonforfit.taikan.app, replace the file (untouched by the rename), upload the FCM server key to Expo push credentials. - Universal links: AASA already lists
P25F278UF8.fit.taikan.app;app.taikan.fitmust serve it over HTTPS before the first TestFlight build. - Store listings: new name “Taikan”, screenshots, privacy labels;
store-assets/play/*copy is already renamed. - Publish
@desmotech/taikan-sharedfirst (§8) then bumpfitkit-mobile/package.json— right now@taikan/sharedis aliased tonpm:@desmotech/fitkit-shared@^0.1.55so the repo installs. - Sunset plan for
fit.fitkit.app: ship one last FitKit OTA/build whose home screen says “we’re now Taikan — install the new app”, keepapi.fitkit.fitanswering for as long as that install base exists, then remove from sale.
7. Resend
- Add
usetaikan.com→ DKIM/SPF/MX records into DNS (§1), verify. - Add
inbound.usetaikan.comas a receiving domain (MX) and point the inbound webhook athttps://api.taikan.fit/<existing path>. - Flip
RESEND_FROM_ADDRESSon Railway + Vercel; keepfitkit.fitverified until the inbound threads drain.
8. GitHub + npm
gh repo rename taikan --repo desmotech/FitKit --yes
gh repo rename taikan-mobile --repo desmotech/FitKit-mobile --yes
gh repo edit desmotech/taikan --homepage https://usetaikan.com --description "Taikan — fitness business OS"
gh secret set TEST_SEED_SECRET --body taikan-e2e-secret # if it is ever promoted to a secret- Repos renamed; main checkouts’ remotes updated. Other worktrees still work via GitHub redirect, but to be tidy:
git remote set-url origin git@github.com:desmotech/taikan.gitin/Users/saar/dev/fitkitand every worktree; same for the mobile checkout. - GitHub Packages cannot rename a package: the next run of
publish-shared.ymlpublishes@desmotech/taikan-sharedas a new package (0.1.56). Then in the mobile repo:"@taikan/shared": "npm:@desmotech/taikan-shared@^0.1.56"+ manual lockfile bump (memory: lockfile bump is manual — the GH registry 404s transitive deps). - Environments
FitKit / preview,FitKit / production→ rename toTaikan / …(dashboard) and update theenvironment:keys in the workflows that reference them. - Branch names/PR titles are fine;
FIT-Linear keys stay.
9. PostHog (org “VibraFit”, slug fitnx, project now “Taikan”)
Done via MCP: project renamed to Taikan, dashboard “FitKit Coach Observability” → “Taikan Coach Observability”.
- Org name → Taikan (dashboard; MCP has no org-update).
- Feature flag
fitkit-managed-payments/taikan-managed-payments: moot — the flag was merged permanently ON and deleted from the codebase on 2026-08-22. Archive whichever key still exists in the PostHog UI; nothing reads it. - Authorized URLs:
https://usetaikan.com,https://app.taikan.fitadded (old kept). - Flag descriptions still say “FitKit prod org” — cosmetic.
10. Better Stack
- Monitors: add
https://usetaikan.com,https://app.taikan.fit/he/sign-in,https://api.taikan.fit/health(keyword) and rename the three existing ones to “(legacy fitkit.fit)” until the redirects are removed. Heartbeat namesFitKit scheduler aliveetc. → rename (URLs/tokens unchanged, no code change).fitkit-db-backupheartbeat is currently down — unrelated, worth a look.
11. Linear
- Workspace name + URL key (
linear.app/fitkit→linear.app/taikan) and team name — dashboard. Keep the team keyFIT(hundreds ofFIT-nnnreferences in commits/docs would break).
12. Local dev after merge
# main checkout: the Makefile/docker-compose/.env.example now say taikan_dev / taikan_test
psql postgresql://postgres:postgres@localhost:5432/postgres -c 'ALTER DATABASE fitkit_dev RENAME TO taikan_dev' -c 'ALTER DATABASE fitkit_test RENAME TO taikan_test'
# then edit apps/api/.env + libs/db/.env DATABASE_URL (untracked, not touched by the rename)
pnpm db:doctorPer-worktree clones from db-clone.sh will now be named taikan_dev_<branch>.
13. Blind spots / verification checklist
- Logo: every
taikan-logo*.svg/pngis the old FitKit artwork under a new filename (path-only SVGs, no text node). A real Taikan mark is needed before the marketing site, emails (https://usetaikan.com/logo/taikan-logo-64.pngis in the letterhead), app icons, Clerk branding and store listings go live. - Client state resets: moving the web app to a new origin drops every cookie/localStorage key (active-org cookie, cookie-consent record, drafts, tour flags) — no shim is possible across origins; expect everyone to re-consent and re-pick an org. Same for mobile: new bundle id = fresh AsyncStorage/SecureStore. That is why no storage-key migration shims were added.
- Cardcom:
TypeOfPlannedServiceKYC text now says “מערכת Taikan” — confirm with Cardcom that the merchant-of-record name on the dealer agreement doesn’t need to match; module idsFITKITPAYMENT*stay. - Morning / invoices: check whether issued documents carry a platform name anywhere (templates, footers).
- Legal: Terms/Privacy/DPA pages on
usetaikan.commust name the legal entity; a rename clause + notice to customers (Israeli Privacy Protection Law requires notifying data subjects of the controller’s identity change if the entity changes, not just the brand). Vendor DPAs (Clerk, PostHog, Sentry, Resend, Railway, Vercel, Cloudflare) are per-account and survive. - Email deliverability: brand-new sending domain = cold reputation. Warm
usetaikan.com(send from it for low-volume transactional first; keep DMARC atp=nonefor two weeks, thenquarantine). - Search / SEO: 301 every
fitkit.fitURL to itsusetaikan.comtwin (marketing slugs are unchanged), new Search Console property + change-of-address, updaterobots.tssitemap host (already points atusetaikan.com), OG images. - Existing members’ bookmarks, QR codes, printed material: QR codes encode
fitkit:///app.fitkit.fit/checkin— both keep working (scheme kept, host redirected). Tell gym owners anyway. - Advisory locks:
fitkit:recurring-charge-cron/fitkit:audit-retention-cronlock ids changed. During the rolling deploy old and new pods hold different locks → one overlapping window where both could run the recurring-charge tick. Deploy the API with a brief scheduler pause (AUTOMATIONS_ENABLED/scheduler scale-to-zero for 1 min) or accept the idempotency guards. TEST_SEED_SECRET: the value moved totaikan-e2e-secretin CI + fixtures; the Railway preview env must match or the full-test gate fails on seed calls.- Test-auth headers
fitkit.test.*→taikan.test.*: web and API renamed together; e2e storage state caches from before the rename are stale — deleteapps/web/e2e/.auth. - Apple Pay / Google Pay merchant ids, Meta Business Verification (WhatsApp work) and Facebook app name: all carry the brand; update when those go live.
- Docs archive (
docs/_archive/**) was renamed too, per “everywhere” — it now reads as if the product was always Taikan. Revert that folder if you want the history intact. - Third-party listings: app store search, G2/Product Hunt if any, LinkedIn company page, email signatures, invoice templates, bank descriptor (“FITKIT” on members’ credit-card statements comes from the Cardcom terminal name — ask Cardcom to change the DBA).