Initialize Your Project
Run yarn init:project to turn the boilerplate into your own app, then let yarn verify:identity prove that none of the template's identity survived.
Turn the boilerplate into your app
A fork of this boilerplate once reached a working feature loop, three merged pull requests and a live backend while still wearing the parent's bundle id, the parent's URL scheme, and a third app's Firebase project. Every test passed. Every build succeeded. Nothing errored, because nothing in the repo was capable of erroring on it. The symptom surfaced weeks later, in a runtime log, when someone worked out that every analytics event that app had ever recorded had been landing in another project the whole time.
So the bootstrap does not stop at "it builds". It ends in a check that reads your tree and fails the run while any of the template's identity is still in it.
yarn init:project rewrites your identity files and runs expo prebuild --clean. Run it once, on a fresh clone, before you have written code you would hate to lose. To see what it does without touching native folders, copy the repo somewhere throwaway and run node init.js --no-native.
These two commands arrive with the init identity bootstrap change, dated 2026-08-22 and written up in ai_rules/migrations/2026-08-22-init-identity-bootstrap.md inside the boilerplate. If yarn verify:identity is not listed in your package.json, pull the latest boilerplate and follow that migration file, which carries the exact steps.
Before you start
Four things. init asks for each one, and three of the four decide whether the final check passes.
| What you need | Why it matters | Where to get it |
|---|---|---|
| A bundle id you own | It goes into app.json as ios.bundleIdentifier and android.package, and Firebase has to be registered against the same string | Reverse DNS on a domain you control, for example com.yourcompany.yourapp |
| Your own Firebase project, with both config files downloaded | init copies google-services.json and GoogleService-Info.plist into the repo root. Skip this and the tree still names the template's project, which is exactly the failure above | Firebase Setup |
| A square source image, 1024x1024 or larger | init resizes it to 1024x1024 for every icon and splash image app.json declares. A directory of prepared assets works too | Icons and Splash |
| An iOS Google OAuth client id, only if you want Google sign in | Leave it blank and the template's GIDClientID key is removed outright. Google sign in stays off until you add your own, which beats silently pointing at somebody else's OAuth client | Firebase console, iOS app, OAuth client |
Register both the iOS bundle id and the Android package in Firebase before you run init. The check compares the package name inside each config file against app.json, and a config exported before you registered the right identifier will fail it.
Run it
yarn init:projectSeven steps, in this order.
| Step | It asks for | It writes |
|---|---|---|
| 1. Record | nothing | .init-parent-identity.json, the identity you forked from, captured before anything is rewritten |
| 2. App idea | name, one line pitch, category | ai_rules/app_approach/product_description.md, and only while that file is still byte identical to the committed template. Author it yourself first and this step skips instead of overwriting you |
| 3. Identity | bundle id, Android package, URL scheme, iOS Google client id | app.json name, slug, scheme, ios.bundleIdentifier, android.package, ios.infoPlist.CFBundleURLTypes, ios.infoPlist.GIDClientID, plus AUTH_SCHEME in src/features/auth/constants/index.ts |
| 4. Firebase | paths to your two config files | copies both into the repo root |
| 5. Brand colors | a preset, or your own hex | the ten accent* literals in src/ui/tokens/colors.ts, plus expo.android.adaptiveIcon.backgroundColor |
| 6. Icon and splash | a square image, or a directory of prepared assets | every image path app.json declares |
| 7. Verify | nothing | nothing. It reads, and a non zero exit stops the run |
Then, unless you passed --no-native, it runs yarn install, expo prebuild --clean, and verifies a second time. That second pass matters: prebuild writes ios/ and android/, and those trees carry their own copies of the Firebase configs and the bundle id.
The color presets
midnight-lime, warm-sand, deep-violet, mono-ink, forest (the boilerplate default), or custom to supply your own light and dark hex.
Each preset is a starting point for the accent ramp, nothing more. init writes accent tokens and stops there, because the semantic map in src/ui/style/colors-theme.ts already dereferences them. For the full design system, typography, spacing, radii and motion, run the design system generator afterwards. See Claude Design.
Icon resizing uses what is already on your machine
From a single square image, init tries macOS sips first, then Python's Pillow. No new dependency is added for this. If it finds neither, it says so and asks you for a directory of prepared assets instead, named to match what app.json declares.
The check that ends the run
scripts/verify-identity.js is the reason this page exists. It is executable, it runs at the end of init, and you can run it on its own any time:
yarn verify:identitySeven things get checked.
app.jsondeclares a scheme, an iOS bundle id and an Android package.AUTH_SCHEMEequalsapp.json'sexpo.scheme. A stale one breaks OAuth redirects quietly.expo.schemeis registered inCFBundleURLTypes, so a deep link opens your app and not the template's.- Every Firebase config in the tree names one single project id.
- Each Firebase config's bundle id and package name match
app.json. This is the check that catches an app running as one identity and reporting as another. - The icons and splash images
app.jsondeclares are not byte identical to the template's. - None of the recorded parent identity strings survive anywhere in
app.json, either Firebase config,src/features/auth/constants/index.ts, or the generatedios/andandroid/trees.
Findings are reported by record key and file:line, never by value, because a couple of those strings are credential adjacent.
Exit codes:
| Code | Meaning |
|---|---|
| 0 | clean |
| 1 | at least one check failed |
| 2 | a check could not run, which is treated as a failure and never as a pass |
Why the check does not go stale
The template's identity strings are not hardcoded anywhere in the verifier. Step 1 of init reads your tree before it rewrites anything and records what it found to .init-parent-identity.json. The verifier then asserts the absence of exactly those strings, so it stays correct for any future variant of the boilerplate without anyone maintaining a list.
Do not delete .init-parent-identity.json. Without it the absence sweep and the icon check cannot run, and the verifier exits 2 rather than reporting a clean pass on a check it never performed. The file is gitignored on purpose, since it is per fork and some of its values are credential adjacent.
If you are working in the template repo itself, and it is meant to wear the template identity, skip the absence sweep explicitly:
node scripts/verify-identity.js --templateThat still runs the positive checks: scheme parity, Firebase project consistency, and the bundle id match.
You are done when the check passes
Not when it builds. Building was never the thing that broke.
-
yarn verify:identityexits 0 on the source tree - It still exits 0 after
expo prebuild --clean, withios/andandroid/on disk -
.init-parent-identity.jsonexists and is not committed - Your Firebase console shows events arriving in your project, not somebody else's
Prove the check can actually fail before you trust a green one. On a throwaway copy, change AUTH_SCHEME in src/features/auth/constants/index.ts to something else, run yarn verify:identity, and watch it exit 1 and name the check. Then put it back.
Non interactive runs
Useful in CI, or when you want the same bootstrap twice.
node init.js --answers answers.json
node init.js --answers answers.json --no-nativeanswers.json accepts these keys: projectName, pitch, category, iosBundleIdentifier, androidPackage, scheme, googleClientId, googleServicesJson, googleServiceInfoPlist, theme, brandColorLight, brandColorDark, vibe, radius, iconSource, platform.
--no-native skips yarn install, expo prebuild --clean and the app launch, so only the source tree is touched and only the first verify pass runs.
Common issues
| What you see | What it means |
|---|---|
no .init-parent-identity.json and exit 2 | The record is missing, so the absence sweep cannot run. Re run yarn init:project, or pass --template if this really is the template repo |
AUTH_SCHEME does not equal app.json expo.scheme | Your scheme changed in app.json but not in src/features/auth/constants/index.ts. OAuth redirects will break |
BUNDLE_ID does not match app.json | Your Firebase config was exported for a different identifier. Register the right bundle id and package in Firebase and download both files again |
is byte-identical to the template's | That icon or splash image is still the template's. Re run the icon step with your own artwork |
no image tool available | Neither sips nor Python's Pillow is on this machine. Pass a directory of prepared assets instead of a single image |
WARNING: no icon source given | You skipped the icon step. It is a warning at that moment and a failure at step 7 |
Next
With the app wearing its own identity, carry on to Tool Integration for Supabase and RevenueCat, then First App Launch for environment variables and your first run.