# pushit
A small, cross-platform command-line tool for sending push notifications.
```sh
pushit send "build finished"
pushit send --title "deploy" --priority 1 "v1.2.3 is live"
Runs on **Linux, macOS, and Windows**, has no runtime dependencies beyond the
binary itself, stores configuration as [RON](https://github.com/ron-rs/ron)
files.
> **Service support:** [Pushover](https://pushover.net) is the only backend as
> of now, but `pushit` is designed to support multiple backends.
## Table of contents
- [Installation](#installation)
- [Quick start](#quick-start)
- [Profiles](#profiles)
- [User vs. system profiles](#user-vs-system-profiles)
- [Filesystem locations](#filesystem-locations)
- [Profile file format](#profile-file-format)
- [Command reference](#command-reference)
- [`pushit send`](#pushit-send)
- [`pushit profile add`](#pushit-profile-add)
- [`pushit profile list`](#pushit-profile-list)
- [`pushit profile show`](#pushit-profile-show)
- [`pushit profile use`](#pushit-profile-use)
- [`pushit profile remove`](#pushit-profile-remove)
- [`pushit profile path`](#pushit-profile-path)
- [Pushover specifics](#pushover-specifics)
- [Security note](#security-note)
- [Exit codes](#exit-codes)
- [Building from source](#building-from-source)
## Installation
```shell
cargo install pushit
```
## Quick start
Get a [Pushover](https://pushover.net) account, create an application, and grab
two strings:
- your **user key** (one per Pushover account, shown on the dashboard), and
- an **application API token** (one per application you register).
Create a profile and set it as the default:
```shell
pushit profile add personal \
--service pushover \
--token aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \
--user-key bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
pushit profile use personal
```
Send something:
```shell
pushit send "hello from pushit"
```
Your phone or browser should receive a push notification within a second or two.
You can override per-call attributes with flags:
```shell
pushit send --title "ci" --priority 1 --sound siren "build failed on main"
```
## Profiles
A **profile** bundles everything needed to send a notification through one
account on one service. You can create as many profiles as you want.
### User vs. system profiles
Profiles live in one of two **tiers**:
- The **user tier** is per-user, writable without elevation.
- The **system tier** is machine-wide, pass `--system` to operate on this tier.
On name collisions the **user tier always wins**, `pushit` falls back to the
system value only if the user hasn't set one.
### Filesystem locations
| Windows | `%APPDATA%\pushit\` | `%PROGRAMDATA%\pushit\` |
| macOS | `~/Library/Application Support/pushit/` | `/Library/Application Support/pushit/` |
| Linux (XDG) | `$XDG_CONFIG_HOME/pushit/` (default `~/.config/pushit/`) | `/etc/xdg/pushit/` |
Within each tier:
```
<tier_root>/
config.ron # tracks default_profile
profiles/
personal.ron
alerts.ron
...
```
You can ask `pushit` directly:
```shell
pushit profile path # user profiles directory
pushit profile path --system # system profiles directory
pushit profile path personal # exact path to one profile
```
### Profile file format
Profiles are stored as [RON](https://github.com/ron-rs/ron) (Rusty Object
Notation.)
Example profile:
```ron
(
name: "personal",
service: Pushover((
token: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
user_key: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
)),
defaults: (
title: Some("home server"),
priority: Some(0),
sound: None,
device: None,
url: None,
url_title: None,
),
)
```
The `service` field is a tagged enum.
The top-level `config.ron` in each tier is even simpler:
```ron
(default_profile: Some("personal"))
```
## Command reference
### `pushit send`
```text
pushit send [OPTIONS] <MESSAGE>
```
Sends a notification through a profile.
| `-p`, `--profile NAME` | Profile to use. Defaults to the resolved default (user > system). |
| `-t`, `--title TITLE` | Notification title. |
| `-P`, `--priority N` | `-2` lowest … `2` emergency (see [Pushover specifics](#pushover-specifics)). |
| `-s`, `--sound SOUND` | One of Pushover's [named sounds](https://pushover.net/api#sounds). |
| `-d`, `--device DEVICE` | Restrict delivery to a single device name. |
| `-u`, `--url URL` | Supplementary URL. |
| `--url-title TITLE` | Display text for `--url`. |
| `<MESSAGE>` | The notification body (required, positional). |
Example:
```shell
pushit send -t "ci" -P 1 -u "https://ci.example.com/builds/1337" \
--url-title "view build" "build 1337 failed"
```
### `pushit profile add`
```text
pushit profile add <NAME> --service pushover --token <TOKEN> --user-key <KEY>
[--title T] [--priority N] [--sound S]
[--device D] [--url U] [--url-title T] [--system]
```
Creates a new profile and writes it to disk.
- `NAME` must match `[A-Za-z0-9_-]+`.
- `--token` and `--user-key` are required when `--service pushover`.
- Any of `--title …` through `--url-title …` are stored as the profile's
defaults.
- `--system` writes to the system tier instead of the user tier (requires
elevation).
### `pushit profile list`
```text
pushit profile list
```
Prints profiles from both tiers, with the user's default marked `(default)`, the
system default marked `(system default)`, and any system profile shadowed by a
same-named user profile marked `(shadowed)`:
```text
user:
alerts (default)
personal
system:
alerts (shadowed)
corporate (system default)
```
### `pushit profile show`
```text
pushit profile show <NAME> [--system]
```
Prints the raw `.ron` contents of a profile. Without `--system`, the user tier
wins on name collisions. With `--system`, the system copy is always shown.
### `pushit profile use`
```text
pushit profile use <NAME> [--system]
```
Sets `NAME` as the default profile. Writes to the user tier by default;
`--system` writes the system-tier default. The named profile must exist in
_some_ tier.
### `pushit profile remove`
```text
pushit profile remove <NAME> [--system]
```
Deletes a profile from the chosen tier (user by default).
### `pushit profile path`
```text
pushit profile path [NAME] [--system]
```
Prints the on-disk location of:
- the profiles directory (when no `NAME` is given), or
- a specific profile's `.ron` file.
`--system` selects the system tier.
Useful for piping into editors, file managers, or shell scripts:
```shell
$EDITOR "$(pushit profile path personal)"
```
## Pushover specifics
`pushit` talks to `https://api.pushover.net/1/messages.json` over HTTPS using a
form-encoded POST. The fields it sends, mapped to
[Pushover's API](https://pushover.net/api):
| _(profile credential)_ | `token` |
| _(profile credential)_ | `user` |
| `<MESSAGE>` | `message` |
| `--title` | `title` |
| `--priority` | `priority` |
| `--sound` | `sound` |
| `--device` | `device` |
| `--url` | `url` |
| `--url-title` | `url_title` |
When Pushover returns a non-2xx response, the JSON `errors[]` array is parsed
and surfaced verbatim:
```text
$ pushit send "test"
error: service error: application token is invalid, see https://pushover.net/api
```
## Security note
Profile files containing API keys are stored in **plaintext** on disk.
## Exit codes
| `0` | Success. |
| `1` | Any error: invalid arguments, missing profile, failed HTTP request, non-2xx response from the service, etc. The error reason is printed to `stderr`. |
## Building from source
```shell
git clone https://github.com/collindutrow/pushit.git
cd pushit
cargo build --release
./target/release/pushit --help
```
---
Issues and PRs welcome.