pingd docs

Examples

Real-world recipes. Replace the host, tokens, and topic names with your own.

Disk-space alert from a cron

Set up a topic share token with wo access and publish from a host's crontab:

#!/bin/sh
USAGE=$(df / | awk 'NR==2 {gsub("%",""); print $5}')
if [ "$USAGE" -gt 90 ]; then
  curl -s https://pingd.example.com/topics/alerts.disk/messages \
    -H 'Content-Type: application/json' \
    -H "X-Topic-Token: $PINGD_TOPIC_TOKEN" \
    -d "{
      \"priority\": 3,
      \"tags\": [\"disk\", \"$(hostname)\"],
      \"payload\": {
        \"title\": \"Disk ${USAGE}% full\",
        \"body\": \"$(hostname): / is ${USAGE}% full\"
      }
    }"
fi
*/5 * * * * /usr/local/bin/disk-alert.sh

CI deploy notifications

Create a CI user with write-only permission on deploy.>, then mint a bearer token for it:

pingd-cli users create \
  --username ci-bot --password '<strong-password>' --role user

pingd-cli permissions create \
  --username ci-bot --access wo --pattern 'deploy.>'

pingd-cli tokens create --username ci-bot --label github-actions

Then in your GitHub Actions workflow:

- name: Notify deploy
  run: |
    curl -s "$PINGD_URL/topics/deploy.prod.api/messages" \
      -H "Authorization: Bearer ${{ secrets.PINGD_TOKEN }}" \
      -H 'Content-Type: application/json' \
      -d '{
        "tags": ["github", "${{ github.repository }}"],
        "payload": {
          "title": "Deployed ${{ github.sha }}",
          "body": "${{ github.actor }} deployed to prod"
        }
      }'

Watch a topic from a terminal

pingd-cli messages watch --topic alerts.disk

Or with raw curl:

curl -N https://pingd.example.com/topics/alerts.disk/stream \
  -H "Authorization: Bearer $PINGD_TOKEN"

Grafana alerting

Webhook template for alerts.grafana:

{
  "template": {
    "title": "{{title}}",
    "body": "{{message}}",
    "tags": "grafana,{{state}}",
    "priority": 3
  }
}

Point Grafana's webhook contact point at the /hooks/... URL.

Home Assistant automation

From a Home Assistant rest_command. Store the full bearer header value in secrets.yaml.

rest_command:
  pingd_doorbell:
    url: "https://pingd.example.com/topics/home.frontdoor/messages"
    method: POST
    headers:
      Authorization: !secret pingd_authorization_header
      Content-Type: application/json
    payload: |
      {
        "priority": 3,
        "tags": ["frontdoor"],
        "payload": {
          "title": "Doorbell",
          "body": "Front door rung at {{ now().strftime('%H:%M') }}"
        }
      }

Python publisher

import os, requests

requests.post(
    f"{os.environ['PINGD_URL']}/topics/alerts.app/messages",
    headers={
        "Authorization": f"Bearer {os.environ['PINGD_TOKEN']}",
        "Content-Type": "application/json",
    },
    json={
        "priority": 2,
        "tags": ["python"],
        "payload": {
            "title": "Job finished",
            "body": "Nightly batch completed successfully",
        },
    },
    timeout=5,
)

Node.js SSE listener

import EventSource from "eventsource";

const es = new EventSource(
    "https://pingd.example.com/topics/alerts.app/stream",
    { headers: { Authorization: `Bearer ${process.env.PINGD_TOKEN}` } },
);

es.onmessage = (event) => {
    const message = JSON.parse(event.data);
    console.log(message.payload.title, "-", message.payload.body);
};

Topic per host (NATS-style)

Use a topic per host for fanout to many machines:

alerts.host.web-01
alerts.host.web-02
alerts.host.db-01

A permission grant like alerts.host.> can cover read access for the whole fleet. Subscriptions are still concrete topic names.

Per-environment topics

deploy.prod.api
deploy.prod.web
deploy.staging.api
deploy.staging.web

Give CI wo on deploy.>, and on-call users ro on deploy.prod.>. Subscribe devices to the concrete prod topics that should notify them.