Skip to content

A self-hosted, drop-in replacement for Pushover that can use XMPP, as well as a wide variety of other services as the delivery method while maintaining full compatibility with the Pushover API and also offering a flexible HTTP webhooks endpoint.

License

Notifications You must be signed in to change notification settings

mrusme/overpush

Repository files navigation

Overpush

Overpush

Static Badge Static Badge

Overpush is a self-hosted, drop-in replacement for Pushover that can use XMPP, as well as a wide variety of other services (see below) as the delivery method while maintaining full compatibility with the Pushover API and also offering a flexible HTTP webhooks endpoint. This allows existing setups to continue functioning with minimal changes, while allowing more complex setups further down the road.

Think of Overpush as a bridge between HTTP requests (webhooks) and various target services, such as XMPP, Matrix, Signal, and others:

                    ┌────────────────┐                    
                    │                │                    
                    │  HTTP REQUEST  │                    
                    │                │                    
                    └────────┬───────┘                    
                             │                            
                             │                            
                             │                            
                    ┌────────▼───────┐                    
                    │                │                    
                    │  OVERPUSH API  │                    
                    │                │                    
                    │────────────────│                    
                    │     REDIS      │                    
                    │────────────────│                    
                    │                │                    
                    │     WORKER     │                    
                    │                │                    
                    └────────┬───────┘                    
                             │                            
        ┌────────────────────┼──────────────────┐         
        │                    │                  │         
┌───────▼────────┐  ┌────────▼───────┐  ┌───────▼────────┐
│                │  │                │  │                │
│     SIGNAL     │  │      XMPP      │  │     MATRIX     │
│                │  │                │  │                │
└────────────────┘  └────────────────┘  └────────────────┘

The Overpush API accepts HTTP POST requests in multiple formats, including:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • application/json
  • application/xml/text/xml

The configuration file allows you to define custom templates using Go's text/template syntax to extract and transform data from webhook payloads.

Internally, Overpush uses asynq to queue messages for processing by a background worker. This requires a Redis-compatible server as a backend. You can run your own instance (e.g. Redis, Valkey, KeyDB, DragonflyDB) or use the free Essentials plan from Redis Cloud, which is reliable and sufficient for most use cases.

The Overpush worker has native support for XMPP as a target. For other services, it integrates with Apprise, requiring the apprise CLI tool to be available on the same host.

Build

To build Overpush, simply clone this repository and run the following command inside your local copy:

$ go build

Configure

The Overpush configuration is organized into four main sections:

  • [Server]: Specifies server settings, like the IP address and port on which Overpush should run.
  • [Redis]: Configures the connection to a Redis server or cluster.
  • [Users]: Defines individual users and their settings.
  • [Targets]: Sets up message targets (or destinations) for each user.

Overpush will try to read the overpush.toml file from one of the following paths:

  • /etc/overpush.toml
  • $XDG_CONFIG_HOME/overpush.toml
  • $HOME/.config/overpush.toml
  • $HOME/overpush.toml
  • $PWD/overpush.toml

Every configuration key available in the example overpush.toml can be exported as environment variable, by separating scopes using _ and prepend OVERPUSH to it.

Sources

The Overpush API accepts requests to the legacy /1/messages.json endpoint used by Pushover, which allows you to simply flip the host/domain in your configurations to drop in Overpush instead of Pushover. In addition, Overpush offers a flexible HTTP POST endpoint on /:token (where :token is an Application's Token), which can be used to retrieve various different webhook formats.

Pushover clients

The official Pushover API documentation shows how to submit a message to the /1/messages.json endpoint. Replacing Pushover with Overpush only requires your tooling to support changing the endpoint URL to your own server. Enable = true

You can find an example script here that serves as a command-line API client for both Pushover and Overpush to submit notifications. Since Overpush does not yet offer 100% feature parity with Pushover, some features are not available.

Custom HTTP Webhooks

Overpush can handle a wide variety of custom webhooks by configuring dedicated Applications in its config. Here are some examples:

CrowdSec

Add the following application to your Overpush config:

[[Users.Applications]]
Enable = true
Token = "XXX"
Name = "CrowdSec"
IconPath = ""
Target = "your_target"
TargetArgs.Destination = "[email protected]"
Format = "custom"
CustomFormat.Message = '{{ webhook "body.alerts.0.message" }}'
CustomFormat.Title = 'CrowdSec: {{ webhook "body.alerts.0.scenario" }}'

Edit the CrowdSec config notifications/http.yaml (under /etc/crowdsec/notifications/http.yaml or /usr/local/etc/crowdsec/notifications/http.yaml) as follows:

type: http
name: http_default

log_level: info

format: |
  {"alerts":{{.|toJson}}}

url: https://my.overpush.net/XXX

method: POST

headers:
  Content-Type: application/json

# skip_tls_verification:  true

Set XXX to the unique token of the Overpush application.

Grafana

Add the following application to your Overpush config:

[[Users.Applications]]
Enable = true
Token = "XXX"
Name = "Grafana"
IconPath = ""
Target = "your_target"
TargetArgs.Destination = "[email protected]"
Format = "custom"
CustomFormat.Message = '{{ webhook "body.message" }}'
CustomFormat.Title = '{{ webhook "body.title" }}'
CustomFormat.URL = '{{ webhook "body.externalURL" }}'

Create a new contact point in your Grafana under /alerting/notifications/receivers/new, choose the Webhook integration add set your Overpush instance:

https://my.overpush.net/XXX

Set XXX to the unique token of the Overpush application.

Targets

Targets are the target services that messages should be routed to. Overpush supports XMPP out of the box, but can use Apprise to forward messages to a wide range of different services.

Each Application must specify a Target. Multiple Application configurations might use the same target.

XMPP (built-in)

Overpush supports XMPP (without OTR/OMEMO) out of the box, without any additional software. The configuration for the XMPP target might look like this:

[[Targets]]
Enable = true
ID = "your_target"
Type = "xmpp"

  [Targets.Args]
  server = "conversations.im"
  tls = "true"
  username = "[email protected]"
  password = "hunter2"

To use this target, specify its ID inside an Application configuration:

...
Target = "your_target"
TargetArgs.Destination = "[email protected]"
...

Apprise

Overpush supports the following platforms via Apprise:

The configuration for the Matrix target might look like this:

[[Targets]]
Enable = true
ID = "your_target"
Type = "apprise"

  [Targets.Args]
  apprise = "/home/you/.local/share/pyenv/bin/apprise"
  connection = 'matrixs://your_bot:[email protected]:443/{{ arg "destination" }}?format=markdown'

To use this target, specify its ID inside an Application configuration:

...
Target = "your_target"
TargetArgs.Destination = "!xXxXxXxXXXxxxXXXxX:matrix.org"
...

Internal Endpoints

Overpush serves internal endpoints under /_internal/. These endpoints are not to be made available publicly/for clients. Make sure your reverse proxy blocks access to every URL starting with /_internal/. Hosting Overpush without a reverse proxy is not supported.

Health Check

Overpush provides the following URLs for health checks:

  • /_internal/health/livez: Checks if the server is up and running.
  • /_internal/health/readyz: Assesses if the application is ready to handle requests.
  • /_internal/health/startupz: Checks if the application has completed its startup sequence and is ready to proceed with initialization and readiness checks.

These endpoints can return the following HTTP status codes:

  • 200 OK
  • 503 Service Unavailable

Run

All that's needed is a configuration and Overpush can be launched:

$ overpush

Supervisor

To run Overpush via supervisord, create a config like this inside /etc/supervisord.conf or /etc/supervisor/conf.d/overpush.ini. See this example.

Note: It is advisable to run Overpush under its own, dedicated daemon user. Make sure to either adjust directory as well as user.

OpenBSD rc

As before, create a configuration file under /etc/overpush.toml.

Then copy the example rc.d script to /etc/rc.d/overpush and copy the binary to e.g. /usr/local/bin/overpush. Last but not least, update the /etc/rc.conf.local file to contain the following line:

overpush_user="_overpush"

It is advisable to run Overpush as a dedicated user, hence create the _overpush daemon account or adjust the line above according to your setup.

You can now run Overpush by enabling and starting the service:

$ rcctl enable overpush
$ rcctl start overpush

systemd

As before, create a configuration file under /etc/overpush.toml.

Then copy the example systemd service to /etc/systemd/system/overpush.service and copy the binary to e.g. /usr/local/bin/overpush. Ensure the overpush user and group exist, or change them to something appropriate.

Then, as root, reload systemd and enable the service:

# systemctl daemon-reload
# systemctl enable --now overpush

If you'd rather prefer running Overpush as a user-level service, put the service file in ~/.config/systemd/user/overpush.service and reload and enable the service as user:

$ systemctl --user daemon-reload
$ systemctl --user enable --now overpush

Podman

$ podman run \
  -d \
  --name overpush \
  --network podman \
  -v "$(pwd)/overpush.toml:/etc/overpush.toml:ro" \
  -p 8080:8080 \
  ghcr.io/mrusme/overpush:latest

Info: apprise inside the Docker container is available at /usr/bin/apprise; Make sure to set the apprise = "/usr/bin/apprise" flag inside apprise targets.

Docker

$ docker run \
  -d \
  --name overpush \
  -v "$(pwd)/overpush.toml:/etc/overpush.toml:ro" \
  -p 8080:8080 \
  ghcr.io/mrusme/overpush:latest

Info: apprise inside the Docker container is available at /usr/bin/apprise; Make sure to set the apprise = "/usr/bin/apprise" flag inside apprise targets.

Kubernetes

TODO

Amazon Web Services Lambda Function

TODO

Google Cloud Function

TODO

FAQ

Why?

That's why.

XMPP? Without OTR? Or OMEMO?

Nope, none of those. The XMPP ecosystem is a bit of a can of worms in this regard. First, when using modern languages like Go, there are very few XMPP libraries available. The ones that do exist generally don't support OTR or OMEMO. Adding support would require either implementing these protocols from scratch or interfacing with a low-level C library.

Even if someone were willing to go through that effort, they'd run into the second major issue with XMPP: fragmentation. For example, if someone were to implement OMEMO in Go today, they would likely choose OMEMO 0.8.3 or newer. Unfortunately, many XMPP clients are still stuck on version 0.3.0, which uses AES-128-GCM -- an encryption algorithm considered weaker by modern standards (e.g., compared to what Matrix.org or Signal use). As a result, most implementations would have to fall back to a significantly older and less secure version of OMEMO.

Workaround

For notifications that require content encryption, good old GPG can be used:

curl -s \
  --form-string "token=$OP_TOKEN" \
  --form-string "user=$OP_USER" \
  --form-string "message=$(gpg -e -r KEY_ID --armor -o - file_to_encrypt)" \
  "$OP_URL"

On the receiving end, for example, Android running Conversations, the message can be shared to OpenKeychain using the standard Android sharing popup. OpenKeychain will then decrypt and display the message content.

This is the simplest way to enable encrypted notifications without relying on XMPP clients to support modern encryption standards.

But Matrix is E2EE, right?

Nope. Use the aforementioned workaround.

About

A self-hosted, drop-in replacement for Pushover that can use XMPP, as well as a wide variety of other services as the delivery method while maintaining full compatibility with the Pushover API and also offering a flexible HTTP webhooks endpoint.

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

Packages