snac2 with native push notifications? One of the most requested features for mobile users has finally arrived by adding native push notification support for iOS and Android applications is now available for snac and snac2.

I've been a snac citizen in the Fediverse (because Mastodon is just too fat when running a single user instance) since the very beginning of the project and have contributed a few smaller pull requests (e.g., favicon support, ssl ciphers nginx template, [...]) to Grunfink over time. Throughout all these years, there was one feature that I consistently missed whenever I used snac from a mobile device: native push notifications.

Don't get me wrong — snac already provides excellent alternatives. The integrated ntfy support works well and offers a practical solution for receiving notifications. However, it always felt like a workaround rather than a fully integrated experience. Modern mobile operating systems are built around their native notification frameworks, and most users expect notifications to appear seamlessly inside their preferred Fediverse application without requiring additional services or setup steps.

That missing piece was ultimately the reason I decided to spend some time implementing native Web Push support directly within the snac ecosystem.

Architecture and Design of snac2-push

One of the things I appreciate most about snac is its straightforward and pragmatic architecture. After reviewing the existing codebase, it quickly became apparent that many of the required building blocks were already available.

The notification infrastructure already existed. Queue handling was already implemented. User-specific storage locations were already available. The Mastodon API implementation already provided most of the required integration points. In many ways, the foundation for Web Push support had been prepared long ago.

Because of that, the implementation focused primarily on extending existing functionality rather than introducing entirely new subsystems. The goal was to integrate push notifications in a way that feels natural to snac itself: small, self-contained, file-based, and easy to understand.

The implementation in snac2-push adds support for Mastodon's push subscription API endpoints. Mobile clients can now register their Web Push subscription information, including the endpoint, public encryption key (p256dh), and authentication secret. These subscriptions are stored per user inside the existing snac data structure.

Whenever a notification is generated, the existing notification pipeline now additionally creates a push notification job. The notification type is evaluated, user preferences are checked, and a Web Push message is constructed using the stored subscription data.

The generated subscription information is stored within the user's push directory, while actual notification delivery jobs are placed into the user's queue directory. This follows the same design principles already used throughout snac for asynchronous processing.

The Web Push Helper

To keep the integration clean and modular, the actual Web Push delivery is performed by a dedicated helper application called snac-webpush-helper. I wasn't fully sure here to completely adapt this into a single C binary within snac or to craft a new binary. As my C isn't that good I wanted to keep the modifications as low as possible within the snac base and added a new one as the snac-webpush-helper.

The helper is intentionally designed as a standalone utility. When a queued push notification is processed, snac creates a temporary JSON job file containing all information required for delivery:


  • The subscriber endpoint
  • The client's encryption keys
  • The configured VAPID keys
  • The notification payload itself

Once the file has been created, snac launches the helper process and passes the JSON file as its only argument.

The helper then performs all Web Push specific operations:


  • Generation of a VAPID JWT
  • Elliptic Curve Diffie-Hellman key exchange
  • Payload encryption according to the Web Push standards
  • Transmission of the encrypted payload to the target endpoint

If delivery succeeds, the temporary job file is removed. If a delivery fails, snac can automatically requeue the notification according to its existing retry logic. Invalid or expired subscriptions are automatically cleaned up as well.

This design keeps the core snac process lightweight while allowing the Web Push implementation to remain completely independent. It also follows the project's preference for small focused components instead of large monolithic subsystems.

Installation of snac2-push & snac-webpush-helper

The current implementation has been tested on Linux Debian, FreeBSD, OpenBSD and NetBSD. One of the primary goals during development was to keep the dependency footprint as small as possible. Therefore, the implementation still only requires OpenSSL and libcurl, matching the existing requirements of snac itself.

The project can be used as a drop-in replacement for snac2 and remains fully compatible in both directions. Existing installations can migrate to this version without modifying their current data structure, and moving back to upstream snac2 is equally possible.

Internally, the existing data structure is only extended where necessary. The additional Web Push functionality does not alter existing data in a way that would prevent interoperability with upstream snac2. Furthermore, the stored push subscription information does not require the presence of the snac-webpush-helper binary, meaning that reverting to an unmodified snac2 installation remains possible at any time.

That being said, you should always create proper backups before making any changes to a production system. Before proceeding with the installation, ensure that your complete snacdata directory is backed up.

To keep maintenance simple, all required build adjustments have been integrated directly into the Makefile. The usual build and installation procedures therefore remain unchanged.

Building snac2-push

Compiling snac2-push from source keeps mostly unchanged and is fully integrated where it remains to simply run make. However, here's a fully workflow of creating a new instance:

  • useradd -m snac
  • su - snac
  • git clone https://git.gyptazy.com/gyptazy/snac2-push.git
  • cd snac2-push
  • make
  • ./snac-webpush-helper --gen-keys

The last command generates the VAPID key pair required for Web Push authentication. Make sure to save both the public and private keys, as they will be required during the next configuration step.

Configuring snac2-push

After generating the VAPID keys, the snac server.json configuration file must be extended with three new configuration options:


  • webpush_helper: Path to the webpush helper
  • vapid_private_key: The generated VAPID private key
  • vapid_public_key: The generated VAPID public key

An example configuration could look as follows:

{
 "host": "gyptazy.com",
 "prefix": "/fedi",
 "address": "0.0.0.0",
 "port": 8001,
 "layout": 2.7,
 "dbglevel": 0,
 "queue_retry_minutes": 2,
 "queue_retry_max": 10,
 "queue_timeout": 6,
 "queue_timeout_2": 8,
 "cssurls": [
 ""
 ],
 "def_timeline_entries": 50,
 "max_timeline_entries": 50,
 "timeline_purge_days": 120,
 "local_purge_days": 0,
 "min_account_age": 0,
 "admin_email": "contact@gyptazy.com",
 "admin_account": "gyptazy@gyptazy.com",
 "title": "gyptazy - DevOps, Coding, Networking and BSD",
 "short_description": "Only tech related content - nothing else!",
 "short_description_raw": false,
 "protocol": "https",
 "webpush_helper": "/home/snac/snac2-push/snac-webpush-helper",
 "vapid_private_key": "FOO66BARFOO66BARFOO66BARFOO66BARFOO66BARFOO66BAR",
 "vapid_public_key": "FOO66BARFOO66BARFOO66BARFOO66BARFOO66BARFOO66BAR",
 "fastcgi": false
}

Once these options have been configured, no further adjustments are necessary. The snac daemon automatically invokes the snac-webpush-helper whenever a push notification needs to be delivered.

There is no need to configure additional cron jobs, systemd timers, background workers or external schedulers. Push notification delivery is fully integrated into the existing snac queue processing workflow and operates transparently alongside the already established notification mechanisms.

Current State and Next Steps

At its current stage, this implementation should be considered a technical proof of concept and a first successful integration of native Web Push support into the snac ecosystem. The primary goal was to demonstrate that native push notifications for iOS and Android clients can be integrated into snac without introducing significant complexity, external infrastructure, or major architectural changes.

While the implementation is already functional, there are certainly areas that can be improved, refined, and further tested. As with any first draft, real-world usage will help identify opportunities for optimization and polish.

It is important to note that I do not intend to maintain this as a separate long-term fork or independent project. My preference has always been to contribute improvements back to the upstream project whenever possible rather than creating another permanently diverging code base.

The next logical step is therefore to discuss the implementation with Grunfink and evaluate whether these changes could become part of upstream snac. From my perspective, the chances are reasonably good. The modifications to the snac core itself are intentionally minimal and largely build upon mechanisms that already exist within the project. The actual Web Push implementation is encapsulated within a dedicated standalone helper binary, allowing the additional functionality to remain largely isolated from the core codebase.

This approach was chosen deliberately. By keeping the integration lightweight and following existing snac design patterns, the resulting solution remains simple to understand, easy to maintain, and consistent with the overall philosophy of the project.

Whether all of these changes eventually make their way into upstream snac remains to be seen, but I hope that the relatively small footprint, limited complexity, and optional nature of the implementation align well with the design principles and acceptance criteria that have guided the project from the beginning.