diff --git a/server/public/home.html b/server/public/home.html
index a5a9c8f..3a5ab46 100644
--- a/server/public/home.html
+++ b/server/public/home.html
@@ -4234,6 +4234,15 @@ let _nativeTok=null;
async function setupNativePush(){
const PN=capPlugin('PushNotifications'); const plat=nativePlatform();
if(!PN || (plat!=='ios' && plat!=='android')) return false; // not a mobile native app
+ // Android push needs Firebase: google-services.json in the build AND FCM on the server. Without it,
+ // PushNotifications.register() throws "Default FirebaseApp is not initialized" — an UNCAUGHT NATIVE crash
+ // (thrown on Capacitor's plugin thread; a JS try/catch can't stop it, it kills the app on every launch).
+ // So on Android, only register when the server reports FCM is configured (we add google-services.json to the
+ // build + FCM_SERVICE_ACCOUNT to the server together). iOS (APNs) is unaffected and registers as before.
+ if(plat==='android'){
+ let cfg={}; try{ cfg=await fetch('/api/meetings/config').then(r=>r.json()); }catch(_){}
+ if(!cfg || !cfg.fcm){ console.log('[push] Android FCM not configured on the server — skipping native push registration (avoids the FirebaseApp-not-initialized crash)'); return true; }
+ }
try{
PN.addListener('registration', async (t)=>{ const token=t&&t.value; if(!token) return; _nativeTok=token;
try{ await postJSON('/api/v1/devices',{ platform:plat, token }); pushActive=true; console.log('[push] native device registered'); }
diff --git a/server/push.js b/server/push.js
index 335daef..33e5691 100644
--- a/server/push.js
+++ b/server/push.js
@@ -179,6 +179,10 @@ console.log(enabled.length ? '[push] enabled: ' + enabled.join(', ') : '[push] d
function isEnabled() { return webReady; } // Web Push specifically (drives /api/push/vapid)
function publicKey() { return webReady ? PUBLIC : ''; }
+// Whether Android FCM is configured on the server. The Android app must NOT call PushNotifications.register()
+// unless Firebase is set up (client google-services.json + this) — otherwise it throws "Default FirebaseApp is
+// not initialized", an uncaught NATIVE crash. The web uses this flag to gate Android push registration.
+function fcmReady() { return !!fcmSA; }
// Fire-and-forget to every channel the user has: Web Push subscriptions + native device
// tokens. Dead endpoints/tokens are pruned. Never throws.
@@ -207,4 +211,4 @@ async function sendToUser(userId, payload) {
}
}
-module.exports = { isEnabled, publicKey, sendToUser, sendCallNotification, sendCallCancel };
+module.exports = { isEnabled, publicKey, sendToUser, sendCallNotification, sendCallCancel, fcmReady };
diff --git a/server/routes.js b/server/routes.js
index 4b6eff4..fae99c3 100644
--- a/server/routes.js
+++ b/server/routes.js
@@ -993,7 +993,7 @@ route('POST', '/api/calls/invite', async (req, res) => {
// Does this deployment use the LiveKit SFU for meeting media? The client asks on load; if sfu is
// false it uses the built-in P2P mesh. url is the browser-facing signaling endpoint (wss://…).
route('GET', '/api/meetings/config', (req, res) => {
- json(res, 200, { sfu: LIVEKIT_ENABLED, url: LIVEKIT_ENABLED ? LIVEKIT_URL : '', callkit: CALLKIT_ENABLED });
+ json(res, 200, { sfu: LIVEKIT_ENABLED, url: LIVEKIT_ENABLED ? LIVEKIT_URL : '', callkit: CALLKIT_ENABLED, fcm: PUSH.fcmReady() });
});
// #5 GIF search — server-side GIPHY proxy so the API key never reaches the browser. Empty q → trending.