diff --git a/docs/develop.md b/docs/develop.md index 2b37b021..99ba6880 100644 --- a/docs/develop.md +++ b/docs/develop.md @@ -1,6 +1,7 @@ # Building ## ntfy server +The ntfy server source code is available [on GitHub](https://github.com/binwiederhier/ntfy). To quickly build on amd64, you can use `make build-simple`: ``` @@ -29,6 +30,7 @@ Releasing (requires goreleaser): There are currently no platform-specific make targets, so they will build for all platforms (which may take a while). ## Android app +The ntfy Android app source code is available [on GitHub](https://github.com/binwiederhier/ntfy-android). The Android app has two flavors: * **Google Play:** The `play` flavor includes Firebase (FCM) and requires a Firebase account diff --git a/docs/index.md b/docs/index.md index bf6d425a..c6a42cc1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,10 +1,12 @@ -# ntfy.sh | simple HTTP-based pub-sub +# Getting started **ntfy** (pronounce: *notify*) is a simple HTTP-based [pub-sub](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) -notification service. It allows you to send notifications to your phone or desktop via scripts from any computer, -entirely **without signup, cost or setup**. It's also [open source](https://github.com/binwiederhier/ntfy) if you want -to run your own. +notification service. It allows you to **send push notifications to your phone or desktop via scripts from any computer**, +entirely **without signup, cost or setup**. It's [open source](https://github.com/binwiederhier/ntfy) if you want to run your own. + +
+ +
Sending push notifications to your Android phone
+
-(pub sub diagram) -(screenshot / video / gif) diff --git a/docs/install.md b/docs/install.md index f9a38c51..ca2137dd 100644 --- a/docs/install.md +++ b/docs/install.md @@ -15,47 +15,120 @@ We support amd64, armv7 and arm64. Please check out the [releases page](https://github.com/binwiederhier/ntfy/releases) for binaries and deb/rpm packages. -**x86_64/amd64:** -``` -wget https://github.com/binwiederhier/ntfy/releases/download/v1.5.0/ntfy_1.5.0_linux_x86_64.tar.gz -sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy -``` +=== "x86_64/amd64" + ```bash + wget https://github.com/binwiederhier/ntfy/releases/download/v1.5.0/ntfy_1.5.0_linux_x86_64.tar.gz + sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy + sudo ./ntfy + ``` -**armv7:** -``` -wget https://github.com/binwiederhier/ntfy/releases/download/v1.5.0/ntfy_1.5.0_linux_armv7.tar.gz -sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy -``` +=== "armv7/armhf" + ```bash + wget https://github.com/binwiederhier/ntfy/releases/download/v1.5.0/ntfy_1.5.0_linux_armv7.tar.gz + sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy + sudo ./ntfy + ``` -**arm64/v8:** -``` -wget https://github.com/binwiederhier/ntfy/releases/download/v1.5.0/ntfy_1.5.0_linux_arm64.tar.gz -sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy -``` +=== "arm64" + ```bash + wget https://github.com/binwiederhier/ntfy/releases/download/v1.5.0/ntfy_1.5.0_linux_arm64.tar.gz + sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy + sudo ./ntfy + ``` ## Debian/Ubuntu repository Installation via Debian repository: -```bash -curl -sSL https://archive.heckel.io/apt/pubkey.txt | sudo apt-key add - -sudo apt install apt-transport-https -sudo sh -c "echo 'deb [arch=amd64] https://archive.heckel.io/apt debian main' > /etc/apt/sources.list.d/archive.heckel.io.list" -sudo apt update -sudo apt install ntfy -``` + +=== "x86_64/amd64" + ```bash + curl -sSL https://archive.heckel.io/apt/pubkey.txt | sudo apt-key add - + sudo apt install apt-transport-https + sudo sh -c "echo 'deb [arch=amd64] https://archive.heckel.io/apt debian main' \ + > /etc/apt/sources.list.d/archive.heckel.io.list" + sudo apt update + sudo apt install ntfy + sudo systemctl enable ntfy + sudo systemctl start ntfy + ``` + +=== "armv7/armhf" + ```bash + curl -sSL https://archive.heckel.io/apt/pubkey.txt | sudo apt-key add - + sudo apt install apt-transport-https + sudo sh -c "echo 'deb [arch=armhf] https://archive.heckel.io/apt debian main' \ + > /etc/apt/sources.list.d/archive.heckel.io.list" + sudo apt update + sudo apt install ntfy + sudo systemctl enable ntfy + sudo systemctl start ntfy + ``` + +=== "arm64" + ```bash + curl -sSL https://archive.heckel.io/apt/pubkey.txt | sudo apt-key add - + sudo apt install apt-transport-https + sudo sh -c "echo 'deb [arch=arm64] https://archive.heckel.io/apt debian main' \ + > /etc/apt/sources.list.d/archive.heckel.io.list" + sudo apt update + sudo apt install ntfy + sudo systemctl enable ntfy + sudo systemctl start ntfy + ``` Manually installing the .deb file: -```bash -wget https://github.com/binwiederhier/ntfy/releases/download/v1.5.0/ntfy_1.5.0_amd64.deb -dpkg -i ntfy_1.5.0_amd64.deb -``` + +=== "x86_64/amd64" + ```bash + wget https://github.com/binwiederhier/ntfy/releases/download/v1.5.0/ntfy_1.5.0_linux_amd64.deb + sudo dpkg -i ntfy_*.deb + sudo systemctl enable ntfy + sudo systemctl start ntfy + ``` + +=== "armv7/armhf" + ```bash + wget https://github.com/binwiederhier/ntfy/releases/download/v1.5.0/ntfy_1.5.0_linux_armv7.deb + sudo dpkg -i ntfy_*.deb + sudo systemctl enable ntfy + sudo systemctl start ntfy + ``` + +=== "arm64" + ```bash + wget https://github.com/binwiederhier/ntfy/releases/download/v1.5.0/ntfy_1.5.0_linux_arm64.deb + sudo dpkg -i ntfy_*.deb + sudo systemctl enable ntfy + sudo systemctl start ntfy + ``` ## Fedora/RHEL/CentOS -```bash -rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.5.0/ntfy_1.5.0_amd64.rpm -``` + +=== "x86_64/amd64" + ```bash + sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.5.0/ntfy_1.5.0_linux_amd64.rpm + sudo systemctl enable ntfy + sudo systemctl start ntfy + ``` + +=== "armv7/armhf" + ```bash + sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.5.0/ntfy_1.5.0_linux_armv7.rpm + sudo systemctl enable ntfy + sudo systemctl start ntfy + ``` + +=== "arm64" + ```bash + sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.5.0/ntfy_1.5.0_linux_arm64.rpm + sudo systemctl enable ntfy + sudo systemctl start ntfy + ``` ## Docker -The ntfy server exposes its web UI and the API on port 80, so you need to expose that in Docker. To use the persistent +The [ntfy image](https://hub.docker.com/r/binwiederhier/ntfy) is available for amd64, armv7 and arm64. It should be pretty +straight forward to use. + +The server exposes its web UI and the API on port 80, so you need to expose that in Docker. To use the persistent message cache, you also need to map a volume to `/var/cache/ntfy`. To change other settings, you should map `/etc/ntfy`, so you can edit `/etc/ntfy/config.yml`. @@ -88,6 +161,7 @@ To install via Go, simply run: ```bash go install heckel.io/ntfy@latest ``` + !!! info Please [let me know](https://github.com/binwiederhier/ntfy/issues) if there are any issues with this installation method. The SQLite bindings require CGO and it works for me, but I have the feeling it may not work for everyone. diff --git a/docs/publish.md b/docs/publish.md index f742bc5d..22329cdf 100644 --- a/docs/publish.md +++ b/docs/publish.md @@ -1,9 +1,9 @@ # Publishing - -Publishing messages can be done via PUT or POST. Topics are created on the fly by subscribing or publishing to them. -Because there is no sign-up, the topic is essentially a password, so pick something that's not easily guessable. +Publishing messages can be done via HTTP PUT or POST. Topics are created on the fly by subscribing or publishing to them. +Because there is no sign-up, **the topic is essentially a password**, so pick something that's not easily guessable. Here's an example showing how to publish a simple message using a POST request: + === "Command line (curl)" ``` curl -d "Backup successful 😀" ntfy.sh/mytopic @@ -31,7 +31,7 @@ Here's an example showing how to publish a simple message using a POST request: ``` === "PHP" - ``` php + ``` php-inline file_get_contents('https://ntfy.sh/mytopic', false, stream_context_create([ 'http' => [ 'method' => 'POST', // PUT also works @@ -96,7 +96,7 @@ a [title](#message-title), and [tag messages](#tags-emojis) 🥳 🎉. Here's an ``` === "PHP" - ``` php + ``` php-inline file_get_contents('https://ntfy.sh/phil_alerts', false, stream_context_create([ 'http' => [ 'method' => 'POST', // PUT also works @@ -152,7 +152,7 @@ you can set the `X-Title` header (or any of its aliases: `Title`, `ti`, or `t`). ``` === "PHP" - ``` php + ``` php-inline file_get_contents('https://ntfy.sh/controversial', false, stream_context_create([ 'http' => [ 'method' => 'POST', @@ -218,7 +218,7 @@ You can set the priority with the header `X-Priority` (or any of its aliases: `P ``` === "PHP" - ``` php + ``` php-inline file_get_contents('https://ntfy.sh/phil_alerts', false, stream_context_create([ 'http' => [ 'method' => 'POST', @@ -315,7 +315,7 @@ them with a comma, e.g. `tag1,tag2,tag3`. ``` === "PHP" - ``` php + ``` php-inline file_get_contents('https://ntfy.sh/backups', false, stream_context_create([ 'http' => [ 'method' => 'POST', diff --git a/docs/static/img/badge-appstore.png b/docs/static/img/badge-appstore.png new file mode 100644 index 00000000..0b4ce1c0 Binary files /dev/null and b/docs/static/img/badge-appstore.png differ diff --git a/docs/static/img/badge-fdroid.png b/docs/static/img/badge-fdroid.png new file mode 100644 index 00000000..9464d38a Binary files /dev/null and b/docs/static/img/badge-fdroid.png differ diff --git a/docs/static/img/badge-googleplay.png b/docs/static/img/badge-googleplay.png new file mode 100644 index 00000000..36036d8b Binary files /dev/null and b/docs/static/img/badge-googleplay.png differ diff --git a/docs/static/img/overview.gif b/docs/static/img/overview.gif new file mode 100644 index 00000000..b925502a Binary files /dev/null and b/docs/static/img/overview.gif differ diff --git a/docs/static/img/overview.mp4 b/docs/static/img/overview.mp4 new file mode 100644 index 00000000..cf295099 Binary files /dev/null and b/docs/static/img/overview.mp4 differ diff --git a/docs/static/img/screenshot-web-notifcation.png b/docs/static/img/screenshot-web-notifcation.png new file mode 100644 index 00000000..aeb5a31a Binary files /dev/null and b/docs/static/img/screenshot-web-notifcation.png differ diff --git a/docs/static/img/screenshot-web.png b/docs/static/img/screenshot-web.png new file mode 100644 index 00000000..08c759f8 Binary files /dev/null and b/docs/static/img/screenshot-web.png differ diff --git a/docs/subscribe/api.md b/docs/subscribe/api.md index 708250ad..06b97d8e 100644 --- a/docs/subscribe/api.md +++ b/docs/subscribe/api.md @@ -1,6 +1,235 @@ # Subscribe via API +You can create and subscribe to a topic either in the [web UI](web.md), via the [phone app](phone.md), or in your own +app or script by subscribing the API. This page describes how to subscribe via API. You may also want to check out the +page that describes how to [publish messages](../publish.md). + +The subscription API relies on a simple HTTP GET request with a streaming HTTP response, i.e **you open a GET request and +the connection stays open forever**, sending messages back as they come in. There are three different API endpoints, which +only differ in the response format: + +* [JSON stream](#subscribe-as-json-stream): `/json` returns a JSON stream, with one JSON message object per line +* [SSE stream](#subscribe-as-sse-stream): `/sse` returns messages as [Server-Sent Events (SSE)](https://en.wikipedia.org/wiki/Server-sent_events), which + can be used with [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) +* [Raw stream](#subscribe-as-raw-stream): `/raw` returns messages as raw text, with one line per message + +## Subscribe as JSON stream +Here are a few examples of how to consume the JSON endpoint (`/json`). For almost all languages, **this is the +recommended way to subscribe to a topic**. The notable exception is JavaScript, for which the +[SSE/EventSource stream](#subscribe-as-sse-stream) is much easier to work with. + +=== "Command line (curl)" + ``` + $ curl -s ntfy.sh/disk-alerts/json + {"id":"SLiKI64DOt","time":1635528757,"event":"open","topic":"mytopic"} + {"id":"hwQ2YpKdmg","time":1635528741,"event":"message","topic":"mytopic","message":"Disk full"} + {"id":"DGUDShMCsc","time":1635528787,"event":"keepalive","topic":"mytopic"} + ... + ``` + +=== "HTTP" + ``` http + GET /disk-alerts/json HTTP/1.1 + Host: ntfy.sh + + HTTP/1.1 200 OK + Content-Type: application/x-ndjson; charset=utf-8 + Transfer-Encoding: chunked -## Fetching cached messages + {"id":"SLiKI64DOt","time":1635528757,"event":"open","topic":"mytopic"} + {"id":"hwQ2YpKdmg","time":1635528741,"event":"message","topic":"mytopic","message":"Disk full"} + {"id":"DGUDShMCsc","time":1635528787,"event":"keepalive","topic":"mytopic"} + ... + ``` + +=== "Go" + ``` go + resp, err := http.Get("https://ntfy.sh/disk-alerts/json") + if err != nil { + log.Fatal(err) + } + defer resp.Body.Close() + scanner := bufio.NewScanner(resp.Body) + for scanner.Scan() { + println(scanner.Text()) + } + ``` + +=== "PHP" + ``` php-inline + $fp = fopen('https://ntfy.sh/disk-alerts/json', 'r'); + if (!$fp) die('cannot open stream'); + while (!feof($fp)) { + echo fgets($fp, 2048); + flush(); + } + fclose($fp); + ``` + +## Subscribe as SSE stream +Using [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) in JavaScript, you can consume +notifications via a [Server-Sent Events (SSE)](https://en.wikipedia.org/wiki/Server-sent_events) stream. It's incredibly +easy to use. Here's what it looks like. You may also want to check out the [live example](/example.html). + +=== "Command line (curl)" + ``` + $ curl -s ntfy.sh/mytopic/sse + event: open + data: {"id":"weSj9RtNkj","time":1635528898,"event":"open","topic":"mytopic"} + + data: {"id":"p0M5y6gcCY","time":1635528909,"event":"message","topic":"mytopic","message":"Hi!"} + + event: keepalive + data: {"id":"VNxNIg5fpt","time":1635528928,"event":"keepalive","topic":"test"} + ... + ``` + +=== "HTTP" + ``` http + GET /mytopic/sse HTTP/1.1 + Host: ntfy.sh + + HTTP/1.1 200 OK + Content-Type: text/event-stream; charset=utf-8 + Transfer-Encoding: chunked + + event: open + data: {"id":"weSj9RtNkj","time":1635528898,"event":"open","topic":"mytopic"} + + data: {"id":"p0M5y6gcCY","time":1635528909,"event":"message","topic":"mytopic","message":"Hi!"} + + event: keepalive + data: {"id":"VNxNIg5fpt","time":1635528928,"event":"keepalive","topic":"test"} + ... + ``` + +=== "JavaScript" + ``` javascript + const eventSource = new EventSource('https://ntfy.sh/mytopic/sse'); + eventSource.onmessage = (e) => { + console.log(e.data); + }; + ``` + +## Subscribe as raw stream +The `/raw` endpoint will output one line per message, and **will only include the message body**. It's useful for extremely +simple scripts, and doesn't include all the data. Additional fields such as [priority](../publish.md#message-priority), +[tags](../publish.md#tags--emojis--) or [message title](../publish.md#message-title) are not included in this output +format. Keepalive messages are sent as empty lines. + +=== "Command line (curl)" + ``` + $ curl -s ntfy.sh/disk-alerts/raw + + Disk full + ... + ``` + +=== "HTTP" + ``` http + GET /disk-alerts/raw HTTP/1.1 + Host: ntfy.sh + + HTTP/1.1 200 OK + Content-Type: text/plain; charset=utf-8 + Transfer-Encoding: chunked + + Disk full + ... + ``` + +=== "Go" + ``` go + resp, err := http.Get("https://ntfy.sh/disk-alerts/raw") + if err != nil { + log.Fatal(err) + } + defer resp.Body.Close() + scanner := bufio.NewScanner(resp.Body) + for scanner.Scan() { + println(scanner.Text()) + } + ``` + +=== "PHP" + ``` php-inline + $fp = fopen('https://ntfy.sh/disk-alerts/raw', 'r'); + if (!$fp) die('cannot open stream'); + while (!feof($fp)) { + echo fgets($fp, 2048); + flush(); + } + fclose($fp); + ``` + +## JSON message format +Both the [`/json` endpoint](#subscribe-as-json-stream) and the [`/sse` endpoint](#subscribe-as-sse-stream) return a JSON +format of the message. It's very straight forward: + +| Field | Required | Type | Example | Description | +|---|---|---|---|---| +| `id` | ✔️ | *string* | `hwQ2YpKdmg` | Randomly chosen message identifier | +| `time` | ✔️ | *int* | 1635528741 | Message date time, as Unix time stamp | +| `event` | ✔️ | `open`, `keepalive` or `message` | `message` | Message type, typically you'd be only interested in `message` | +| `topic` | ✔️ | *string* | `topic1,topic2` | Comma-separated list of topics the message is associated with; only one for all `message` events, but may be a list in `open` events | +| `message` | - | *string* | `Some message` | Message body; always present in `message` events | +| `title` | - | *string* | `Some title` | Message [title](../publish.md#message-title); if not set defaults to `ntfy.sh/` | +| `tags` | - | *string array* | `["tag1","tag2"]` | List of [tags](../publish.md#tags--emojis--) that may or not map to emojis | +| `priority` | - | *1, 2, 3, 4, or 5* | `4` | Message [priority](../publish.md#message-priority) with 1=min, 3=default and 5=max | + +Here's an example for each message type: + +=== "Notification message" + ``` json + { + "id": "wze9zgqK41", + "time": 1638542110, + "event": "message", + "topic": "phil_alerts", + "priority": 5, + "tags": [ + "warning", + "skull" + ], + "title": "Unauthorized access detected", + "message": "Remote access to phils-laptop detected. Act right away." + } + ``` + + +=== "Notification message (minimal)" + ``` json + { + "id": "wze9zgqK41", + "time": 1638542110, + "event": "message", + "topic": "phil_alerts", + "message": "Remote access to phils-laptop detected. Act right away." + } + ``` + +=== "Open message" + ``` json + { + "id": "2pgIAaGrQ8", + "time": 1638542215, + "event": "open", + "topic": "phil_alerts" + } + ``` + +=== "Keepalive message" + ``` json + { + "id": "371sevb0pD", + "time": 1638542275, + "event": "keepalive", + "topic": "phil_alerts" + } + ``` + +## Advanced features + +### Fetching cached messages Messages may be cached for a couple of hours (see [message caching](../config.md#message-cache)) to account for network interruptions of subscribers. If the server has configured message caching, you can read back what you missed by using the `since=` query parameter. It takes either a duration (e.g. `10m` or `30s`), a Unix timestamp (e.g. `1635528757`) @@ -10,7 +239,7 @@ or `all` (all cached messages). curl -s "ntfy.sh/mytopic/json?since=10m" ``` -## Polling +### Polling You can also just poll for messages if you don't like the long-standing connection using the `poll=1` query parameter. The connection will end after all available messages have been read. This parameter can be combined with `since=` (defaults to `since=all`). @@ -19,7 +248,7 @@ combined with `since=` (defaults to `since=all`). curl -s "ntfy.sh/mytopic/json?poll=1" ``` -## Subscribing to multiple topics +### Subscribing to multiple topics It's possible to subscribe to multiple topics in one HTTP call by providing a comma-separated list of topics in the URL. This allows you to reduce the number of connections you have to maintain: diff --git a/docs/subscribe/phone.md b/docs/subscribe/phone.md index 9cdc221e..d2a500d5 100644 --- a/docs/subscribe/phone.md +++ b/docs/subscribe/phone.md @@ -3,5 +3,20 @@ You can use the [ntfy Android App](https://play.google.com/store/apps/details?id notifications directly on your phone. Just like the server, this app is also [open source](https://github.com/binwiederhier/ntfy-android). Since I don't have an iPhone or a Mac, I didn't make an iOS app yet. I'd be awesome if [someone else could help out](https://github.com/binwiederhier/ntfy/issues/4). - - +## Android +You can get the Android app from both [Google Play](https://play.google.com/store/apps/details?id=io.heckel.ntfy) and +from [F-Droid](https://f-droid.org/en/packages/io.heckel.ntfy/). Both are largely identical, with the one exception that +the F-Droid flavor does not use Firebase. + + + + +### Firebase + Instant delivery + + + +## iPhone/iOS +I almost feel devious for putting the *Download on the App Store* button on this page. Currently, there is no iOS app +for ntfy, but it's in the works. You can track the status on GitHub. + + diff --git a/docs/subscribe/web.md b/docs/subscribe/web.md index 7da6add2..478d6126 100644 --- a/docs/subscribe/web.md +++ b/docs/subscribe/web.md @@ -1 +1,11 @@ -# Subscribe from the web UI +# Subscribe from the Web UI +You can use the Web UI to subscribe to topics as well. If you do, and you keep the website open, **notifications will +pop up as desktop notifications**. Simply type in the topic name and click the *Subscribe* button. The browser will +keep a connection open and listen for incoming notifications. + +
+ ![web subscribe](../static/img/screenshot-web.png){ width=300 } +
Subscribe via Web UI
+
+ +Once subscribed, you can [publish messages](../publish.md) via `curl` or from without any of your scripts. diff --git a/examples/subscribe-go/main.go b/examples/subscribe-go/main.go new file mode 100644 index 00000000..5f807610 --- /dev/null +++ b/examples/subscribe-go/main.go @@ -0,0 +1,19 @@ +package main + +import ( + "bufio" + "log" + "net/http" +) + +func main() { + resp, err := http.Get("https://ntfy.sh/phil_alerts/json") + if err != nil { + log.Fatal(err) + } + defer resp.Body.Close() + scanner := bufio.NewScanner(resp.Body) + for scanner.Scan() { + println(scanner.Text()) + } +} diff --git a/examples/subscribe-php/subscribe.php b/examples/subscribe-php/subscribe.php new file mode 100644 index 00000000..fcdbe98b --- /dev/null +++ b/examples/subscribe-php/subscribe.php @@ -0,0 +1,12 @@ +