pingd docs

Topics

A topic is a named stream of messages. Topics are concrete names; patterns are only used for permissions. Use public flags for open read/publish access, and permissions or share tokens for scoped access.

Naming

Topic names are at least 3 characters and use only a–z, A–Z, 0–9, ., -, and _. The dot is the recommended hierarchy separator (NATS-style):

alerts
alerts.disk
alerts.disk.full
deploy.prod.api
home.frontdoor

Topics are concrete. Subscribing to alerts.disk does not subscribe you to alerts.cpu. Wildcard subscriptions don't exist; you subscribe to each name explicitly. Wildcards do exist for permissions (see Permissions).

Access flags

Every topic has two independent public access flags. By default both are false, unless PINGD_DEFAULT_PUBLIC_READ or PINGD_DEFAULT_PUBLIC_PUBLISH is set. Without public access, a new topic is private.

The full access matrix:

publicReadpublicPublishDefault access
false false private; owner, admin, matching permission, or share token only
true false public read, restricted publish
false true restricted read, public publish
true true public read and publish

Admin and owner access are resolved before permissions, so they always have read/publish access. For non-owners, a matching deny permission overrides public flags.

Share tokens

For per-topic credentials, the owner or an admin creates a share token with fixed access: ro, wo, or rw. Clients send the token in X-Topic-Token.

curl http://localhost:7685/topics/alerts/messages \
  -H 'X-Topic-Token: tk_...' \
  -H 'Content-Type: application/json' \
  -d '{ "payload": { "body": "Hello" } }'

Share tokens can expire and can be rotated or revoked without changing the topic's public flags. See Permissions for details.

Endpoints

MethodPathDescription
GET/topicsList topics visible to caller
POST/topicsCreate topic (auth required)
GET/topics/:nameGet a topic
PATCH/topics/:nameUpdate public flags (owner or admin)
DELETE/topics/:nameDelete topic (owner or admin)
GET/topics/:name/statsStats (admin only)
GET/topics/:name/sharesList share tokens (owner or admin)
POST/topics/:name/sharesCreate share token (owner or admin)
PATCH/topics/:name/shares/:idUpdate share token (owner or admin)
POST/topics/:name/shares/:id/rotateRotate share token (owner or admin)
DELETE/topics/:name/shares/:idRevoke share token (owner or admin)

Create

curl -s http://localhost:7685/topics \
  -H 'Authorization: Bearer $TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "alerts.critical",
    "publicRead": true,
    "publicPublish": false
  }'
pingd-cli topics create \
  --name alerts.critical \
  --public-read
import requests

requests.post(
    "http://localhost:7685/topics",
    headers={"Authorization": "Bearer TOKEN"},
    json={
        "name": "alerts.critical",
        "publicRead": True,
        "publicPublish": False,
    },
)

The caller becomes the owner.

Update

curl -s -X PATCH http://localhost:7685/topics/alerts.critical \
  -H 'Authorization: Bearer $TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "publicRead": false,
    "publicPublish": false
  }'
pingd-cli topics update \
  --name alerts.critical \
  --no-public-read \
  --no-public-publish
import requests

requests.patch(
    "http://localhost:7685/topics/alerts.critical",
    headers={"Authorization": "Bearer TOKEN"},
    json={
        "publicRead": False,
        "publicPublish": False,
    },
)

Owner-or-admin only. Omitted fields keep their existing values.

Delete

curl -s -X DELETE http://localhost:7685/topics/alerts.critical \
  -H 'Authorization: Bearer $TOKEN'
pingd-cli topics delete --name alerts.critical
import requests

requests.delete(
    "http://localhost:7685/topics/alerts.critical",
    headers={"Authorization": "Bearer TOKEN"},
)

Cascades to messages, deliveries, subscriptions, share tokens, and webhooks attached to the topic. Pattern permissions are not topic-owned rows and are not deleted.

Stats

Admin-only. Returns counts of messages, active subscriptions, deliveries, and recent activity for capacity planning.

curl -s http://localhost:7685/topics/alerts.critical/stats \
  -H 'Authorization: Bearer $TOKEN'
pingd-cli topics stats --name alerts.critical
import requests

response = requests.get(
    "http://localhost:7685/topics/alerts.critical/stats",
    headers={"Authorization": "Bearer TOKEN"},
)
print(response.json())

Naming conventions

Use dots to namespace by domain. Examples that work well in practice:

Then a permission like alerts.> covers everything under alerts, and a CI service token can be limited to deploy.>.