β This software is currently in alpha phase. |
---|
PushBits is a relay server for push notifications. It enables you to send notifications via a simple web API, and delivers them to you through Matrix. This is similar to what Pushover and Gotify offer, but it does not require an additional app.
The vision is to have compatibility with Gotify on the sending side, while on the receiving side an established service is used. This has the advantages that
- sending plugins written for Gotify (like those for Watchtower and Jellyfin) as well as
- receiving clients written for Matrix can be reused.
I would totally do this with Signal if there was a proper API. Unfortunately, neither Signal nor WhatsApp come with an API through which PushBits could interact.
In Telegram there is an API to run bots, but these are limited in that they cannot create chats by themselves. If you insist on going with Telegram, have a look at webhook2telegram.
I myself started using Matrix only for this project. The idea of a federated, synchronized but yet end-to-end encrypted protocol is awesome, but its clients simply aren't really there yet. Still, if you haven't tried it yet, I suggest you to check it out.
- Multiple users and multiple channels (applications) per user
- Compatibility with Gotify's API for sending messages
- API and CLI for managing users and applications
- Optional check for weak passwords using HIBP
- Argon2 as KDF for password storage
- Two-factor authentication, issue
- Bi-directional key verification, issue
PushBits is meant to be self-hosted. That means you have to install it on your own server.
Currently, the only supported way of installing PushBits is via Docker or Podman. The image is hosted here on Docker Hub.
To see what can be configured, have a look at the config.sample.yml
file inside the root of the repository.
Settings can optionally be provided via environment variables.
The name of the environment variable is composed of a starting PUSHBITS_
, followed by the keys of the setting, all
joined with _
.
As an example, the HTTP port can be provided as an environment variable called PUSHBITS_HTTP_PORT
.
To get started, here is a Docker Compose file you can use.
version: '2'
services:
server:
image: eikendev/pushbits:latest
ports:
- 8080:8080
environment:
PUSHBITS_DATABASE_DIALECT: 'sqlite3'
PUSHBITS_ADMIN_MATRIXID: '@your/matrix/username:matrix.org' # The Matrix account on which the admin will receive their notifications.
PUSHBITS_ADMIN_PASSWORD: 'your/pushbits/password' # The login password of the admin account. Default username is 'admin'.
PUSHBITS_MATRIX_USERNAME: 'your/matrix/username' # The Matrix account from which notifications are sent to all users.
PUSHBITS_MATRIX_PASSWORD: 'your/matrix/password' # The password of the above account.
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
- ./data:/data
In this example, the configuration file would be located at ./data/config.yml
on the host.
The SQLite database would be written to ./data/pushbits.db
.
Don't forget to adjust the permissions of the ./data
directory, otherwise PushBits will fail to operate.
Now, how can you interact with the server? I wrote a little CLI tool called pbcli to make basic API requests to the server. It helps you to create new users and applications. You will find further instructions in the linked repository.
At the time of writing, there is no fancy GUI built-in, and I'm not sure if this is necessary at all. I don't do much front end development myself, so if you want to contribute in this regard I'm happy if you reach out!
After you have created a user and an application, you can use the API to send a push notification to your Matrix account.
curl \
--header "Content-Type: application/json" \
--request POST \
--data '{"message":"my message","title":"my title"}' \
"https://pushbits.example.com/message?token=$PB_TOKEN"
Note that the token is associated with your application and has to be kept secret. You can retrieve the token using pbcli by running following command.
pbcli application show $PB_APPLICATION --url https://pushbits.example.com --username $PB_USERNAME
Pushbits offers you two methods of authenticating against the server:
- Basic authentication (
basic
) - Oauth 2.0 (
oauth
)
You will find the corresponding setting in the security section.
...
security:
...
# The authentication method to use
authentication: basic
...
For basic authentication you have to provide your username and password in each request to the server. For example in curl you can do this with the --user
flag:
curl -u myusername:totallysecretpassword
Oauth 2.0 is a token based authentication method. Instead of passing your password with each request you request a token from an authorization server. With this token you are then able to authenticate yourself against the PushBits server.
Make sure to setup the "oauth" section in the config file correctly.
For authentication use the ``/oauth2/auth` endpoint. E.g.:
curl \
--header "Content-Type: application/json" \
--request GET \
"https://pushbits.example.com/oauth2/auth?client_id=000000&username=admin&password=1233456&response_type=code&redirect_uri=https://myapp.example.com"
This will return a HTTP redirect with the status code 302
and an authentication code set as parameter:
HTTP/2 302
date: Sun, 23 May 2021 10:33:27 GMT
location: https://myapp.example.com?code=4T1TJXMBPTOS4NNGILBDYW
content-length: 0
Your app then needs to use this code to trade it for a access token.
Hint for command line users: you can extract the authentication code from the redirect without the need of a running webserver.
You can get an access token from the /oauth/token
endpoint. There are several methods, so called "grant types" for receiving a token. PushBits currently supports the following one's:
- Refresh
- Authentication code
Oauth 2.0 authentication is based on "clients", thus you need to provide identifiers for a client with your request. These are the client_id
and the client_secret
.
For your first token you will need a authentication code, see the section above. Then use it like this:
curl \
--header "Content-Type: application/json" \
--request GET \
"https://pushbits.example.com/oauth2/token?grant_type=authorization_code&client_id=000000&client_secret=49gjg4js9&response_type=token&redirect_uri=https://myapp.example.com&code=OP1Q2UJEVL-RPR9GZAUURA"
This will then return an access token and refresh token for you.
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIwMDAwMDAiLCJleHAiOjE2MjE4NTU3ODcsInN1YiI6IjEifQ.jMux7CBw6fY15Ohc8exEbcnUiMBVVgCowvq3rMrw7MQ",
"expires_in": 86400,
"refresh_token": "OP1Q2UJEVL-RPR9GZAUURA",
"token_type": "Bearer"
}
The access token is short lived, the refresh token is long lived, but can not be used for authentication. If your access token runs out, you can use the refresh token to generate a new access token:
curl \
--header "Content-Type: application/json" \
--request GET \
"https://pushbits.example.com/oauth2/token?grant_type=refresh_token&client_id=000000&client_secret=49gjg4js9&response_type=token&refresh_token=OP1Q2UJEVL-RPR9GZAUURA"
With a valid access token you can get information about it from /oauth/tokeninfo
. This is meant for testing if a token is issued correctly.
curl \
--header "Content-Type: application/json" \
--request GET \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIwMDAwMDAiLCJleHAiOjE2MjE4NTU3ODcsInN1YiI6IjEifQ.jMux7CBw6fY15Ohc8exEbcnUiMBVVgCowvq3rMrw7MQ" \
"https://pushbits.example.com/oauth2/tokeninfo"
Admin users are eligible to revoke tokens. This should not be necessary in normal operation, as tokens are only short lived. But there might be situations where attackers might have gotten knowledge about a token.
curl \
--header "Content-Type: application/json" \
--request POST \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIwMDAwMDAiLCJleHAiOjE2MjE4NTU3ODcsInN1YiI6IjEifQ.jMux7CBw6fY15Ohc8exEbcnUiMBVVgCowvq3rMrw7MQ" \
--data '{"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIwMDAwMDAiLCJleHAiOjE2MjE4NDg1MDYsInN1YiI6IjEifQ.cO0_8fqsJDG4KswjC0CSzc_EznntH-FDQejdolPAISo"}'
"https://pushbits.example.com/oauth2/revoke"
Messages are supporting three different syntaxes:
- text/plain
- text/html
- text/markdown
To set a specific syntax you need to set the extras
(inspired by Gotifys message extras):
curl \
--header "Content-Type: application/json" \
--request POST \
--data '{"message":"my message with\n\n**Markdown** _support_.","title":"my title","extras":{"client::display":{"contentType": "text/markdown"}}}' \
"https://pushbits.example.com/message?token=$PB_TOKEN"
HTML-Content might not be fully rendered in your Matrix-Client - see the corresponding Matrix specs. This also holds for Markdown, as it is transfered to the corresponding HTML-syntax.
The idea for this software and most parts of the initial source are heavily inspired by Gotify. Many thanks to jmattheis for his well-structured code.
The source code is located on GitHub. You can retrieve it by checking out the repository as follows.
git clone https://github.com/pushbits/server.git