Push workflow
Push is not a single display action. Permission, device registration, application-server registration, foreground event handling, and removal are separate stages. This page shows how those stages fit together.
The workflow below is implemented by the push runtime binding and
sercrod.push adapter.
1. Smallest useful declaration
<p *push="/api/push"></p>
A non-clickable base declaration will perform the workflow in order:
- Check notification permission.
- Register the app or browser with its push provider.
- Send the normalized registration to the application's
/api/pushendpoint. - Start the default foreground receive/action watch.
/api/push is the application's push registration
endpoint. It is not an FCM/APNs sending endpoint and is not the
Web Push subscription's own subscription.endpoint.
Use <p *push></p> when registration must remain
device-side and no application endpoint should receive it.
2. Browser fallback
Capacitor can invoke the native OS permission request during automatic
setup. A browser may require a click before showing its permission prompt.
When a click can continue setup, Sercrod reports
$push.needs_action === true.
<p *push="/api/push"></p>
<button
*if="$push && $push.needs_action"
*push.register>
Enable notifications
</button>
The application owns the visible explanation and control. Sercrod does not
insert fallback copy, localization, layout, or buttons. A denied permission
uses $push.status === "denied" with
$push.needs_action === false; an application cannot select
Allow for the user.
3. Choose the shortest test path
- Capacitor Android registration: use
app/push, addgoogle-services.json, build, and confirm the FCM token. - Browser FCM registration: use
app/push, add the public Web app configuration and public VAPID key, then register over HTTPS. - Complete server delivery: use
app/push-server, test the upper Node.js path, then test PHP only after Node works.
An FCM registration token confirms provider registration; it does not by itself confirm server storage or delivery.
4. One application registration
Provider registration is application-wide, not owned by one
<serc-rod>. Multiple declarations and controls with the
same effective configuration reuse the in-progress or completed
registration.
<p *push="/api/push"></p>
<serc-rod>
<button *push.register>Enable notifications</button>
</serc-rod>
<serc-rod>
<button *push.register>Enable notifications here too</button>
</serc-rod>
Two different active registration endpoints are a configuration conflict; they are not silently combined. The initial design supports one active application registration endpoint. A different endpoint may be used after a complete unregister.
5. Application endpoint contract
{
"action": "register",
"registration": {
"platform": "android",
"provider": "fcm",
"token": "..."
}
}
Browser Web Push sends its normalized subscription information instead. Client deduplication reduces repeated handoff, but the application endpoint must still handle repeated registration safely. Provider accounts, private sending credentials, token storage, recipients, and server-side delivery remain application responsibilities.
6. Receive events in the running page
An unnamed declaration is the default watch:
<section
*push.watch
*response="'notification'">
<p *if="notification">
%notification.notification.body%
</p>
</section>
Background display remains an OS, service-worker, FCM, or APNs
responsibility. *push.watch handles events delivered to the
running page.
Use names only when local consumers must be managed independently:
<header
*push.watch="'header'"
*response="'header_notification'">
<p *if="header_notification">
%header_notification.notification.title%
</p>
</header>
<main
*push.watch="'messages'"
*response="'message_notification'">
<p *if="message_notification">
%message_notification.notification.body%
</p>
</main>
Names identify local consumers; they do not filter notification categories. Without an application-defined filter, every watch receives every normalized event.
7. Clear watches
<button *push.clear>Stop the default watch</button>
<button *push.clear="'messages'">
Stop the messages watch
</button>
The declared name is the watch identifier. Sercrod retains the associated
native or browser listener handle internally. Watch names are unique within
their owning <serc-rod>.
8. Unregister
<button *push.unregister>
Disable notifications
</button>
When an application endpoint is active, Sercrod asks it to remove the saved registration first, then unregisters the provider subscription. If server removal fails, the provider registration remains available for retry. Without an application endpoint, unregister is device-side only.
9. Try the Capacitor Android sample
The distribution includes app/push, ready for the shared
Windows Capacitor builder.
- Install Android Studio, its Android SDK, Node.js, and MSYS2 as required by
app/build.bat. - In Firebase, create an Android app with the Capacitor App ID. A new sample uses
com.sercrod.push. - Save the downloaded file as
app/push/google-services.json. - Connect an Android device with USB debugging enabled.
- Run
app/build.bat, selectpush, and choose whether to synchronize the public samples first. - Open the installed app. The token shown under Registration confirms client registration.
The builder installs @capacitor/push-notifications, creates
and synchronizes Android, copies google-services.json into
android/app, builds the debug APK, and installs it.
Receiving a message additionally requires sending through Firebase or
your provider server.
For a custom Capacitor project:
npm install @capacitor/push-notifications
npx cap sync
On Android, the app-level google-services.json remains
required. On iOS, add the iOS platform, enable the
Push Notifications capability in Xcode, add Capacitor's
current remote-notification forwarding methods to
AppDelegate.swift, and test on a signed physical device.
10. Try browser FCM
Browser FCM uses a Firebase Web app, not Android's google-services.json.
- Add a Web app in Firebase project settings and copy its public
firebaseConfig. - Create a Web Push certificate under Cloud Messaging and copy its public VAPID key.
- Put those public values in
app/push/www/firebase-config.js. - Serve over HTTPS, activate registration, grant permission, and confirm
platform: "web"andprovider: "fcm".
The Web configuration and VAPID public key are browser-visible by design.
A service-account JSON file, its private_key, APNs private key,
and administrator password are server-only. Never paste or place them in
HTML, chat, an app bundle, a public directory, or the repository. Revoke an
exposed private key before continuing.
11. Try server delivery
The app/push-server sample
uses one browser/Capacitor client with Node.js above PHP. Test Node first.
- For Capacitor, add Firebase Android package
com.sercrod.push_serverand place itsgoogle-services.jsonunderapp/push-server/. - Keep the service-account JSON outside the repository and public web root; pass its path to the server through
PUSH_FIREBASE_CREDENTIAL_FILE. - Publish the Node registration API behind HTTPS.
- Allow the public site plus the actual Capacitor origins in
PUSH_ALLOWED_ORIGIN. Common values arehttps://localhost,http://localhost, andcapacitor://localhost. - Register in the upper Node section, open its authenticated admin page, select the masked device, and send.
- Unregister before switching to PHP.
The live PHP registration endpoint is
https://sercrod.com/push-php/api/push, with its admin page at
/push-php/admin. Node and PHP
have both been verified through FCM with the same provider-neutral
registration contract.
Responsibility summary
*push: coordinates setup and the default watch.*push.register: explicitly continues the shared registration.*push.unregister: removes server registration, then provider registration.*push.watch: receives normalized events in a running page.*push.clear: removes the default or named local watch.- Application server: authenticates, stores registrations, selects recipients, sends notifications, and owns production policy.