The short version
- Firebase is fully managed and proprietary. It runs only on Google Cloud. There is no self-hosted or open-source version of the backend.
- Concile is open source under FSL-1.1-Apache-2.0. It runs wherever you put it. You can use your hardware, a VPS, a container, a single binary, or a Cloudflare Worker.
- Firebase's realtime model uses document listeners and a rules language. Concile uses live queries over server-side functions. Authorization happens in code.
Side by side
| Concile | Firebase | |
|---|---|---|
| Live results from your own server code | Yes, read-set precise | Partly, document listeners |
| Backend is plain TypeScript functions | Yes | Partly, Cloud Functions |
| Authorization is ordinary code, not a rules language | Yes | No, Security Rules DSL |
| Self-host the whole product | Yes | No, Google Cloud only |
| Single binary with no database to run | Yes | No |
| Swap the database without touching app code | Yes, SQLite or Postgres | No, Firestore or Realtime Database |
| Durable workflows with rollback | Yes, saga compensation | No, bring your own |
| Offline writes that survive a reload | Yes, durable outbox | Yes, Firestore persistence |
| Full-text and vector search | Not built yet | Partly, vector only; text needs an add-on |
| Hosted cloud | None | Yes, only |
| Price | Free to self-host | Spark free; Blaze pay as you go, metered per service |
Realtime: listeners versus live queries
Firestore realtime uses onSnapshot(). You attach a listener to a document or a query. You get an initial snapshot right away. You get a new snapshot on every change. Local writes trigger the listener immediately through latency compensation. This happens before the backend confirms them. The metadata.hasPendingWrites property tells you which state you are viewing. It is a good model with documented limitations. Pipeline operations do not support realtime listeners. The PHP client library does not support them at all.
In Concile, you do not attach listeners. A query is a subscription. The server records what it read. It re-runs the read when a commit touches that data. The server then pushes the new result. Optimistic updates work here too. A durable outbox means an offline write survives a page reload. It replays exactly once.
Authorization: rules versus code
Firebase Security Rules live outside your app. You manage them in the console or the CLI. They combine a path match with a condition. Firestore and Cloud Storage use a service/match/allow if block. Realtime Database uses a separate JSON .read/.write format. Rules combine as OR. If any matching rule grants access, access is granted. The documentation notes this as something to design around.
In Concile, the client never touches the database. Every read and write goes through a function you wrote. This happens in a transaction. The authorization check is a line of TypeScript in that function. It is type-checked with the app. It is unit tested with the app.
Where Firebase is ahead
- Nobody runs anything. Firebase is fully managed on Google's infrastructure. If you do not want a server, Firebase does not give you one.
- Offline persistence in Firestore is mature. It comes with the client SDKs.
- Firebase offers a no-cost Spark tier. It offers a pay-as-you-go Blaze tier. You can get $300 in free credit eligibility.
- Vector search is available. Full-text search requires an add-on.
Where Concile is ahead
- You own it. Concile is open source and self-hosted. It does not phone home. It is portable across a binary, a container, your own Postgres, or Cloudflare Workers. Firebase has only one hosting path.
- Live queries compute on the server. Clients do not reason about listeners.
- Authorization lives in ordinary code. It runs in the same transaction as the data access.
- You get one bill. That bill is zero. Firebase meters Firestore, Realtime Database, Functions, Auth and Hosting separately. Each service has its own free threshold and overage rate.
- Durable workflows use compensation. Cron jobs and a dashboard run in the same process.
Moving from Firebase
There is no automatic migration. You export your Firestore collections. You define the same shapes in schema.ts. You import with concile migrate import. Each Security Rule becomes a check inside the function that reads or writes data. Client code changes from onSnapshot() to useQuery(). It changes from direct document writes to useMutation() calls.
When to pick Firebase
You want a fully managed backend from Google. You want first-party mobile SDKs. You never want to run a server. You accept that Google Cloud is the only place it lives.
When to pick Concile
You want to own the backend. You want to run it anywhere. You want to write authorization as code. You want live queries without listener bookkeeping. You prefer one process over a set of metered services.