Describe VITE_DEFAULT_NOTIFY_API_SERVER / DEFAULT_NOTIFY_API_SERVER, the debug-override resolution order, and replace outdated APP_SERVER assumptions in build and notification testing docs.
12 KiB
Notification Debug Panel
Created: 2026-07-07
Updated: 2026-07-22
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.
Notification API base URL
Notification HTTP calls (/notifications/register, /notifications/refresh, /debug/send-wakeup, etc.) do not use APP_SERVER. They use a dedicated Notification API host, resolved at runtime by getNotificationApiBaseUrl() in NotificationDebugConfig.ts.
Configuration constants
| Symbol | Location | Purpose |
|---|---|---|
VITE_DEFAULT_NOTIFY_API_SERVER |
.env.development / .env.test / .env.production |
Build-time default Notification API URL for that Vite mode (same pattern as other VITE_DEFAULT_* backends) |
DEFAULT_NOTIFY_API_SERVER |
src/constants/app.ts |
Runtime constant: import.meta.env.VITE_DEFAULT_NOTIFY_API_SERVER || AppString.PROD_NOTIFY_API_SERVER |
AppString.PROD_NOTIFY_API_SERVER |
src/constants/app.ts |
Hardcoded production fallback: https://notify-api.timesafari.app |
AppString.TEST_NOTIFY_API_SERVER |
src/constants/app.ts |
Hardcoded test host: https://test-notify-api.timesafari.app (for explicit UI/debug use; not the automatic fallback) |
Production, test, and development builds get different Notification API URLs from their respective .env.* files. Runtime request code always goes through DEFAULT_NOTIFY_API_SERVER (via getNotificationApiBaseUrl()), not by reading the env var directly at each call site.
Typical values today:
| Build / env file | VITE_DEFAULT_NOTIFY_API_SERVER |
|---|---|
.env.production |
https://notify-api.timesafari.app |
.env.test |
https://test-notify-api.timesafari.app |
.env.development |
https://test-notify-api.timesafari.app |
URL resolution order
getNotificationApiBaseUrl() selects the base URL in this order:
- Debug Panel backend override —
localStoragekeynotificationDebug.backendBaseUrl(set via Save Backend URL orsetBackendBaseUrl()) VITE_DEFAULT_NOTIFY_API_SERVER— baked into the build as part ofDEFAULT_NOTIFY_API_SERVERAppString.PROD_NOTIFY_API_SERVER— hardcoded fallback when the env var is unset (https://notify-api.timesafari.app)
Clearing the Debug Panel override (empty field + Save) returns the app to step 2 / 3 (DEFAULT_NOTIFY_API_SERVER). The override never changes auth behavior by itself.
APP_SERVER / VITE_APP_SERVER remain for deep links and the main app web host only — not for notification API traffic.
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 DEFAULT_NOTIFY_API_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 configured build default (DEFAULT_NOTIFY_API_SERVER, from VITE_DEFAULT_NOTIFY_API_SERVER or AppString.PROD_NOTIFY_API_SERVER). The Debug Panel override still wins whenever a non-empty URL is saved.
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
On development and test builds, this host is already the default via VITE_DEFAULT_NOTIFY_API_SERVER. You can leave Notification Backend URL empty, or paste the same URL explicitly.
| Setting | Value |
|---|---|
| Notification Backend URL | Empty (use default) or 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/constants/app.ts |
DEFAULT_NOTIFY_API_SERVER, PROD_NOTIFY_API_SERVER, TEST_NOTIFY_API_SERVER |
src/components/dev/NotificationDebugPanel.vue |
Dev UI |
src/services/notifications/NotificationDebugConfig.ts |
Base URL resolution, 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 |