This adds the detection of `NTFY_PASSWORD_HASH` when creating a user or
changing its passsword so that scripts don't have to manipulate the bare
password.
Without this a UnifiedPush/Web Push message with encryption would be
turned into an attachment. That in itself isn't pretty but can still
work, but it requires attachments to be enabled in the first place.
If a topic does not allow anonymous reads, this change ensures that we send a "poll_request" event instead of relaying the message via Firebase. Additionally, we include generic text in the title and body/message. This way, if the client cannot retrieve the actual message, the user will still receive a notification, prompting them to update the client manually.
EasyMorph (https://easymorph.com) is a visual workflow-based data preparation and automation tool. It has 180+ actions, including a dedicated action to send notifications to ntfy as a workflow step.
The proposed link leads to the official help page for the "Send message to ntfy" action.
Added note to add ", chain=DOCKER-USER" to the fail2ban jail action if using docker networks
By default, the jail action chain is "INPUT", but "FORWARD" is used when using docker networks. "DOCKER-USER", available when using docker, is part of the "FORWARD" chain. Hence the note to use "DOCKER-USER".
Corrected PowerShell 7+ Parameter from Authorization to Authentication
Converted Token text to SecureString Inline - token must be of type SecureString
Added comment noting the Token parameter must be a SecureString
This should fix "read-only access to topic *" being applied before "read-write access to topic _PREFIX_*"
Before this if we have:
ntfy access user "mytopic*" rw
ntfy access user "*" ro
read-only access rule was applied first and user couldn't write to
mytopic*
- Now, only if the header being processed is the "priority" header, the cloudflarePriorityIgnore function is called, solving problems with that header injected by CF
- we make the check with regex now.
With these changes, If the web request contains the new Priority header (RFC 9218), The server will ignore it and continue searching for other headers or query parameters.
This would help inexperienced sysadmins who may not realise that since TLS terminates at proxy, ntfy is actually listening on a TCP socket that’s using http rather than https.
- Also handle notification permission changes
- Remove web push schedule worker since this complicates
things and doesn’t do _that_ much. We have the reminder
notification if the user truly doesn’t reload ntfy in
more than a week.
There are sometimes edge cases on iOS which cause the app to crash,
it’s good to have a reload button as there’s no browser chrome (reload,
back, forward) in an iOS standalone PWA.
The harder-to-refactor parts are the places where exists/username/token
are called within a React component. However, `resetAndRedirect` and
`store` are already called from async contexts, so adding an `await`
is simple.
This thus merges the logic, keeping localStorage for the components to
call, but making sure reset/store behaviour works correctly for the
replica.
- Use a single endpoint
- Use a declarative web push sync hook. This thus handles all edge cases
that had to be manually handled before: logout, login, account sync,
etc.
- Simplify UX: browser notifications are always enabled (unless denied),
web push toggle only shows up if permissions are already granted.
- Use new notification request/opt-in flow for push
- Implement unsubscribing
- Implement muting
- Implement emojis in title
- Add iOS specific PWA warning
- Don’t use websockets when web push is enabled
- Fix duplicate notifications
- Implement default web push setting
- Implement changing subscription type
- Implement web push subscription refresh
- Implement web push notification click
I’d like to test #751 on my own instance, but installing all the build
dependencies on my server isn’t ideal - having this script in the repo
would make it possible to simply point my compose file to the git repo
and have it build the Linux binary itself.
Note that it uses a somewhat “inefficient” builder step, i.e. not
combining steps together to reduce layers, as it uses a multi-stage
build to have a lean final image. This makes it easier to re-build if
something needs to change, as the cache is used more optimally.
For example, if only some go files change, most of the build is already
cached and only the go step gets re-run.
The more “efficient” builder step would look like this, but would have
to build the docs, web app and go CLI for any change in any file:
```Dockerfile
FROM golang:1.19-bullseye as builder
RUN apt-get update && \
curl -fsSL https://deb.nodesource.com/setup_18.x | bash && \
apt-get install -y \
build-essential \
nodejs \
python3-pip
WORKDIR /app
ADD . .
RUN make web docs cli-linux-server
```
`apt` is for interactive shell usage, using it in a script results in a
warning as the CLI interface is not stable
> WARNING: apt does not have a stable CLI interface.
> Use with caution in scripts.
These are safe fixes, more complicated fixes can be done separately
(just disabled those errors for now).
- Reorder declarations to fix `no-use-before-define`
- Rename parameters for `no-shadow`
- Remove unused parameters, functions, imports
- Switch from `++` and `—` to `+= 1` and `-= 1` for `no-unary`
- Use object spreading instead of parameter reassignment in auth utils
- Use `window.location` instead of `location` global
- Use inline JSX strings instead of unescaped values
-
- Use the newest versions to solve the deprecation warning
- Remove the cache step as the newest go and node actions have built-in
caching
- Add the official actions@github.com email address
This fixes a pending TODO comment regarding inefficient tags to emojis
mapping, by requiring a full scan over emoji aliases to determine
matches.
Instead, now the JSON file is a map, with aliases as keys, and emojis as
values. The script to convert the file with Python was:
```python
import json
with open("./mailer_emoji.json", "r", encoding="utf-8") as f:
content = json.load(f)
emoji_map = {}
for emoji in content:
for alias in emoji["aliases"]:
if alias in emoji_map:
print("WARNING: Duplicate alias:", alias)
continue
emoji_map[alias] = str(emoji["emoji"])
sorted_emoji_map = {k: emoji_map[k] for k in sorted(emoji_map)}
with open("./mailer_emoji_map.json", "w", encoding="utf-8") as f:
json.dump(sorted_emoji_map, f, indent=4, ensure_ascii=False)
```
As the `ContainsAll` is working with a match counter, it could return
a false positive when the `haystack` slice contains duplicate elements.
This can be checked with the included testing scenario, with
`haystack = [1, 1]` and `needles = [1, 2]`. Iterating over the haystack
to check for items to be present in needles will increase the match
counter to 2, even if `2` is not present in the first slice.
Some e-mails are sent using quoted-printable encoding [0], resulting in
notifications with weird characters.
This commit adds support for this encoding, resulting in the following:
**Before**
```
A
=3D=3D=3D=3D=3D
B
=3D=3D=3D=3D=3D
C
```
**After**
```
A
=====
B
=====
C
```
[0] https://www.rfc-editor.org/rfc/rfc2045.html
Windows has an issue displaying country flag emoji. This is a platform issue which does not even appear to be fixed in Win11. As a result this fix will just hide the emoji when a windows operating system is detected.
resolves#606
In many languages there is more than one plural form of nouns and rules
for choosing the correct one are often far more complex than in English.
Luckily both react-i18next and Weblate provide built-in support for
translating and selecting plural forms in accordance with grammatical
rules of any given language.
In order to enable plural forms `{count: n}` option is added to relevant
`t()` calls. In translations files "_one" and "_other" suffix is added
to English labels such that Weblate can detect which entries represent a
set of plural forms and show appropriate language-specific form on the
translation page. E.g. in Polish there are 2 plural forms and hence 3
resulting suffixes: "_one", "_few", "_many".
Note on transition period: in the absence of expected suffixed variants
react-i18next will use non-suffixed one (if present) so existing
translations will continue to work just fine even if they happen to be
grammatically imperfect. Translators can provide proper plural forms in
once this change is merged and Weblate will then replace non-suffixed
labels with the suffixed ones.
We also have to chown the attachments directory otherwise the docker container does not start and crashes.
BTW, all that should be automated at the container creation.
Because it took me at least an hour to understand that the only way to accomplish that chown command was to first launch the container as root, run the commands, and only then edit docker-compose.yml to add uid/gid. After that I could restart the container and it would now not crash.
The flag --cache-file and its argument need to be passed as two separate
arguments, otherwise it gets parsed as a single long flag and results in
an "incorrect usage" error.
The pvc needs to be mounted to actually get used.
I didn't understand why the `ntfy publish --debug topic message` command don't choose the default-host I entered in `/etc/ntfy/client.yml`.
If command is run as sudo -> config file = `/etc/ntfy/client.yml`
If command is run as non-sudo -> config file = `~/.config/ntfy/client.yml`
I think this is an important precision for users.
With this change, any developer can simply open a development environment in Gitpod. The environment has docs, web, and binary being built on every code change.
Also included the vscode extensions for Go and Docker.
Signed-off-by: Yarden Shoham <hrsi88@gmail.com>
Added a new button. When clicked it'll generate a random alphanumeric string and append to the current topic (or replace if empty).
Signed-off-by: Yarden Shoham <hrsi88@gmail.com>
Use netip.Addr instead of storing addresses as strings. This requires
conversions at the database level and in tests, but is more memory
efficient otherwise, and facilitates the following.
Parse rate limit exemptions as netip.Prefix. This allows storing IP
ranges in the exemption list. Regular IP addresses (entered explicitly
or resolved from hostnames) are IPV4/32, denoting a range of one
address.
sequentially and in-order.
If this is not set, make -j2 web or higher job counts will
cause the build to fail as some dependencies are not expressed
directly on the dependent tasks but as a dependency list
on a parent task.
Alternatively one could add the required dependencies for each
task separately, but that would factually sequentiallize the
build, so there's no real difference except this approach
fixes all dependency chains globally.
Commit 4a6aca4 changed the directory structure, this pull requests updates screenshot URLs.
Feel free to disregard, I am new to submitting pull requests.
Looks great!
Chase
**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 or cost**.
It's also open source (as you can plainly see) if you want to run your own.
**ntfy** (pronounced "*notify*") is a simple HTTP-based [pub-sub](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern)
notification service. With ntfy, you can**send notifications to your phone or desktop via scripts** from any computer,
**without having to sign up or pay any fees**. If you'd like to run your own instance of the service, you can easily do
so since ntfy is open source.
I run a free version of it at **[ntfy.sh](https://ntfy.sh)**, and there's an [opensource](https://github.com/binwiederhier/ntfy-android) [Android app](https://play.google.com/store/apps/details?id=io.heckel.ntfy)
too.
You can access the free version of ntfy at **[ntfy.sh](https://ntfy.sh)**. There is also an [open-source Android app](https://github.com/binwiederhier/ntfy-android)
available on [Google Play](https://play.google.com/store/apps/details?id=io.heckel.ntfy) or [F-Droid](https://f-droid.org/en/packages/io.heckel.ntfy/),
as well as an [open source iOS app](https://github.com/binwiederhier/ntfy-ios) available on the [App Store](https://apps.apple.com/us/app/ntfy/id1625396347).
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for
everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity
and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste,
color, religion, or sexual identity and orientation.
**We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.**
_Please be sure to read the complete [Code of Conduct](CODE_OF_CONDUCT.md)._
## License
Made with ❤️ by [Philipp C. Heckel](https://heckel.io).
The project is dual licensed under the [Apache License 2.0](LICENSE) and the [GPLv2 License](LICENSE.GPLv2).
Thirdparty libraries and resources:
* [github.com/urfave/cli/v2](https://github.com/urfave/cli/v2) (MIT) is used to drive the CLI
* [Mixkit sound](https://mixkit.co/free-sound-effects/notification/) (Mixkit Free License) used as notification sound
* [Lato Font](https://www.latofonts.com/) (OFL) is used as a font in the Web UI
Third-party libraries and resources:
* [github.com/urfave/cli](https://github.com/urfave/cli) (MIT) is used to drive the CLI
* [Mixkit sounds](https://mixkit.co/free-sound-effects/notification/) (Mixkit Free License) are used as notification sounds
* [Sounds from notificationsounds.com](https://notificationsounds.com) (Creative Commons Attribution) are used as notification sounds
* [Roboto Font](https://fonts.google.com/specimen/Roboto) (Apache 2.0) is used as a font in everything web
* [React](https://reactjs.org/) (MIT) is used for the web app
* [Material UI components](https://mui.com/) (MIT) are used in the web app
* [MUI dashboard template](https://github.com/mui/material-ui/tree/master/docs/data/material/getting-started/templates/dashboard) (MIT) was used as a basis for the web app
* [Dexie.js](https://github.com/dexie/Dexie.js) (Apache 2.0) is used for web app persistence in IndexedDB
* [GoReleaser](https://goreleaser.com/) (MIT) is used to create releases
* [go-smtp](https://github.com/emersion/go-smtp) (MIT) is used to receive e-mails
* [stretchr/testify](https://github.com/stretchr/testify) (MIT) is used for unit and integration tests
* [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) (MIT) is used to provide the persistent message cache
* [Firebase Admin SDK](https://github.com/firebase/firebase-admin-go) (Apache 2.0) is used to send FCM messages
* [github/gemoji](https://github.com/github/gemoji) (MIT) is used for emoji support (specifically the [emoji.json](https://raw.githubusercontent.com/github/gemoji/master/db/emoji.json) file)
* [Lightbox with vanilla JS](https://yossiabramov.com/blog/vanilla-js-lightbox)
* [Lightbox with vanilla JS](https://yossiabramov.com/blog/vanilla-js-lightbox) as a lightbox on the landing page
* [HTTP middleware for gzip compression](https://gist.github.com/CJEnright/bc2d8b8dc0c1389a9feeddb110f822d7) (MIT) is used for serving static files
* [Regex for auto-linking](https://github.com/bryanwoods/autolink-js) (MIT) is used to highlight links (the library is not used)
require.Contains(t,stdout.String(),"user * (role: anonymous, tier: none)\n- no topic-specific permissions\n- no access to any (other) topics (server config)")
Before:initConfigFileInputSource("config",flagsServe),// DEPRECATED, see deprecation notice
Flags:flagsServe,// DEPRECATED, see deprecation notice
Commands:[]*cli.Command{
cmdServe,
cmdPublish,
cmdSubscribe,
},
Commands:commands,
Flags:flagsDefault,
Before:initLogFunc,
}
}
funcexecMainApp(c*cli.Context)error{
fmt.Fprintln(c.App.ErrWriter,"\x1b[1;33mDeprecation notice: Please run the server using 'ntfy serve'; see 'ntfy -h' for help.\x1b[0m")
fmt.Fprintln(c.App.ErrWriter,"\x1b[1;33mThis way of running the server will be removed March 2022. See https://ntfy.sh/docs/deprecations/ for details.\x1b[0m")
returnexecServe(c)
}
// initConfigFileInputSource is like altsrc.InitInputSourceWithContext and altsrc.NewYamlSourceFromFlagFunc, but checks
// if the config flag is exists and only loads it if it does. If the flag is set and the file exists, it fails.
&cli.StringFlag{Name:"priority",Aliases:[]string{"p"},EnvVars:[]string{"NTFY_PRIORITY"},Usage:"priority of the message (1=min, 2=low, 3=default, 4=high, 5=max)"},
&cli.StringFlag{Name:"tags",Aliases:[]string{"tag","T"},EnvVars:[]string{"NTFY_TAGS"},Usage:"comma separated list of tags and emojis"},
&cli.StringFlag{Name:"click",Aliases:[]string{"U"},EnvVars:[]string{"NTFY_CLICK"},Usage:"URL to open when notification is clicked"},
&cli.StringFlag{Name:"icon",Aliases:[]string{"i"},EnvVars:[]string{"NTFY_ICON"},Usage:"URL to use as notification icon"},
&cli.StringFlag{Name:"actions",Aliases:[]string{"A"},EnvVars:[]string{"NTFY_ACTIONS"},Usage:"actions JSON array or simple definition"},
&cli.StringFlag{Name:"attach",Aliases:[]string{"a"},EnvVars:[]string{"NTFY_ATTACH"},Usage:"URL to send as an external attachment"},
&cli.BoolFlag{Name:"markdown",Aliases:[]string{"md"},EnvVars:[]string{"NTFY_MARKDOWN"},Usage:"Message is formatted as Markdown"},
&cli.StringFlag{Name:"template",Aliases:[]string{"tpl"},EnvVars:[]string{"NTFY_TEMPLATE"},Usage:"use templates to transform JSON message body"},
&cli.StringFlag{Name:"filename",Aliases:[]string{"name","n"},EnvVars:[]string{"NTFY_FILENAME"},Usage:"filename for the attachment"},
&cli.StringFlag{Name:"file",Aliases:[]string{"f"},EnvVars:[]string{"NTFY_FILE"},Usage:"file to upload as an attachment"},
&cli.StringFlag{Name:"email",Aliases:[]string{"mail","e"},EnvVars:[]string{"NTFY_EMAIL"},Usage:"also send to e-mail address"},
&cli.StringFlag{Name:"user",Aliases:[]string{"u"},EnvVars:[]string{"NTFY_USER"},Usage:"username[:password] used to auth against the server"},
&cli.StringFlag{Name:"token",Aliases:[]string{"k"},EnvVars:[]string{"NTFY_TOKEN"},Usage:"access token used to auth against the server"},
&cli.IntFlag{Name:"wait-pid",Aliases:[]string{"wait_pid","pid"},EnvVars:[]string{"NTFY_WAIT_PID"},Usage:"wait until PID exits before publishing"},
&cli.BoolFlag{Name:"wait-cmd",Aliases:[]string{"wait_cmd","cmd","done"},EnvVars:[]string{"NTFY_WAIT_CMD"},Usage:"run command and wait until it finishes before publishing"},
&cli.BoolFlag{Name:"no-cache",Aliases:[]string{"no_cache","C"},EnvVars:[]string{"NTFY_NO_CACHE"},Usage:"do not cache message server-side"},
&cli.BoolFlag{Name:"no-firebase",Aliases:[]string{"no_firebase","F"},EnvVars:[]string{"NTFY_NO_FIREBASE"},Usage:"do not forward message to Firebase"},
&cli.BoolFlag{Name:"quiet",Aliases:[]string{"q"},EnvVars:[]string{"NTFY_QUIET"},Usage:"do not print message"},
require.Equal(t,`command failed: does-not-exist-no-really "really though", error: exec: "does-not-exist-no-really": executable file not found in $PATH`,err.Error())
altsrc.NewStringFlag(&cli.StringFlag{Name:"base-url",Aliases:[]string{"B"},EnvVars:[]string{"NTFY_BASE_URL"},Usage:"externally visible base URL for this host (e.g. https://ntfy.sh)"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"listen-http",Aliases:[]string{"l"},EnvVars:[]string{"NTFY_LISTEN_HTTP"},Value:server.DefaultListenHTTP,Usage:"ip:port used to as HTTP listen address"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"listen-https",Aliases:[]string{"L"},EnvVars:[]string{"NTFY_LISTEN_HTTPS"},Usage:"ip:port used to as HTTPS listen address"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"key-file",Aliases:[]string{"K"},EnvVars:[]string{"NTFY_KEY_FILE"},Usage:"private key file, if listen-https is set"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"cert-file",Aliases:[]string{"E"},EnvVars:[]string{"NTFY_CERT_FILE"},Usage:"certificate file, if listen-https is set"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"firebase-key-file",Aliases:[]string{"F"},EnvVars:[]string{"NTFY_FIREBASE_KEY_FILE"},Usage:"Firebase credentials file; if set additionally publish to FCM topic"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"cache-file",Aliases:[]string{"C"},EnvVars:[]string{"NTFY_CACHE_FILE"},Usage:"cache file used for message caching"}),
altsrc.NewDurationFlag(&cli.DurationFlag{Name:"cache-duration",Aliases:[]string{"b"},EnvVars:[]string{"NTFY_CACHE_DURATION"},Value:server.DefaultCacheDuration,Usage:"buffer messages for this time to allow `since` requests"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"attachment-cache-dir",EnvVars:[]string{"NTFY_ATTACHMENT_CACHE_DIR"},Usage:"cache directory for attached files"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"attachment-total-size-limit",Aliases:[]string{"A"},EnvVars:[]string{"NTFY_ATTACHMENT_TOTAL_SIZE_LIMIT"},DefaultText:"5G",Usage:"limit of the on-disk attachment cache"}),
altsrc.NewDurationFlag(&cli.DurationFlag{Name:"attachment-expiry-duration",Aliases:[]string{"X"},EnvVars:[]string{"NTFY_ATTACHMENT_EXPIRY_DURATION"},Value:server.DefaultAttachmentExpiryDuration,DefaultText:"3h",Usage:"duration after which uploaded attachments will be deleted (e.g. 3h, 20h)"}),
altsrc.NewDurationFlag(&cli.DurationFlag{Name:"keepalive-interval",Aliases:[]string{"k"},EnvVars:[]string{"NTFY_KEEPALIVE_INTERVAL"},Value:server.DefaultKeepaliveInterval,Usage:"interval of keepalive messages"}),
altsrc.NewDurationFlag(&cli.DurationFlag{Name:"manager-interval",Aliases:[]string{"m"},EnvVars:[]string{"NTFY_MANAGER_INTERVAL"},Value:server.DefaultManagerInterval,Usage:"interval of for message pruning and stats printing"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"smtp-sender-addr",EnvVars:[]string{"NTFY_SMTP_SENDER_ADDR"},Usage:"SMTP server address (host:port) for outgoing emails"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"smtp-sender-user",EnvVars:[]string{"NTFY_SMTP_SENDER_USER"},Usage:"SMTP user (if e-mail sending is enabled)"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"smtp-sender-pass",EnvVars:[]string{"NTFY_SMTP_SENDER_PASS"},Usage:"SMTP password (if e-mail sending is enabled)"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"smtp-sender-from",EnvVars:[]string{"NTFY_SMTP_SENDER_FROM"},Usage:"SMTP sender address (if e-mail sending is enabled)"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"smtp-server-listen",EnvVars:[]string{"NTFY_SMTP_SERVER_LISTEN"},Usage:"SMTP server address (ip:port) for incoming emails, e.g. :25"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"smtp-server-domain",EnvVars:[]string{"NTFY_SMTP_SERVER_DOMAIN"},Usage:"SMTP domain for incoming e-mail, e.g. ntfy.sh"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"smtp-server-addr-prefix",EnvVars:[]string{"NTFY_SMTP_SERVER_ADDR_PREFIX"},Usage:"SMTP email address prefix for topics to prevent spam (e.g. 'ntfy-')"}),
altsrc.NewIntFlag(&cli.IntFlag{Name:"global-topic-limit",Aliases:[]string{"T"},EnvVars:[]string{"NTFY_GLOBAL_TOPIC_LIMIT"},Value:server.DefaultTotalTopicLimit,Usage:"total number of topics allowed"}),
altsrc.NewIntFlag(&cli.IntFlag{Name:"visitor-subscription-limit",EnvVars:[]string{"NTFY_VISITOR_SUBSCRIPTION_LIMIT"},Value:server.DefaultVisitorSubscriptionLimit,Usage:"number of subscriptions per visitor"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"visitor-attachment-total-size-limit",EnvVars:[]string{"NTFY_VISITOR_ATTACHMENT_TOTAL_SIZE_LIMIT"},Value:"100M",Usage:"total storage limit used for attachments per visitor"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"visitor-attachment-daily-bandwidth-limit",EnvVars:[]string{"NTFY_VISITOR_ATTACHMENT_DAILY_BANDWIDTH_LIMIT"},Value:"500M",Usage:"total daily attachment download/upload bandwidth limit per visitor"}),
altsrc.NewIntFlag(&cli.IntFlag{Name:"visitor-request-limit-burst",EnvVars:[]string{"NTFY_VISITOR_REQUEST_LIMIT_BURST"},Value:server.DefaultVisitorRequestLimitBurst,Usage:"initial limit of requests per visitor"}),
altsrc.NewDurationFlag(&cli.DurationFlag{Name:"visitor-request-limit-replenish",EnvVars:[]string{"NTFY_VISITOR_REQUEST_LIMIT_REPLENISH"},Value:server.DefaultVisitorRequestLimitReplenish,Usage:"interval at which burst limit is replenished (one per x)"}),
altsrc.NewIntFlag(&cli.IntFlag{Name:"visitor-email-limit-burst",EnvVars:[]string{"NTFY_VISITOR_EMAIL_LIMIT_BURST"},Value:server.DefaultVisitorEmailLimitBurst,Usage:"initial limit of e-mails per visitor"}),
altsrc.NewDurationFlag(&cli.DurationFlag{Name:"visitor-email-limit-replenish",EnvVars:[]string{"NTFY_VISITOR_EMAIL_LIMIT_REPLENISH"},Value:server.DefaultVisitorEmailLimitReplenish,Usage:"interval at which burst limit is replenished (one per x)"}),
altsrc.NewBoolFlag(&cli.BoolFlag{Name:"behind-proxy",Aliases:[]string{"P"},EnvVars:[]string{"NTFY_BEHIND_PROXY"},Value:false,Usage:"if set, use X-Forwarded-For header to determine visitor IP address (for rate limiting)"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"base-url",Aliases:[]string{"base_url","B"},EnvVars:[]string{"NTFY_BASE_URL"},Usage:"externally visible base URL for this host (e.g. https://ntfy.sh)"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"listen-http",Aliases:[]string{"listen_http","l"},EnvVars:[]string{"NTFY_LISTEN_HTTP"},Value:server.DefaultListenHTTP,Usage:"ip:port used as HTTP listen address"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"listen-https",Aliases:[]string{"listen_https","L"},EnvVars:[]string{"NTFY_LISTEN_HTTPS"},Usage:"ip:port used as HTTPS listen address"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"listen-unix",Aliases:[]string{"listen_unix","U"},EnvVars:[]string{"NTFY_LISTEN_UNIX"},Usage:"listen on unix socket path"}),
altsrc.NewIntFlag(&cli.IntFlag{Name:"listen-unix-mode",Aliases:[]string{"listen_unix_mode"},EnvVars:[]string{"NTFY_LISTEN_UNIX_MODE"},DefaultText:"system default",Usage:"file permissions of unix socket, e.g. 0700"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"key-file",Aliases:[]string{"key_file","K"},EnvVars:[]string{"NTFY_KEY_FILE"},Usage:"private key file, if listen-https is set"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"cert-file",Aliases:[]string{"cert_file","E"},EnvVars:[]string{"NTFY_CERT_FILE"},Usage:"certificate file, if listen-https is set"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"firebase-key-file",Aliases:[]string{"firebase_key_file","F"},EnvVars:[]string{"NTFY_FIREBASE_KEY_FILE"},Usage:"Firebase credentials file; if set additionally publish to FCM topic"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"cache-file",Aliases:[]string{"cache_file","C"},EnvVars:[]string{"NTFY_CACHE_FILE"},Usage:"cache file used for message caching"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"cache-duration",Aliases:[]string{"cache_duration","b"},EnvVars:[]string{"NTFY_CACHE_DURATION"},Value:util.FormatDuration(server.DefaultCacheDuration),Usage:"buffer messages for this time to allow `since` requests"}),
altsrc.NewIntFlag(&cli.IntFlag{Name:"cache-batch-size",Aliases:[]string{"cache_batch_size"},EnvVars:[]string{"NTFY_BATCH_SIZE"},Usage:"max size of messages to batch together when writing to message cache (if zero, writes are synchronous)"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"cache-batch-timeout",Aliases:[]string{"cache_batch_timeout"},EnvVars:[]string{"NTFY_CACHE_BATCH_TIMEOUT"},Value:util.FormatDuration(server.DefaultCacheBatchTimeout),Usage:"timeout for batched async writes to the message cache (if zero, writes are synchronous)"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"cache-startup-queries",Aliases:[]string{"cache_startup_queries"},EnvVars:[]string{"NTFY_CACHE_STARTUP_QUERIES"},Usage:"queries run when the cache database is initialized"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"auth-file",Aliases:[]string{"auth_file","H"},EnvVars:[]string{"NTFY_AUTH_FILE"},Usage:"auth database file used for access control"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"auth-startup-queries",Aliases:[]string{"auth_startup_queries"},EnvVars:[]string{"NTFY_AUTH_STARTUP_QUERIES"},Usage:"queries run when the auth database is initialized"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"auth-default-access",Aliases:[]string{"auth_default_access","p"},EnvVars:[]string{"NTFY_AUTH_DEFAULT_ACCESS"},Value:"read-write",Usage:"default permissions if no matching entries in the auth database are found"}),
altsrc.NewStringSliceFlag(&cli.StringSliceFlag{Name:"auth-access",Aliases:[]string{"auth_access"},EnvVars:[]string{"NTFY_AUTH_ACCESS"},Usage:"pre-provisioned declarative access control entries"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"attachment-cache-dir",Aliases:[]string{"attachment_cache_dir"},EnvVars:[]string{"NTFY_ATTACHMENT_CACHE_DIR"},Usage:"cache directory for attached files"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"attachment-total-size-limit",Aliases:[]string{"attachment_total_size_limit","A"},EnvVars:[]string{"NTFY_ATTACHMENT_TOTAL_SIZE_LIMIT"},Value:util.FormatSize(server.DefaultAttachmentTotalSizeLimit),Usage:"limit of the on-disk attachment cache"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"attachment-expiry-duration",Aliases:[]string{"attachment_expiry_duration","X"},EnvVars:[]string{"NTFY_ATTACHMENT_EXPIRY_DURATION"},Value:util.FormatDuration(server.DefaultAttachmentExpiryDuration),Usage:"duration after which uploaded attachments will be deleted (e.g. 3h, 20h)"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"template-dir",Aliases:[]string{"template_dir"},EnvVars:[]string{"NTFY_TEMPLATE_DIR"},Value:server.DefaultTemplateDir,Usage:"directory to load named message templates from"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"keepalive-interval",Aliases:[]string{"keepalive_interval","k"},EnvVars:[]string{"NTFY_KEEPALIVE_INTERVAL"},Value:util.FormatDuration(server.DefaultKeepaliveInterval),Usage:"interval of keepalive messages"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"manager-interval",Aliases:[]string{"manager_interval","m"},EnvVars:[]string{"NTFY_MANAGER_INTERVAL"},Value:util.FormatDuration(server.DefaultManagerInterval),Usage:"interval of for message pruning and stats printing"}),
altsrc.NewStringSliceFlag(&cli.StringSliceFlag{Name:"disallowed-topics",Aliases:[]string{"disallowed_topics"},EnvVars:[]string{"NTFY_DISALLOWED_TOPICS"},Usage:"topics that are not allowed to be used"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"web-root",Aliases:[]string{"web_root"},EnvVars:[]string{"NTFY_WEB_ROOT"},Value:"/",Usage:"sets root of the web app (e.g. /, or /app), or disables it (disable)"}),
altsrc.NewBoolFlag(&cli.BoolFlag{Name:"enable-signup",Aliases:[]string{"enable_signup"},EnvVars:[]string{"NTFY_ENABLE_SIGNUP"},Value:false,Usage:"allows users to sign up via the web app, or API"}),
altsrc.NewBoolFlag(&cli.BoolFlag{Name:"enable-login",Aliases:[]string{"enable_login"},EnvVars:[]string{"NTFY_ENABLE_LOGIN"},Value:false,Usage:"allows users to log in via the web app, or API"}),
altsrc.NewBoolFlag(&cli.BoolFlag{Name:"enable-reservations",Aliases:[]string{"enable_reservations"},EnvVars:[]string{"NTFY_ENABLE_RESERVATIONS"},Value:false,Usage:"allows users to reserve topics (if their tier allows it)"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"upstream-base-url",Aliases:[]string{"upstream_base_url"},EnvVars:[]string{"NTFY_UPSTREAM_BASE_URL"},Value:"",Usage:"forward poll request to an upstream server, this is needed for iOS push notifications for self-hosted servers"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"upstream-access-token",Aliases:[]string{"upstream_access_token"},EnvVars:[]string{"NTFY_UPSTREAM_ACCESS_TOKEN"},Value:"",Usage:"access token to use for the upstream server; needed only if upstream rate limits are exceeded or upstream server requires auth"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"smtp-sender-addr",Aliases:[]string{"smtp_sender_addr"},EnvVars:[]string{"NTFY_SMTP_SENDER_ADDR"},Usage:"SMTP server address (host:port) for outgoing emails"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"smtp-sender-user",Aliases:[]string{"smtp_sender_user"},EnvVars:[]string{"NTFY_SMTP_SENDER_USER"},Usage:"SMTP user (if e-mail sending is enabled)"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"smtp-sender-pass",Aliases:[]string{"smtp_sender_pass"},EnvVars:[]string{"NTFY_SMTP_SENDER_PASS"},Usage:"SMTP password (if e-mail sending is enabled)"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"smtp-sender-from",Aliases:[]string{"smtp_sender_from"},EnvVars:[]string{"NTFY_SMTP_SENDER_FROM"},Usage:"SMTP sender address (if e-mail sending is enabled)"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"smtp-server-listen",Aliases:[]string{"smtp_server_listen"},EnvVars:[]string{"NTFY_SMTP_SERVER_LISTEN"},Usage:"SMTP server address (ip:port) for incoming emails, e.g. :25"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"smtp-server-domain",Aliases:[]string{"smtp_server_domain"},EnvVars:[]string{"NTFY_SMTP_SERVER_DOMAIN"},Usage:"SMTP domain for incoming e-mail, e.g. ntfy.sh"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"smtp-server-addr-prefix",Aliases:[]string{"smtp_server_addr_prefix"},EnvVars:[]string{"NTFY_SMTP_SERVER_ADDR_PREFIX"},Usage:"SMTP email address prefix for topics to prevent spam (e.g. 'ntfy-')"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"twilio-account",Aliases:[]string{"twilio_account"},EnvVars:[]string{"NTFY_TWILIO_ACCOUNT"},Usage:"Twilio account SID, used for phone calls, e.g. AC123..."}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"twilio-phone-number",Aliases:[]string{"twilio_phone_number"},EnvVars:[]string{"NTFY_TWILIO_PHONE_NUMBER"},Usage:"Twilio number to use for outgoing calls"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"twilio-verify-service",Aliases:[]string{"twilio_verify_service"},EnvVars:[]string{"NTFY_TWILIO_VERIFY_SERVICE"},Usage:"Twilio Verify service ID, used for phone number verification"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"message-size-limit",Aliases:[]string{"message_size_limit"},EnvVars:[]string{"NTFY_MESSAGE_SIZE_LIMIT"},Value:util.FormatSize(server.DefaultMessageSizeLimit),Usage:"size limit for the message (see docs for limitations)"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"message-delay-limit",Aliases:[]string{"message_delay_limit"},EnvVars:[]string{"NTFY_MESSAGE_DELAY_LIMIT"},Value:util.FormatDuration(server.DefaultMessageDelayMax),Usage:"max duration a message can be scheduled into the future"}),
altsrc.NewIntFlag(&cli.IntFlag{Name:"global-topic-limit",Aliases:[]string{"global_topic_limit","T"},EnvVars:[]string{"NTFY_GLOBAL_TOPIC_LIMIT"},Value:server.DefaultTotalTopicLimit,Usage:"total number of topics allowed"}),
altsrc.NewIntFlag(&cli.IntFlag{Name:"visitor-subscription-limit",Aliases:[]string{"visitor_subscription_limit"},EnvVars:[]string{"NTFY_VISITOR_SUBSCRIPTION_LIMIT"},Value:server.DefaultVisitorSubscriptionLimit,Usage:"number of subscriptions per visitor"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"visitor-attachment-total-size-limit",Aliases:[]string{"visitor_attachment_total_size_limit"},EnvVars:[]string{"NTFY_VISITOR_ATTACHMENT_TOTAL_SIZE_LIMIT"},Value:util.FormatSize(server.DefaultVisitorAttachmentTotalSizeLimit),Usage:"total storage limit used for attachments per visitor"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"visitor-attachment-daily-bandwidth-limit",Aliases:[]string{"visitor_attachment_daily_bandwidth_limit"},EnvVars:[]string{"NTFY_VISITOR_ATTACHMENT_DAILY_BANDWIDTH_LIMIT"},Value:"500M",Usage:"total daily attachment download/upload bandwidth limit per visitor"}),
altsrc.NewIntFlag(&cli.IntFlag{Name:"visitor-request-limit-burst",Aliases:[]string{"visitor_request_limit_burst"},EnvVars:[]string{"NTFY_VISITOR_REQUEST_LIMIT_BURST"},Value:server.DefaultVisitorRequestLimitBurst,Usage:"initial limit of requests per visitor"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"visitor-request-limit-replenish",Aliases:[]string{"visitor_request_limit_replenish"},EnvVars:[]string{"NTFY_VISITOR_REQUEST_LIMIT_REPLENISH"},Value:util.FormatDuration(server.DefaultVisitorRequestLimitReplenish),Usage:"interval at which burst limit is replenished (one per x)"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"visitor-request-limit-exempt-hosts",Aliases:[]string{"visitor_request_limit_exempt_hosts"},EnvVars:[]string{"NTFY_VISITOR_REQUEST_LIMIT_EXEMPT_HOSTS"},Value:"",Usage:"hostnames and/or IP addresses of hosts that will be exempt from the visitor request limit"}),
altsrc.NewIntFlag(&cli.IntFlag{Name:"visitor-message-daily-limit",Aliases:[]string{"visitor_message_daily_limit"},EnvVars:[]string{"NTFY_VISITOR_MESSAGE_DAILY_LIMIT"},Value:server.DefaultVisitorMessageDailyLimit,Usage:"max messages per visitor per day, derived from request limit if unset"}),
altsrc.NewIntFlag(&cli.IntFlag{Name:"visitor-email-limit-burst",Aliases:[]string{"visitor_email_limit_burst"},EnvVars:[]string{"NTFY_VISITOR_EMAIL_LIMIT_BURST"},Value:server.DefaultVisitorEmailLimitBurst,Usage:"initial limit of e-mails per visitor"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"visitor-email-limit-replenish",Aliases:[]string{"visitor_email_limit_replenish"},EnvVars:[]string{"NTFY_VISITOR_EMAIL_LIMIT_REPLENISH"},Value:util.FormatDuration(server.DefaultVisitorEmailLimitReplenish),Usage:"interval at which burst limit is replenished (one per x)"}),
altsrc.NewIntFlag(&cli.IntFlag{Name:"visitor-prefix-bits-ipv4",Aliases:[]string{"visitor_prefix_bits_ipv4"},EnvVars:[]string{"NTFY_VISITOR_PREFIX_BITS_IPV4"},Value:server.DefaultVisitorPrefixBitsIPv4,Usage:"number of bits of the IPv4 address to use for rate limiting (default: 32, full address)"}),
altsrc.NewIntFlag(&cli.IntFlag{Name:"visitor-prefix-bits-ipv6",Aliases:[]string{"visitor_prefix_bits_ipv6"},EnvVars:[]string{"NTFY_VISITOR_PREFIX_BITS_IPV6"},Value:server.DefaultVisitorPrefixBitsIPv6,Usage:"number of bits of the IPv6 address to use for rate limiting (default: 64, /64 subnet)"}),
altsrc.NewBoolFlag(&cli.BoolFlag{Name:"behind-proxy",Aliases:[]string{"behind_proxy","P"},EnvVars:[]string{"NTFY_BEHIND_PROXY"},Value:false,Usage:"if set, use forwarded header (e.g. X-Forwarded-For, X-Client-IP) to determine visitor IP address (for rate limiting)"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"proxy-forwarded-header",Aliases:[]string{"proxy_forwarded_header"},EnvVars:[]string{"NTFY_PROXY_FORWARDED_HEADER"},Value:"X-Forwarded-For",Usage:"use specified header to determine visitor IP address (for rate limiting)"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"proxy-trusted-hosts",Aliases:[]string{"proxy_trusted_hosts"},EnvVars:[]string{"NTFY_PROXY_TRUSTED_HOSTS"},Value:"",Usage:"comma-separated list of trusted IP addresses, hosts, or CIDRs to remove from forwarded header"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"stripe-secret-key",Aliases:[]string{"stripe_secret_key"},EnvVars:[]string{"NTFY_STRIPE_SECRET_KEY"},Value:"",Usage:"key used for the Stripe API communication, this enables payments"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"stripe-webhook-key",Aliases:[]string{"stripe_webhook_key"},EnvVars:[]string{"NTFY_STRIPE_WEBHOOK_KEY"},Value:"",Usage:"key required to validate the authenticity of incoming webhooks from Stripe"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"billing-contact",Aliases:[]string{"billing_contact"},EnvVars:[]string{"NTFY_BILLING_CONTACT"},Value:"",Usage:"e-mail or website to display in upgrade dialog (only if payments are enabled)"}),
altsrc.NewBoolFlag(&cli.BoolFlag{Name:"enable-metrics",Aliases:[]string{"enable_metrics"},EnvVars:[]string{"NTFY_ENABLE_METRICS"},Value:false,Usage:"if set, Prometheus metrics are exposed via the /metrics endpoint"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"metrics-listen-http",Aliases:[]string{"metrics_listen_http"},EnvVars:[]string{"NTFY_METRICS_LISTEN_HTTP"},Usage:"ip:port used to expose the metrics endpoint (implicitly enables metrics)"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"profile-listen-http",Aliases:[]string{"profile_listen_http"},EnvVars:[]string{"NTFY_PROFILE_LISTEN_HTTP"},Usage:"ip:port used to expose the profiling endpoints (implicitly enables profiling)"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"web-push-public-key",Aliases:[]string{"web_push_public_key"},EnvVars:[]string{"NTFY_WEB_PUSH_PUBLIC_KEY"},Usage:"public key used for web push notifications"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"web-push-private-key",Aliases:[]string{"web_push_private_key"},EnvVars:[]string{"NTFY_WEB_PUSH_PRIVATE_KEY"},Usage:"private key used for web push notifications"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"web-push-file",Aliases:[]string{"web_push_file"},EnvVars:[]string{"NTFY_WEB_PUSH_FILE"},Usage:"file used to store web push subscriptions"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"web-push-email-address",Aliases:[]string{"web_push_email_address"},EnvVars:[]string{"NTFY_WEB_PUSH_EMAIL_ADDRESS"},Usage:"e-mail address of sender, required to use browser push services"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"web-push-startup-queries",Aliases:[]string{"web_push_startup_queries"},EnvVars:[]string{"NTFY_WEB_PUSH_STARTUP_QUERIES"},Usage:"queries run when the web push database is initialized"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"web-push-expiry-duration",Aliases:[]string{"web_push_expiry_duration"},EnvVars:[]string{"NTFY_WEB_PUSH_EXPIRY_DURATION"},Value:util.FormatDuration(server.DefaultWebPushExpiryDuration),Usage:"automatically expire unused subscriptions after this time"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"web-push-expiry-warning-duration",Aliases:[]string{"web_push_expiry_warning_duration"},EnvVars:[]string{"NTFY_WEB_PUSH_EXPIRY_WARNING_DURATION"},Value:util.FormatDuration(server.DefaultWebPushExpiryWarningDuration),Usage:"send web push warning notification after this time before expiring unused subscriptions"}),
returnerrors.New("if web push is enabled, web-push-private-key, web-push-public-key, web-push-file, web-push-email-address, and base-url should be set. run 'ntfy webpush keys' to generate keys")
}elseifkeepaliveInterval<5*time.Second{
returnerrors.New("keepalive interval cannot be lower than five seconds")
returnerrors.New("base-url and upstream-base-url cannot be identical, you'll likely want to set upstream-base-url to https://ntfy.sh, see https://ntfy.sh/docs/config/#ios-instant-notifications")
error:"invalid auth-tokens: alice:abcdefghijklmnopqrstuvwxyz12345, token abcdefghijklmnopqrstuvwxyz12345 invalid, use 'ntfy token generate' to generate a random token",
},
}
for_,tt:=rangetests{
t.Run(tt.name,func(t*testing.T){
result,err:=parseTokens(tt.users,tt.input)
require.Error(t,err)
require.Nil(t,result)
assert.Contains(t,err.Error(),tt.error)
})
}
}
funcTestCLI_Serve_Unix_Curl(t*testing.T){
sockFile:=filepath.Join(t.TempDir(),"ntfy.sock")
configFile:=newEmptyFile(t)// Avoid issues with existing server.yml file on system
fmt.Fprintf(c.App.Writer,"- %s%s, %s, accessed from %s at %s%s\n",t.Value,label,expires,t.LastOrigin.String(),t.LastAccess.Format(time.RFC822),provisioned)
}
}
ifusersWithTokens==0{
fmt.Fprintf(c.App.Writer,"no users with tokens\n")
altsrc.NewStringFlag(&cli.StringFlag{Name:"auth-file",Aliases:[]string{"auth_file","H"},EnvVars:[]string{"NTFY_AUTH_FILE"},Usage:"auth database file used for access control"}),
altsrc.NewStringFlag(&cli.StringFlag{Name:"auth-default-access",Aliases:[]string{"auth_default_access","p"},EnvVars:[]string{"NTFY_AUTH_DEFAULT_ACCESS"},Value:"read-write",Usage:"default permissions if no matching entries in the auth database are found"}),
)
varcmdUser=&cli.Command{
Name:"user",
Usage:"Manage/show users",
UsageText:"ntfy user [list|add|remove|change-pass|change-role] ...",
UsageText:"ntfy user add [--role=admin|user] USERNAME\nNTFY_PASSWORD=... ntfy user add [--role=admin|user] USERNAME\nNTFY_PASSWORD_HASH=... ntfy user add [--role=admin|user] USERNAME",
If you like ntfy, please consider sponsoring me via <atarget="_blank"href="https://github.com/sponsors/binwiederhier"><strong>GitHub Sponsors</strong></a>
or <atarget="_blank"href="https://en.liberapay.com/ntfy/"><strong>Liberapay</strong></a>
Then either follow the steps for building with or without Firebase.
### Building without Firebase (F-Droid flavor)
Without Firebase, you may want to still change the default `app_base_url` in [strings.xml](https://github.com/binwiederhier/ntfy-android/blob/main/app/src/main/res/values/strings.xml)
### BuildF-Droid flavor (no FCM)
!!! info
I do build the ntfy Android app using IntelliJ IDEA (Android Studio), so I don't know if these Gradle commands will
work without issues. Please give me feedback if it does/doesn't work for you.
Without Firebase, you may want to still change the default `app_base_url` in [values.xml](https://github.com/binwiederhier/ntfy-android/blob/main/app/src/main/res/values/values.xml)
if you're self-hosting the server. Then run:
```
# Remove Google dependencies (FCM)
sed -i -e '/google-services/d' build.gradle
sed -i -e '/google-services/d' app/build.gradle
# To build an unsigned .apk (app/build/outputs/apk/fdroid/*.apk)
./gradlew assembleFdroidRelease
@@ -56,16 +351,96 @@ if you're self-hosting the server. Then run:
./gradlew bundleFdroidRelease
```
### Building with Firebase (FCM, Google Play flavor)
### Build Play flavor (FCM)
!!! info
I do build the ntfy Android app using IntelliJ IDEA (Android Studio), so I don't know if these Gradle commands will
work without issues. Please give me feedback if it does/doesn't work for you.
To build your own version with Firebase, you must:
* Create a Firebase/FCM account
* Place your account file at `app/google-services.json`
* And change `app_base_url` in [strings.xml](https://github.com/binwiederhier/ntfy-android/blob/main/app/src/main/res/values/strings.xml)
* And change `app_base_url` in [values.xml](https://github.com/binwiederhier/ntfy-android/blob/main/app/src/main/res/values/values.xml)
* Then run:
```
# To build an unsigned .apk (app/build/outputs/apk/play/*.apk)
# To build an unsigned .apk (app/build/outputs/apk/play/release/*.apk)
./gradlew assemblePlayRelease
# To build a bundle .aab (app/play/release/*.aab)
./gradlew bundlePlayRelease
```
## iOS app
Building the iOS app is very involved. Please report any inconsistencies or issues with it. The requirements are
strictly based off of my development on this app. There may be other versions of macOS / XCode that work.
### Requirements
1. macOS Monterey or later
1. XCode 13.2+
1. A physical iOS device (for push notifications, Firebase does not work in the XCode simulator)
1. Firebase account
1. Apple Developer license? (I forget if it's possible to do testing without purchasing the license)
### Apple setup
!!! info
Along with this step, the [PLIST Deployment](#plist-config) step is also required
for these changes to take effect in the iOS app.
1. [Create a new key in Apple Developer Member Center](https://developer.apple.com/account/resources/authkeys/add)
1. Select "Apple Push Notifications service (APNs)"
1. Download the newly created key (should have a file name similar to `AuthKey_ZZZZZZ.p8`, where `ZZZZZZ` is the **Key ID**)
1. Record your **Team ID** - it can be seen in the top-right corner of the page, or on your Account > Membership page
1. Next, navigate to "Project Settings" in the firebase console for your project, and select the iOS app you created. Then, click "Cloud Messaging" in the left sidebar, and scroll down to the "APNs Authentication Key" section. Click "Upload Key", and upload the key you downloaded from Apple Developer.
!!! warning
If you don't do the above setups for APNS, **notifications will not post instantly or sometimes at all**. This is because of the missing APNS key, which is required for firebase to send notifications to the iOS app. See below for a snip from the firebase docs.
If you don't have an APNs authentication key, you can still send notifications to iOS devices, but they won't be delivered
instantly. Instead, they'll be delivered when the device wakes up to check for new notifications or when your application
sends a firebase request to check for them. The time to check for new notifications can vary from a few seconds to hours,
days or even weeks. Enabling APNs authentication keys ensures that notifications are delivered instantly and is strongly
recommended.
### Firebase setup
1. If you haven't already, create a Google / Firebase account
1. Visit the [Firebase console](https://console.firebase.google.com)
1. Create a new Firebase project:
1. Enter a project name
1. Disable Google Analytics (currently iOS app does not support analytics)
1. On the "Project settings" page, add an iOS app
1. Apple bundle ID - "com.copephobia.ntfy-ios" (this can be changed to match XCode's ntfy.sh target > "Bundle Identifier" value)
1. Register the app
1. Download the config file - GoogleInfo.plist (this will need to be included in the ntfy-ios repository / XCode)
1. Generate a new service account private key for the ntfy server
1. Go to "Project settings" > "Service accounts"
1. Click "Generate new private key" to generate and download a private key to use for sending messages via the ntfy server
### ntfy server
Note that the ntfy server is not officially supported on macOS. It should, however, be able to run on macOS using these
steps:
1. If not already made, make the `/etc/ntfy/` directory and move the service account private key to that folder
1. Copy the `server/server.yml` file from the ntfy repository to `/etc/ntfy/`
1. Modify the `/etc/ntfy/server.yml` file `firebase-key-file` value to the path of the private key
1. Install go: `brew install go`
1. In the ntfy repository, run `make cli-darwin-server`.
### XCode setup
1. Follow step 4 of [Add Firebase to your Apple project](https://firebase.google.com/docs/ios/setup) to install the
`firebase-ios-sdk` in XCode, if it's not already present - you can select any packages in addition to Firebase Core / Firebase Messaging
1. Similarly, install the SQLite.swift package dependency in XCode
1. When running the debug build, ensure XCode is pointed to the connected iOS device - registering for push notifications does not work in the iOS simulators
### PLIST config
To have instant notifications/better notification delivery when using firebase, you will need to add the
`GoogleService-Info.plist` file to your project. Here's how to do that:
1. In XCode, find the NTFY app target. **Not** the NSE app target.
1. Find the Asset/ folder in the project navigator
1. Drag the `GoogleService-Info.plist` file into the Asset/ folder that you get from the firebase console. It can be
found in the "Project settings" > "General" > "Your apps" with a button labled "GoogleService-Info.plist"
The environment variable `WATCHTOWER_NOTIFICATION_SKIP_TITLE` is required to prevent Watchtower from [replacing the `title` query parameter](https://containrrr.dev/watchtower/notifications/#settings). If omitted, the provided notification title will not be used.
Or, if you only want to send notifications using shoutrrr:
<!-- Sonarr v4 is in beta as of May 2023, should be updated to remove v3 reference when stable -->
Radarr, Prowlarr, and Sonarr v4 support ntfy natively under Settings > Connect.
Sonarr v3, Readarr, and SABnzbd support custom scripts for downloads, warnings, grabs, etc.
Some simple bash scripts to achieve this are kindly provided in [nickexyz's ntfy-shellscripts repository](https://github.com/nickexyz/ntfy-shellscripts).
## Node-RED
You can use the HTTP request node to send messages with [Node-RED](https://nodered.org), some examples:
<details>
<summary>Example: Send a message (click to expand)</summary>
Here is an example for the configuration.yml file to setup a REST notify component.
Since Home Assistant is going to POST JSON, you need to specify the root of your ntfy resource.
```yaml
notify:
- name: ntfy
platform: rest
method: POST_JSON
data:
topic: YOUR_NTFY_TOPIC
title_param_name: title
message_param_name: message
resource: https://ntfy.sh
```
If you need to authenticate to your ntfy resource, define the authentication, username and password as below:
```yaml
notify:
- name: ntfy
platform: rest
method: POST_JSON
authentication: basic
username: YOUR_USERNAME
password: YOUR_PASSWORD
data:
topic: YOUR_NTFY_TOPIC
title_param_name: title
message_param_name: message
resource: https://ntfy.sh
```
If you need to add any other [ntfy specific parameters](https://ntfy.sh/docs/publish/#publish-as-json) such as priority, tags, etc., add them to the `data` array in the example yml. For example:
```yaml
notify:
- name: ntfy
platform: rest
method: POST_JSON
data:
topic: YOUR_NTFY_TOPIC
priority: 4
title_param_name: title
message_param_name: message
resource: https://ntfy.sh
```
## Uptime Kuma
Go to your [Uptime Kuma](https://github.com/louislam/uptime-kuma) Settings > Notifications, click on **Setup Notification**.
Then set your desired **title** (e.g. "Uptime Kuma"), **ntfy topic**, **Server URL** and **priority (1-5)**:
Go to your [UptimeRobot](https://github.com/uptimerobot) My Settings > Alert Contacts > Add Alert Contact
Select **Alert Contact Type** = Webhook. Then set your desired **Friendly Name** (e.g. "ntfy-sh-UP"), **URL to Notify**, **POST value** and select checkbox **Send as JSON (application/json)**. Make sure to send the JSON POST request to ntfy.domain.com without the topic name in the url and include the "topic" name in the JSON body.
Add notification on Rundeck (attachment type must be: `Attached as file to email`):

## Traccar
This will only work on selfhosted [traccar](https://www.traccar.org/) ([Github](https://github.com/traccar/traccar)) instances, as you need to be able to set `sms.http.*` keys, which is not possible through the UI attributes
The easiest way to integrate traccar with ntfy, is to configure ntfy as the SMS provider for your instance. You then can set your ntfy topic as your account's phone number in traccar. Sending the email notifications to ntfy will not work, as ntfy does not support HTML emails.
**Info:** Add a phone number to your traccar account not in device, as otherwise it will not try to send SMS.
**Caution:** JSON publishing is only possible, when POST-ing to the root URL of the ntfy instance. (see [documentation](publish.md#publish-as-json))
```xml
<entry key='sms.http.url'>https://ntfy.sh</entry>
<entry key='sms.http.template'>
{
"topic": "{phone}",
"message": "{message}"
}
</entry>
```
If [access control](config.md#access-control) is enabled, and the target topic does not support anonymous writes, you'll also have to provide an authorization header, for example in form of a privileged token
or by simply providing traccar with a valid username/password combination.
```xml
<entry key='sms.http.user'>phil</entry>
<entry key='sms.http.password'>mypass</entry>
```
## Terminal Notifications for Long-Running Commands
This example provides a simple way to send notifications using [ntfy.sh](https://ntfy.sh) when a terminal command completes. It includes success or failure indicators based on the command's exit status.
Store your ntfy.sh bearer token securely if access control is enabled:
```sh
echo "your_bearer_token_here" > ~/.ntfy_token
chmod 600 ~/.ntfy_token
```
Add the following function and alias to your `.bashrc` or `.bash_profile`:
```sh
# Function for alert notifications using ntfy.sh
notify_via_ntfy() {
local exit_status=$? # Capture the exit status before doing anything else
local token=$(< ~/.ntfy_token) # Securely read the token
@@ -13,36 +13,55 @@ The ntfy server comes as a statically linked binary and is shipped as tarball, d
We support amd64, armv7 and arm64.
1. Install ntfy using one of the methods described below
2. Then (optionally) edit `/etc/ntfy/server.yml` for the server (see [configuration](config.md) or [sample server.yml](https://github.com/binwiederhier/ntfy/blob/main/server/server.yml))
3. Or (optionally) create/edit `~/.config/ntfy/client.yml` (or `/etc/ntfy/client.yml`, see [sample client.yml](https://github.com/binwiederhier/ntfy/blob/main/client/client.yml))
2. Then (optionally) edit `/etc/ntfy/server.yml` for the server (Linux only, see [configuration](config.md) or [sample server.yml](https://github.com/binwiederhier/ntfy/blob/main/server/server.yml))
3. Or (optionally) create/edit `~/.config/ntfy/client.yml` (for the non-root user), `~/Library/Application Support/ntfy/client.yml` (for the macOS non-root user), or `/etc/ntfy/client.yml` (for the root user), see [sample client.yml](https://github.com/binwiederhier/ntfy/blob/main/client/client.yml))
To run the ntfy server, then just run `ntfy serve` (or `systemctl start ntfy` when using the deb/rpm).
To send messages, use `ntfy publish`. To subscribe to topics, use `ntfy subscribe` (see [subscribing via CLI][subscribe/cli.md]
To send messages, use `ntfy publish`. To subscribe to topics, use `ntfy subscribe` (see [subscribing via CLI](subscribe/cli.md)
for details).
## Binaries and packages
If you like tutorials, check out :simple-youtube: [Kris Occhipinti's ntfy install guide](https://www.youtube.com/watch?v=bZzqrX05mNU) on YouTube, or
[Alex's Docker-based setup guide](https://blog.alexsguardian.net/posts/2023/09/12/selfhosting-ntfy/). Both are great
resources to get started. _I am not affiliated with Kris or Alex, I just liked their video/post._
## Linux binaries
Please check out the [releases page](https://github.com/binwiederhier/ntfy/releases) for binaries and
ntfy can be installed using an [AUR package](https://aur.archlinux.org/packages/ntfysh-bin/). You can use an [AUR helper](https://wiki.archlinux.org/title/AUR_helpers) like `paru`, `yay` or others to download, build and install ntfy and keep it up to date.
ntfy can be installed using an [AUR package](https://aur.archlinux.org/packages/ntfysh-bin/).
You can use an [AUR helper](https://wiki.archlinux.org/title/AUR_helpers) like `paru`, `yay` or others to download,
build and install ntfy and keep it up to date.
```
paru -S ntfysh-bin
```
@@ -146,15 +185,71 @@ cd ntfysh-bin
makepkg -si
```
## NixOS / Nix
ntfy is packaged in nixpkgs as `ntfy-sh`. It can be installed by adding the package name to the configuration file and calling `nixos-rebuild`. Alternatively, the following command can be used to install ntfy in the current user environment:
```
nix-env -iA ntfy-sh
```
NixOS also supports [declarative setup of the ntfy server](https://search.nixos.org/options?channel=unstable&show=services.ntfy-sh.enable&from=0&size=50&sort=relevance&type=packages&query=ntfy).
## macOS
The [ntfy CLI](subscribe/cli.md) (`ntfy publish` and `ntfy subscribe` only) is supported on macOS as well.
To install, please [download the tarball](https://github.com/binwiederhier/ntfy/releases/download/v2.13.0/ntfy_2.13.0_darwin_all.tar.gz),
extract it and place it somewhere in your `PATH` (e.g. `/usr/local/bin/ntfy`).
If run as `root`, ntfy will look for its config at `/etc/ntfy/client.yml`. For all other users, it'll look for it at
`~/Library/Application Support/ntfy/client.yml` (sample included in the tarball).
If using a non-root user when running the docker version, be sure to chown the server.yml, user.db, and cache.db files and attachments directory to the same uid/gid.
Alternatively, you may wish to build a customized Docker image that can be run with fewer command-line arguments and without delivering the configuration file separately.
ntfy can be deployed in a Kubernetes cluster with [Kustomize](https://github.com/kubernetes-sigs/kustomize), a tool used
to customize Kubernetes objects using a `kustomization.yaml` file.
1. Create new folder - `ntfy`
2. Add all files listed below
1. `kustomization.yaml` - stores all configmaps and resources used in a deployment
2. `ntfy-deployment.yaml` - define deployment type and its parameters
3. `ntfy-pvc.yaml` - describes how [persistent volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) will be created
4. `ntfy-svc.yaml` - expose application to the internal kubernetes network
5. `ntfy-ingress.yaml` - expose service to outside the network using [ingress controller](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/)
6. `server.yaml` - simple server configuration
3. Replace **TESTNAMESPACE** within `kustomization.yaml` with designated namespace
4. Replace **ntfy.test** within `ntfy-ingress.yaml` with desired DNS name
5. Apply configuration to cluster set in current context:
```bash
go install heckel.io/ntfy@latest
kubectl apply -k /ntfy
```
!!! 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.
=== "kustomization.yaml"
```yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ntfy-deployment.yaml # deployment definition
- ntfy-svc.yaml # service connecting pods to cluster network
- ntfy-pvc.yaml # pvc used to store cache and attachment
- ntfy-ingress.yaml # ingress definition
configMapGenerator: # will parse config from raw config to configmap,it allows for dynamic reload of application if additional app is deployed ie https://github.com/stakater/Reloader
- name: server-config
files:
- server.yml
namespace: TESTNAMESPACE # select namespace for whole application
```
=== "ntfy-deployment.yaml"
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: ntfy-deployment
labels:
app: ntfy-deployment
spec:
revisionHistoryLimit: 1
replicas: 1
selector:
matchLabels:
app: ntfy-pod
template:
metadata:
labels:
app: ntfy-pod
spec:
containers:
- name: ntfy
image: binwiederhier/ntfy:v1.28.0 # set deployed version
args: ["serve"]
env: #example of adjustments made in environmental variables
- name: TZ # set timezone
value: XXXXXXX
- name: NTFY_DEBUG # enable/disable debug
value: "false"
- name: NTFY_LOG_LEVEL # adjust log level
value: INFO
- name: NTFY_BASE_URL # add base url
value: XXXXXXXXXX
ports:
- containerPort: 80
name: http-ntfy
resources:
limits:
memory: 300Mi
cpu: 200m
requests:
cpu: 150m
memory: 150Mi
volumeMounts:
- mountPath: /etc/ntfy
subPath: server.yml
name: config-volume # generated vie configMapGenerator from kustomization file
- mountPath: /var/cache/ntfy
name: cache-volume #cache volume mounted to persistent volume
volumes:
- name: config-volume
configMap: # uses configmap generator to parse server.yml to configmap
name: server-config
- name: cache-volume
persistentVolumeClaim: # stores /cache/ntfy in defined pv
claimName: ntfy-pvc
```
=== "ntfy-pvc.yaml"
```yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ntfy-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path # adjust storage if needed
There are quite a few projects that work with ntfy, integrate ntfy, or have been built around ntfy. It's super exciting to see what you guys have come up with. Feel free to [create a pull request on GitHub](https://github.com/binwiederhier/ntfy/issues) to add your own project here.
I've added a ⭐ to projects or posts that have a significant following, or had a lot of interaction by the community.
## Table of Contents
- [Official integrations](#official-integrations)
- [Integration via HTTP/SMTP/etc.](#integration-via-httpsmtpetc)
- [changedetection.io](https://changedetection.io) ⭐ - Website change detection and notification
- [Home Assistant](https://www.home-assistant.io/integrations/ntfy) ⭐ - Home Assistant is an open-source platform for automating and controlling smart home devices.
- [Healthchecks.io](https://healthchecks.io/) ⭐ - Online service for monitoring regularly running tasks such as cron jobs
- [Apprise](https://github.com/caronc/apprise/wiki/Notify_ntfy) ⭐ - Push notifications that work with just about every platform
- [Uptime Kuma](https://uptime.kuma.pet/) ⭐ - A self-hosted monitoring tool
- [Robusta](https://docs.robusta.dev/master/catalog/sinks/webhook.html) ⭐ - open source platform for Kubernetes troubleshooting
- [borgmatic](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#third-party-monitoring-services) ⭐ - configuration-driven backup software for servers and workstations
- [Radarr](https://radarr.video/) ⭐ - Movie collection manager for Usenet and BitTorrent users
- [Sonarr](https://sonarr.tv/) ⭐ - PVR for Usenet and BitTorrent users
- [Gatus](https://gatus.io/) ⭐ - Automated service health dashboard
- [Automatisch](https://automatisch.io/) ⭐ - Open source Zapier alternative / workflow automation tool
- [FlexGet](https://flexget.com/Plugins/Notifiers/ntfysh) ⭐ - Multipurpose automation tool for all of your media
- [Shoutrrr](https://containrrr.dev/shoutrrr/v0.8/services/ntfy/) ⭐ - Notification library for gophers and their furry friends.
- [EasyMorph](https://help.easymorph.com/doku.php?id=transformations:sendntfymessage) - Visual data transformation and automation tool
- [Monibot](https://monibot.io/) - Monibot monitors your websites, servers and applications and notifies you if something goes wrong.
- [Miniflux](https://miniflux.app/docs/ntfy.html) - Minimalist and opinionated feed reader
- [Beszel](https://beszel.dev/guide/notifications/ntfy) - Server monitoring platform
## Integration via HTTP/SMTP/etc.
- [Watchtower](https://containrrr.dev/watchtower/) ⭐ - Automating Docker container base image updates (see [integration example](examples.md#watchtower-shoutrrr))
- [Jellyfin](https://jellyfin.org/) ⭐ - The Free Software Media System (see [integration example](examples.md#))
- [Overseerr](https://docs.overseerr.dev/using-overseerr/notifications/webhooks) ⭐ - a request management and media discovery tool for Plex (see [integration example](examples.md#jellyseerroverseerr-webhook))
- [Tautulli](https://github.com/Tautulli/Tautulli) ⭐ - Monitoring and tracking tool for Plex (integration [via webhook](https://github.com/Tautulli/Tautulli/wiki/Notification-Agents-Guide#webhook))
- [Mailrise](https://github.com/YoRyan/mailrise) - An SMTP gateway (integration via [Apprise](https://github.com/caronc/apprise/wiki/Notify_ntfy))
- [Proxmox-Ntfy](https://github.com/qtsone/proxmox-ntfy) - Python script that monitors Proxmox tasks and sends notifications using the Ntfy service.
- [Scrutiny](https://github.com/AnalogJ/scrutiny) - WebUI for smartd S.M.A.R.T monitoring. Scrutiny includes shoutrrr/ntfy integration ([see integration README](https://github.com/AnalogJ/scrutiny?tab=readme-ov-file#notifications))
- [UptimeObserver](https://uptimeobserver.com) - Uptime Monitoring tool for Websites, APIs, SSL Certificates, DNS, Domain Names and Ports. [Integration Guide](https://support.uptimeobserver.com/integrations/ntfy/)
- [ntfy-php-library](https://github.com/VerifiedJoseph/ntfy-php-library) - PHP library for sending messages using a ntfy server (PHP)
- [ntfy-notifier](https://github.com/DAcodedBEAT/ntfy-notifier) - Symfony Notifier integration for ntfy (PHP)
- [ntfpy](https://github.com/Nevalicjus/ntfpy) - API Wrapper for ntfy.sh (Python)
- [pyntfy](https://github.com/DP44/pyntfy) - A module for interacting with ntfy notifications (Python)
- [vntfy](https://github.com/lmangani/vntfy) - Barebone V client for ntfy (V)
- [ntfy-middleman](https://github.com/nachotp/ntfy-middleman) - Wraps APIs and send notifications using ntfy.sh on schedule (Python)
- [ntfy-dotnet](https://github.com/nwithan8/ntfy-dotnet) - .NET client library to interact with a ntfy server (C# / .NET)
- [node-ntfy-publish](https://github.com/cityssm/node-ntfy-publish) - A Node package to publish notifications to an ntfy server (Node)
- [ntfy](https://github.com/jonocarroll/ntfy) - Wraps the ntfy API with pipe-friendly tooling (R)
- [ntfy-for-delphi](https://github.com/hazzelnuts/ntfy-for-delphi) - A friendly library to push instant notifications ntfy (Delphi)
- [ntfy](https://github.com/ffflorian/ntfy) - Send notifications over ntfy (JS)
- [ntfy_dart](https://github.com/jr1221/ntfy_dart) - Dart wrapper around the ntfy API (Dart)
- [gotfy](https://github.com/AnthonyHewins/gotfy) - A Go wrapper for the ntfy API (Go)
- [symfony/ntfy-notifier](https://symfony.com/components/NtfyNotifier) ⭐ - Symfony Notifier integration for ntfy (PHP)
- [ntfy-java](https://github.com/MaheshBabu11/ntfy-java/) - A Java package to interact with a ntfy server (Java)
- [aiontfy](https://github.com/tr4nt0r/aiontfy) - Asynchronous client library for publishing and subscribing to ntfy (Python)
## CLIs + GUIs
- [ntfy.sh.sh](https://github.com/mininmobile/ntfy.sh.sh) - Run scripts on ntfy.sh events
- [ntfy-desktop](https://codeberg.org/zvava/ntfy-desktop) - Cross-platform desktop application for ntfy
- [ntfy-desktop](https://github.com/Aetherinox/ntfy-desktop) - Desktop client for Windows, Linux, and MacOS with push notifications
- [ntfy svelte front-end](https://github.com/novatorem/Ntfy) - Front-end built with svelte
- [wio-ntfy-ticker](https://github.com/nachotp/wio-ntfy-ticker) - Ticker display for a ntfy.sh topic
- [ntfysh-windows](https://github.com/lucas-bortoli/ntfysh-windows) - A ntfy client for Windows Desktop
- [ntfyr](https://github.com/haxwithaxe/ntfyr) - A simple commandline tool to send notifications to ntfy
- [ntfy.py](https://github.com/ioqy/ntfy-client-python) - ntfy.py is a simple nfty.sh client for sending notifications
- [wlzntfy](https://github.com/Walzen-Group/ntfy-toaster) - A minimalistic, receive-only toast notification client for Windows 11
- [Ntfy_CSV_Reminders](https://github.com/thiswillbeyourgithub/Ntfy_CSV_Reminders) - A Python tool that sends random-timing phone notifications for recurring tasks by using daily probability checks based on CSV-defined frequencies.
- [Daily Fact Ntfy](https://github.com/thiswillbeyourgithub/Daily_Fact_Ntfy) - Generate [llm](https://github.com/simonw/llm) generated fact every day about any topic you're interested in.
- [ntfyexec](https://github.com/alecthomas/ntfyexec) - Send a notification through ntfy.sh if a command fails
- [Ntfy Desktop](https://github.com/emmaexe/ntfyDesktop) - Fully featured desktop client for Linux, built with Qt and C++.
- [ntfy-long-zsh-command](https://github.com/robfox92/ntfy-long-zsh-command) - Notifies you once a long-running command completes (zsh)
- [ntfy-shellscripts](https://github.com/nickexyz/ntfy-shellscripts) - A few scripts for the ntfy project (Shell)
- [alertmanager-ntfy-relay](https://github.com/therobbielee/alertmanager-ntfy-relay) - ntfy.sh relay for Alertmanager (Go)
- [QuickStatus](https://github.com/corneliusroot/QuickStatus) - A shell script to alert to any immediate problems upon login (Shell)
- [ntfy.el](https://github.com/shombando/ntfy) - Send notifications from Emacs (Emacs)
- [backup-projects](https://gist.github.com/anthonyaxenov/826ba65abbabd5b00196bc3e6af76002) - Stupidly simple backup script for own projects (Shell)
- [grav-plugin-whistleblower](https://github.com/Himmlisch-Studios/grav-plugin-whistleblower) - Grav CMS plugin to get notifications via ntfy (PHP)
- [ntfy-server-status](https://github.com/filip2cz/ntfy-server-status) - Checking if server is online and reporting through ntfy (C)
- [ntfy.sh *arr script](https://github.com/agent-squirrel/nfty-arr-script) - Quick and hacky script to get sonarr/radarr to notify the ntfy.sh service (Shell)
- [website-watcher](https://github.com/muety/website-watcher) - A small tool to watch websites for changes (with XPath support) (Python)
- [siteeagle](https://github.com/tpanum/siteeagle) - A small Python script to monitor websites and notify changes (Python)
- [send_to_phone](https://github.com/whipped-cream/send_to_phone) - Scripts to upload a file to Transfer.sh and ping ntfy with the download link (Python)
- [ntfy Discord bot](https://github.com/jr1221/ntfy_discord_bot) - An advanced modal-based bot for interacting with the ntfy.sh API (Dart)
- [Bettarr Notifications](https://github.com/NiNiyas/Bettarr-Notifications) - Better Notifications for Sonarr and Radarr (Python)
- [Notify me the intruders](https://github.com/nothingbutlucas/notify_me_the_intruders) - Notify you if they are intruders or new connections on your network (Shell)
- [Send GitHub Action to ntfy](https://github.com/NiNiyas/ntfy-action) - Send GitHub Action workflow notifications to ntfy (JS)
- [~xenrox/ntfy-alertmanager](https://hub.xenrox.net/~xenrox/ntfy-alertmanager) - A bridge between ntfy and Alertmanager (Go)
- [pinpox/alertmanager-ntfy](https://github.com/pinpox/alertmanager-ntfy) - Relay prometheus alertmanager alerts to ntfy (Go)
- [alexbakker/alertmanager-ntfy](https://github.com/alexbakker/alertmanager-ntfy) - Service that forwards Prometheus Alertmanager notifications to ntfy (Go)
- [restreamchat2ntfy](https://github.com/kurohuku7/restreamchat2ntfy) - Send restream.io chat to ntfy to check on the Meta Quest (JS)
- [huginn-global-entry-notif](https://github.com/kylezoa/huginn-global-entry-notif) - Checks CBP API for available appointments with Huginn (JSON)
- [ntfyer](https://github.com/KikyTokamuro/ntfyer) - Sending various information to your ntfy topic by time (TypeScript)
- [git-simple-notifier](https://github.com/plamenjm/git-simple-notifier) - Script running git-log, checking for new repositories (Shell)
- [ntfy-to-slack](https://github.com/ozskywalker/ntfy-to-slack) - Tool to subscribe to a ntfy topic and send the messages to a Slack webhook (Go)
- [ansible-ntfy](https://github.com/jpmens/ansible-ntfy) - Ansible action plugin to post JSON messages to ntfy (Python)
- [ntfy-notification-channel](https://github.com/wijourdil/ntfy-notification-channel) - Laravel Notification channel for ntfy (PHP)
- [ntfy_on_a_chip](https://github.com/gergepalfi/ntfy_on_a_chip) - ESP8266 and ESP32 client code to communicate with ntfy
- [ntfy-sdk](https://github.com/yukibtc/ntfy-sdk) - ntfy client library to send notifications (Rust)
- [ntfy_ynh](https://github.com/YunoHost-Apps/ntfy_ynh) - ntfy app for YunoHost
- [woodpecker-ntfy](https://codeberg.org/l-x/woodpecker-ntfy)- Woodpecker CI plugin for sending ntfy notfication from a pipeline (Go)
- [drone-ntfy](https://github.com/Clortox/drone-ntfy) - Drone.io plugin for sending ntfy notifications from a pipeline (Shell)
- [ignition-ntfy-module](https://github.com/Kyvis-Labs/ignition-ntfy-module) - Adds support for sending notifications via a ntfy server to Ignition (Java)
- [maubot-ntfy](https://gitlab.com/999eagle/maubot-ntfy) - Matrix bot to subscribe to ntfy topics and send messages to Matrix (Python)
- [ntfy-wrapper](https://github.com/vict0rsch/ntfy-wrapper) - Wrapper around ntfy (Python)
- [nodebb-plugin-ntfy](https://github.com/NodeBB/nodebb-plugin-ntfy) - Push notifications for NodeBB forums
- [n8n-ntfy](https://github.com/raghavanand98/n8n-ntfy.sh) - n8n community node that lets you use ntfy in your workflows
- [helm-charts](https://github.com/sarab97/helm-charts) - Helm charts of some of the selfhosted services, incl. ntfy
- [ntfy_ansible_role](https://github.com/stevenengland/ntfy_ansible_role) (on [Ansible Galaxy](https://galaxy.ansible.com/stevenengland/ntfy)) - Ansible role to install ntfy
- [easy2ntfy](https://github.com/chromoxdor/easy2ntfy) - Gateway for ESPeasy to receive commands through ntfy and using easyfetch (HTML/JS)
- [ntfy_lite](https://github.com/MPI-IS/ntfy_lite) - Minimalist python API for pushing ntfy notifications (Python)
- [ntfy-browser](https://github.com/johman10/ntfy-browser) - browser extension to receive notifications without having the page open (TypeScript)
- [ntfy-electron](https://github.com/xdpirate/ntfy-electron) - Electron wrapper for the ntfy web app (JS)
- [systemd-ntfy-poweronoff](https://github.com/stendler/systemd-ntfy-poweronoff) - Systemd services to send notifications on system startup, shutdown and service failure
- [msgdrop](https://github.com/jbrubake/msgdrop) - Send and receive encrypted messages (Bash)
- [vigilant](https://github.com/VerifiedJoseph/vigilant) - Monitor RSS/ATOM and JSON feeds, and send push notifications on new entries (PHP)
- [ansible-role-ntfy-alertmanager](https://github.com/bleetube/ansible-role-ntfy-alertmanager) - Ansible role to install xenrox/ntfy-alertmanager
- [NtfyMe-Blender](https://github.com/NotNanook/NtfyMe-Blender) - Blender addon to send notifications to NtfyMe (Python)
- [ntfy-ios-url-share](https://www.icloud.com/shortcuts/be8a7f49530c45f79733cfe3e41887e6) - An iOS shortcut that lets you share URLs easily and quickly.
- [ntfy-ios-filesharing](https://www.icloud.com/shortcuts/fe948d151b2e4ae08fb2f9d6b27d680b) - An iOS shortcut that lets you share files from your share feed to a topic of your choice.
- [systemd-ntfy](https://hackage.haskell.org/package/systemd-ntfy) - monitor a set of systemd services an send a notification to ntfy.sh whenever their status changes
- [RouterOS Scripts](https://git.eworm.de/cgit/routeros-scripts/about/) - a collection of scripts for MikroTik RouterOS
- [ntfy-android-builder](https://github.com/TheBlusky/ntfy-android-builder) - Script for building ntfy-android with custom Firebase configuration (Docker/Shell)
- [jetspotter](https://github.com/vvanouytsel/jetspotter) - send notifications when planes are spotted near you (Go)
- [Notify](https://flathub.org/apps/com.ranfdev.Notify) - Native GTK4 client for ntfy (Rust)
- [notify-via-ntfy](https://exchange.checkmk.com/p/notify-via-ntfy) - Checkmk plugin to send notifications via ntfy (Python)
- [ntfy-java](https://github.com/MaheshBabu11/ntfy-java/) - A Java package to interact with a ntfy server (Java)
- [container-update-check](https://github.com/stendler/container-update-check) - Scripts to check and notify if a podman or docker container image can be updated (Podman/Shell)
- [ignition-combustion-template](https://github.com/stendler/ignition-combustion-template) - Templates and scripts to generate a configuration to automatically setup a system on first boot. Including systemd-ntfy-poweronoff (Shell)
- [ntfy-run](https://github.com/quantum5/ntfy-run) - Tool to run a command, capture its output, and send it to ntfy (Rust)
- [Clipboard IO](https://github.com/jim3692/clipboard-io) - End to end encrypted clipboard
- [ntfy-me-mcp](https://github.com/gitmotion/ntfy-me-mcp) - An ntfy MCP server for sending/fetching ntfy notifications to your self-hosted ntfy server from AI Agents (supports secure token auth & more - use with npx or docker!) (Node/Typescript)
- [InvaderInformant](https://github.com/patricksthannon/InvaderInformant) - Script for Mac OS systems that monitors new or dropped connections to your network using ntfy (Shell)
- [NtfyPwsh](https://github.com/ptmorris1/NtfyPwsh) - PowerShell module to help send messages to ntfy (PowerShell)
- [ntfyrr](https://github.com/leukosaima/ntfyrr) - Currently an Overseerr webhook notification to ntfy helper service.
## Blog + forum posts
- [Device notifications via HTTP with ntfy](https://alistairshepherd.uk/writing/ntfy/) - alistairshepherd.uk - 6/2025
- [Notifications about (almost) anything with ntfy.sh](https://hamatti.org/posts/notifications-about-almost-anything-with-ntfy-sh/) - hamatti.org - 6/2025
- [I set up a self-hosted notification service for everything, and I'll never look back](https://www.xda-developers.com/set-up-self-hosted-notification-service/) ⭐ - xda-developers.com - 5/2025
- [How to Set Up Ntfy: Self-Hosted Push Notifications Made Easy](https://www.youtube.com/watch?v=wDJDiAYZ3H0) - youtube.com (sass drew) - 1/2025
- [The NTFY is a game-changer FREE solution for IT people](https://www.youtube.com/watch?v=NtlztHT-sRw) - youtube.com (Valters Tech Turf) - 1/2025
- [Notify: A Powerful Tool for Real-Time Notifications (ntfy.sh)](https://www.youtube.com/watch?v=XXTTeVfGBz0) - youtube.com (LinuxCloudHacks) - 12/2025
- [Push notifications with ntfy and n8n](https://www.youtube.com/watch?v=DKG1R3xYvwQ) - youtube.com (Oskar) - 10/2024
- [Setup ntfy for selfhosted notifications with Cloudflare Tunnel](https://medium.com/@svenvanginkel/setup-ntfy-for-selfhosted-notifications-with-cloudflare-tunnel-e342f470177d) - medium.com (Sven van Ginkel) - 10/2024
- [Self-Host NTFY - How It Works & Easy Setup Guide](https://www.youtube.com/watch?v=79wHc_jfrJE) ⭐ - youtube.com (Techdox)- 9/2024
- [Boost Your Productivity with ntfy.sh: The Ultimate Notification Tool for Command-Line Users](https://dev.to/archetypal/boost-your-productivity-with-ntfysh-the-ultimate-notification-tool-for-command-line-users-iil) - dev.to - 3/2024
- [ZFS and SMART Warnings via Ntfy](https://rair.dev/zfs-smart-ntfy/) - rair.dev - 2/2024
- [Automating Security Camera Notifications With Home Assistant and Ntfy](https://runtimeterror.dev/automating-camera-notifications-home-assistant-ntfy/) ⭐ - runtimeterror.dev - 2/2024
- [Setting up NTFY with Ngnix-Proxy-Manager, authentication and Ansible notifications](https://random-it-blog.de/rocky-linux/setting-up-ntfy-with-ngnix-proxy-manager-authentication-and-ansible-notifications/) - random-it-blog.de - 12/2023
- [Introducing the Monitoring Ntfy.sh Integration Module: Real-time Notifications for Drupal Monitoring](https://cyberschorsch.dev/drupal/introducing-monitoring-ntfysh-integration-module-real-time-notifications-drupal-monitoring) - cyberschorsch.dev - 11/2023
- [How to install Ntfy.sh on CasaOS using BigBearCasaOS](https://www.youtube.com/watch?v=wSWhtSNwTd8) - youtube.com - 10/2023
- [Podman Update Notifications via Ntfy](https://rair.dev/podman-update-notifications-ntfy/) - rair.dev - 9/2023
- [Installing Self Host NTFY On Linux Using Docker Container](https://www.pinoylinux.org/topicsplus/containers/installing-self-host-ntfy-on-linux-using-docker-container/) - pinoylinux.org - 9/2023
- [Homelab Notifications with ntfy](https://blog.alexsguardian.net/posts/2023/09/12/selfhosting-ntfy/) ⭐ - alexsguardian.net - 9/2023
- [Why NTFY is the Ultimate Push Notification Tool for Your Needs](https://osintph.medium.com/why-ntfy-is-the-ultimate-push-notification-tool-for-your-needs-e767421c84c5) - osintph.medium.com - 9/2023
- [Supercharge Your Alerts: Ntfy — The Ultimate Push Notification Solution](https://medium.com/spring-boot/supercharge-your-alerts-ntfy-the-ultimate-push-notification-solution-a3dda79651fe) - spring-boot.medium.com - 9/2023
- [Deploy Ntfy using Docker](https://www.linkedin.com/pulse/deploy-ntfy-mohamed-sharfy/) - linkedin.com - 9/2023
- [Send Notifications With Ntfy for New WordPress Posts](https://www.activepieces.com/blog/ntfy-notifications-for-wordpress-new-posts) - activepieces.com - 9/2023
- [Get Ntfy Notifications About New Zendesk Ticket](https://www.activepieces.com/blog/ntfy-notifications-about-new-zendesk-tickets) - activepieces.com - 9/2023
- [Set reminder for recurring events using ntfy & Cron](https://www.youtube.com/watch?v=J3O4aQ-EcYk) - youtube.com - 9/2023
- [ntfy - Installation and full configuration setup](https://www.youtube.com/watch?v=QMy14rGmpFI) - youtube.com - 9/2023
- [How to install Ntfy.sh on Portainer / Docker Compose](https://www.youtube.com/watch?v=utD9GNbAwyg) - youtube.com - 9/2023
- [Podman Update Notifications via Ntfy](https://rair.dev/podman-upadte-notifications-ntfy/) - rair.dev - 9/2023
- [How to Send Alerts From Raspberry Pi Pico W to a Phone or Tablet](https://www.tomshardware.com/how-to/send-alerts-raspberry-pi-pico-w-to-mobile-device) - tomshardware.com - 8/2023
- [NetworkChunk - how did I NOT know about this?](https://www.youtube.com/watch?v=poDIT2ruQ9M) ⭐ - youtube.com - 8/2023
- [Open Source Push Notifications! Get notified of any event you can imagine. Triggers abound!](https://www.youtube.com/watch?v=WJgwWXt79pE) ⭐ - youtube.com - 8/2023
- [How to install and self host an Ntfy server on Linux](https://linuxconfig.org/how-to-install-and-self-host-an-ntfy-server-on-linux) - linuxconfig.org - 7/2023
- [Basic website monitoring using cronjobs and ntfy.sh](https://burkhardt.dev/2023/website-monitoring-cron-ntfy/) - burkhardt.dev - 6/2023
- [Pingdom alternative in one line of curl through ntfy.sh](https://piqoni.bearblog.dev/uptime-monitoring-in-one-line-of-curl/) - bearblog.dev - 6/2023
- [Using Ntfy to send and receive push notifications - Samuel Rosa de Oliveria - Delphicon 2023](https://www.youtube.com/watch?v=feu0skpI9QI) - youtube.com - 3/2023
- [ntfy: własny darmowy system powiadomień](https://sprawdzone.it/ntfy-wlasny-darmowy-system-powiadomien/) - sprawdzone.it - 3/2023
- [Deploying ntfy on railway](https://www.youtube.com/watch?v=auJICXtxoNA) - youtube.com - 3/2023
- [Start-Job,Variables, and ntfy.sh](https://klingele.dev/2023/03/01/start-jobvariables-and-ntfy-sh/) - klingele.dev - 3/2023
- [Comment envoyer des notifications push sur votre téléphone facilement et gratuitement?](https://korben.info/notifications-push-telephone.html) - 1/2023
- [Using ntfy to warn me when my computer is discharging](https://ulysseszh.github.io/programming/2022/11/28/ntfy-warn-discharge.html) - ulysseszh.github.io - 11/2022
- [Envie Push Notifications por POST (de graça e sem cadastro)](https://www.tabnews.com.br/filipedeschamps/envie-push-notifications-por-post-de-graca-e-sem-cadastro) - tabnews.com.br - 11/2022
- [Push Notifications for KDE](https://volkerkrause.eu/2022/11/12/kde-unifiedpush-push-notifications.html) - volkerkrause.eu - 11/2022
- [Ntfy.sh – Send push notifications to your phone via PUT/POST](https://news.ycombinator.com/item?id=33517944) ⭐ - news.ycombinator.com - 11/2022
- [Ntfy et Jeedom : un plugin](https://lunarok-domotique.com/2022/11/ntfy-et-jeedom/) - lunarok-domotique.com - 11/2022
- [Crea tu propio servidor de notificaciones con Ntfy](https://blog.parravidales.es/crea-tu-propio-servidor-de-notificaciones-con-ntfy/) - blog.parravidales.es - 11/2022
- [unRAID Notifications with ntfy.sh](https://lder.dev/posts/ntfy-Notifications-With-unRAID/) - lder.dev - 10/2022
- [Zero-cost push notifications to your phone or desktop via PUT/POST ](https://lobste.rs/s/41dq13/zero_cost_push_notifications_your_phone) - lobste.rs - 10/2022
- [ntfy: send notifications from your computer to your phone](https://rs1.es/tutorials/2022/01/19/ntfy-send-notifications-phone.html) - rs1.es - 1/2022
- [Free MacroDroid webhook alternative (FrameXX)](https://www.macrodroidforum.com/index.php?threads/ntfy-sh-free-macrodroid-webhook-alternative.1505/) - macrodroidforum.com - 12/2021
- [ntfy otro sistema de notificaciones pub-sub simple basado en HTTP](https://ugeek.github.io/blog/post/2021-11-05-ntfy-sh-otro-sistema-de-notificaciones-pub-sub-simple-basado-en-http.html) - ugeek.github.io - 11/2021
- [Show HN: A tool to send push notifications to your phone, written in Go](https://news.ycombinator.com/item?id=29715464) ⭐ - news.ycombinator.com - 12/2021
- [ntfy on The Canary in the Cage Podcast](https://odysee.com/@TheCanaryInTheCage:b/The-Canary-in-the-Cage-Episode-42:1?r=4gitYjTacQqPEjf22874USecDQYJ5y5E&t=3062) - odysee.com - 1/2025
- [NtfyPwsh - A PowerShell Module to Send Ntfy Messages](https://ptmorris1.github.io/posts/NtfyPwsh/) - github.io - 5/2025
## Alternative ntfy servers
Here's a list of public ntfy servers. As of right now, there is only one official server. The others are provided by the
ntfy community. Thanks to everyone running a public server. **You guys rock!**
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.