Add doc/notification-debug-panel.md as the canonical panel reference. Update ngrok guides, README, and analysis doc to describe independent Backend URL, Test Mode, and Skip JWT Authentication settings, including recommended configs for hosted test servers vs local ngrok.
9.0 KiB
Notification Debug Panel
Created: 2026-07-07
Audience: Developers testing notification registration, refresh, and WAKEUP_PING flows on native (iOS/Android) dev builds.
The Notification Debug Panel is a dev-only UI for exercising the same notification orchestration paths the production app uses: FCM token registration, backend refresh, wakeup handling, and local schedule inspection. It does not duplicate scheduling logic.
Access
- Use a non-production build (for example
build:android:dev,build:ios:dev, orvite devwith a non-productionmode). - Open Account → enable Show All General Advanced Functions.
- Open Notification Debug Panel (route
/dev/notifications).
On native platforms, grant notification permission when prompted so FCM token registration and the debug actions work.
Configuration (Backend Testing)
Settings persist in localStorage via NotificationDebugConfig.ts:
| Key | Default | Purpose |
|---|---|---|
notificationDebug.backendBaseUrl |
(unset — use APP_SERVER) |
Override which notification server receives API calls |
notificationDebug.testMode |
true |
Sent in JSON request bodies (testMode: true/false) |
notificationDebug.bypassAuth |
false |
When true, omit JWT Authorization headers on notification API calls |
All notification API requests (/notifications/register, /notifications/refresh, /debug/send-wakeup, etc.) obtain headers through getNotificationApiHeaders() in notificationApiAuth.ts.
Notification Backend URL
Paste a base URL (no trailing slash) and tap Save Backend URL. This changes only which server the app calls (getNotificationApiBaseUrl()). It does not disable JWT authentication.
Leave empty to use the built-in default (APP_SERVER from VITE_APP_SERVER).
Test Mode
When enabled (default if never saved), register and refresh requests include "testMode": true in the JSON body. The backend can use this to return dev-friendly schedules or route test traffic separately from production.
Test Mode is independent of authentication. It does not control whether Authorization headers are sent.
Skip JWT Authentication (Local Development Only)
When off (default), the app resolves the active DID and sends Authorization: Bearer … on notification API calls.
When on, requests include only Content-Type: application/json — for local servers (localhost or ngrok) that intentionally accept unauthenticated notification requests during development.
Enable this only for local development backends that do not require JWT. Hosted shared test servers that require normal app authentication should leave this off.
The panel Backend Status section shows the active URL, testMode, and bypassAuth values.
Recommended settings
Hosted test server
Example: https://test-notify-api.timesafari.app
| Setting | Value |
|---|---|
| Notification Backend URL | https://test-notify-api.timesafari.app |
| Test Mode | ON (if the server expects testMode: true) |
| Skip JWT Authentication | OFF |
Ensure the app has an active identity (DID) with a valid endorser session so JWT headers can be built.
Local localhost / ngrok development
Example: https://abc123.ngrok-free.app or http://127.0.0.1:3000
| Setting | Value |
|---|---|
| Notification Backend URL | Your local or ngrok URL |
| Test Mode | ON or OFF — match what your local notification-wakeup-service expects |
| Skip JWT Authentication | ON only if the local server accepts unauthenticated requests; OFF if it validates JWT like production |
Backend Testing actions
| Action | What it does |
|---|---|
| Register Token Now | POST {backend}/notifications/register with current FCM token, deviceId, platform, and testMode. Forces re-registration (bypasses duplicate-token skip). |
| Refresh Notifications | POST {backend}/notifications/refresh — same path used after a real WAKEUP_PING. Applies returned schedule to the native plugin. |
| Simulate WAKEUP_PING (Local) | Calls the refresh API directly (no FCM). Quick test of backend URL + auth + refresh parsing without push delivery. |
| Send Real WAKEUP_PING | POST {backend}/debug/send-wakeup; server sends a real FCM data message with data.type = "WAKEUP_PING". Exercises backend → FCM → Capacitor listener → refresh → reschedule. Background the app before expecting delivery. |
Current FCM Token displays the last token from Capacitor/Firebase registration. Event Log shows the last 100 [Notifications] messages (also visible in logcat / Xcode console on native).
Other panel sections
| Section | Purpose |
|---|---|
| Mock Timing Presets | Interval for mock refresh timestamps (30 sec – 10 min). |
| Trigger Mock Refresh | Applies synthetic future timestamps locally — no backend call. |
| Wakeup Ping Simulator | Runs the production push handler with a synthetic WAKEUP_PING payload (no FCM, no backend). |
| Flood Test | Runs 20 sequential mock refreshes (stress test). |
| Pending Notification Inspector | Lists locally scheduled notifications (iOS; Android may show unavailable). |
| Clear Notifications | Clears/cancels all plugin-scheduled notifications on native. |
Programmatic override (optional)
From a WebView dev console (chrome://inspect on Android, Safari Web Inspector on iOS):
import {
setBackendBaseUrl,
setTestMode,
setBypassAuth,
getNotificationApiBaseUrl,
} from "@/services/notifications";
setBackendBaseUrl("https://abc123.ngrok-free.app");
setTestMode(true);
setBypassAuth(true); // local dev only
getNotificationApiBaseUrl();
Troubleshooting
401 Unauthorized (registerToken failed: unauthorized)
Likely causes: JWT required but Skip JWT Authentication is off and the session is missing or expired; or JWT sent but the server rejected it.
Checks:
- Panel Backend Status →
bypassAuth: falsefor hosted servers. - App has an active DID and endorser login.
- Event Log: look for
Using authenticated notification requestvsUsing debug unauthenticated notification request. - For hosted test server: keep Skip JWT Authentication OFF.
Fixes: Sign in / restore identity; refresh endorser session; for local ngrok without JWT support, enable Skip JWT Authentication.
registerToken auth unavailable / Waiting for auth before registration
The app deferred registration because JWT could not be built (no active DID or empty token) and Skip JWT Authentication is off.
Fixes: Complete identity setup in the app, or enable Skip JWT Authentication only for an intentionally unauthenticated local backend.
Failed to fetch / network error
Likely causes: Backend down, wrong URL, stale ngrok tunnel, device offline, or TLS/certificate issues.
Checks: Panel Backend Status URL; curl -sS "$URL/health" from your machine; ngrok inspect UI for incoming requests.
Fixes: Restart backend and ngrok; Save Backend URL with the current HTTPS forwarding URL (no trailing slash).
Backend unreachable / no requests in ngrok
Same as above. Confirm the Active URL in the panel matches your running tunnel or local server port.
Register succeeds but Send Real WAKEUP_PING does not trigger refresh
Real WAKEUP_PING success only means the backend accepted the wakeup request and attempted FCM delivery. Missing pushNotificationReceived / Refresh completed (WAKEUP_PING) indicates an FCM delivery or background execution issue — not necessarily a bad wakeup API call.
Checks: App backgrounded (not force-stopped); FCM token matches registration; Simulate WAKEUP_PING (Local) works (isolates FCM from refresh API).
See platform-specific guides for extended ngrok and FCM workflows:
Key source files
| File | Purpose |
|---|---|
src/components/dev/NotificationDebugPanel.vue |
Dev UI |
src/services/notifications/NotificationDebugConfig.ts |
Backend URL, testMode, bypassAuth persistence |
src/services/notifications/notificationApiAuth.ts |
JWT vs unauthenticated headers |
src/services/notifications/notificationApiDebugMode.ts |
Auth bypass gate |
src/services/notifications/NotificationDebugService.ts |
Panel action handlers |
src/services/notifications/NotificationService.ts |
POST /notifications/register |
src/services/notifications/NativeNotificationService.ts |
POST /notifications/refresh, WAKEUP_PING handler |