# Tutorial: move a Firebase app to Sovaria
Source: https://sovaria.in/docs/tutorial-migrate-from-firebase

A step-by-step move of a Firebase app: export Auth with its password hashes, import Firestore and Storage, turn security rules into policies, keep your Firestore code with an import change, and move Cloud Functions.

This walks through a whole move, from exporting users to switching traffic. The importer's details are in [Migrate from Supabase or Firebase](https://sovaria.in/docs/migrate-from-supabase-or-firebase#firebase); this page is the order to do it in, and how to check each step.

## 1. Prepare

- Create the Sovaria project, install the CLI and sign in: `npm install -g @sovaria/cli`, `sov login`, `sov scope pick`.
- Create a Firebase **service account** with read access to Firestore and Storage, and download its JSON (`sa.json`).
- Copy the **password hash parameters**: Firebase console › Authentication › Users › ⋮ › Password hash parameters, saved as `hash.json`.
- Copy your `firestore.rules` file.

Export users:
```bash
firebase auth:export users.json --format=json --project YOUR_PROJECT
```

hash.json:
```json
{"base64_signer_key": "…", "base64_salt_separator": "Bw==", "rounds": 8, "mem_cost": 14}
```

## 2. Dry run, then import

Terminal:
```bash
sov import firebase --service-account sa.json \
  --auth-export users.json --hash-config hash.json --rules firestore.rules --dry-run

sov import firebase --service-account sa.json \
  --auth-export users.json --hash-config hash.json --rules firestore.rules
```

- Auth goes first, so the importer can write `sovaria-import-uid-map.json` (old Firebase uid → new Sovaria user id) and replace old uids inside your documents, ids and paths. `users/{uid}` is still each user's own document. Turn that off with `--keep-uids` only if your app stores uids somewhere the importer can't see.
- Every collection and subcollection keeps its path. Document ids with characters Sovaria doesn't allow are cleaned and listed in the report.
- Passwords keep working: Firebase's scrypt hash is checked on each user's first sign-in, then upgraded. Nobody has to reset.

## 3. Turn security rules into policies

`sovaria-rules-report.md` lists every rule with its Sovaria equivalent: public, signed-in users, owner only, or a custom policy. Nothing is applied automatically. Add the policies under **Policies** (or with `sov policy upsert`), then prove them with a simulation.

firestore.rules (before):
```text
match /users/{uid}/orders/{orderId} {
  allow read, write: if request.auth != null && request.auth.uid == uid;
}
```

Sovaria row policy (after):
```sql
-- Documents are rows of sov_documents; collection is the full path, e.g. users/<user id>/orders.
-- Imported documents record the importing team member as owner_id, so check the path instead.
create policy user_orders_own on sov_documents for all
  using (collection = 'users/' || auth.uid()::text || '/orders')
  with check (collection = 'users/' || auth.uid()::text || '/orders');
```

Check:
```bash
sov sim run --fail-on critical
```

> **Tip:** Documents are closed to app users until a rule allows them. More patterns (public catalogues, role checks, per-collection policies under **Policies**) are in [Documents](https://sovaria.in/docs/documents-overview) and [Security recipes](https://sovaria.in/docs/security-recipes). The simulation shows, for each kind of caller, what your new rules allow, so you can compare it with what your Firebase rules allowed.

## 4. Check that everything arrived

- Compare document counts for your main collections: `getCountFromServer` on both sides, or the Collections page.
- Sign in as a test user with their old password through the SDK (`await sov.auth.signIn(email, oldPassword)`).
- Open a few documents you know well and check nested maps, arrays and timestamps. Times are stored as ISO-8601 strings ([Firebase-style code (Firestore API)](https://sovaria.in/docs/firestore-compat#differences)).
- Compare Storage file counts per folder.

## 5. Switch the app

Before:
```ts
import { initializeApp } from 'firebase/app'
import { getAuth, signInWithEmailAndPassword } from 'firebase/auth'
import { getFirestore, collection, query, where, onSnapshot } from 'firebase/firestore'

const app = initializeApp(firebaseConfig)
await signInWithEmailAndPassword(getAuth(app), email, password)
const db = getFirestore(app)
```

After:
```ts
import { SovariaSDK } from '@sovaria/sdk'
import { getFirestore, collection, query, where, onSnapshot } from '@sovaria/sdk/firestore'

const sov = new SovariaSDK({ projectUrl: 'https://<project-ref>.sovarias.in', anonKey: '<anon-key>' })
await sov.auth.signIn(email, password)
const db = getFirestore(sov)
// Your Firestore queries, listeners, batches and transactions below stay as they are.
```

- Firestore code keeps working with the import change ([Firebase-style code (Firestore API)](https://sovaria.in/docs/firestore-compat)). Auth and Storage calls move to the Sovaria SDK.
- Flutter, iOS and Android apps use the native SDKs ([Mobile apps](https://sovaria.in/docs/mobile-overview)). Their collection APIs follow Firestore's, but the calls change, so plan time for it.
- Offline persistence works the Firebase way on the web ([Offline-first apps](https://sovaria.in/docs/offline-first)).

## 6. Move Cloud Functions

| Firebase | Sovaria |
|---|---|
| `onDocumentCreated('orders/{id}', …)` | A trigger on `db.document`, resource `orders`, event `document.created` ([Event triggers](https://sovaria.in/docs/function-triggers)). |
| `onCall` / `onRequest` | A function called with `sovaria.functions.call` or over `POST /functions/v1/<name>` ([Functions](https://sovaria.in/docs/functions-overview)). |
| `onSchedule` | A function schedule ([Event triggers](https://sovaria.in/docs/function-triggers)). |
| `beforeUserCreated` / `beforeUserSignedIn` | Blocking auth hooks ([Blocking hooks (before create, before sign-in)](https://sovaria.in/docs/auth-blocking-hooks)). |
| `onObjectFinalized` | A trigger on `storage.object`, event `object.created`. |
| Firebase Admin SDK in functions | Your project API with a server key from the function's secrets ([Deploying and running functions](https://sovaria.in/docs/functions-deploy-and-run#data)). |

## 7. Cut over

- The import is a snapshot. Plan a short write freeze, re-import the data into a fresh project, check counts, then release the app version that points at Sovaria.
- Users signed in to the old app have to sign in once more. Their passwords still work.
- Keep the Firebase project read-only for a week or two before you delete it.
