npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sailingnaturali/signalk-notification-router

v1.2.0

Published

Route Signal K notifications to MQTT, Telegram and an agent webhook — two push lanes with independent failure modes, honouring the notification's own `method`.

Readme

@sailingnaturali/signalk-notification-router

Route Signal K notifications to MQTT, Telegram and an agent webhook — three outputs with deliberately independent failure modes.

Watches notifications.* in-process via the subscription manager and edge-triggers: it acts once when a notification changes state, not repeatedly while it persists.

Looking for ntfy?

This plugin does not send to ntfy — it was renamed from @sailingnaturali/signalk-ntfy-relay and the ntfy sender was removed. signalk-ntfy (Apache-2.0) is actively developed and does that job, against ntfy.sh or a self-hosted server.

The routing rule

method decides whether a notification pushes at all; state decides which lane.

| Output | Trigger | Depends on | |---|---|---| | MQTT <prefix>/<path> | every notification at or above minState | the broker | | Telegram siren | alarm/emergency with sound in method | the internet only | | Agent webhook | alert/warn with sound, plus a context follow-up after every siren | the gateway and the model behind it |

Some gateways need more than {"message": "..."} to actually deliver the reply — the message alone can complete an agent turn and then drop it on the floor. Set hookBodyExtra to a JSON object and it's merged into the POST body alongside message (which always wins on a key collision). Against the OpenClaw gateway, for example, the delivery target is a body field, not implied by the URL: {"deliver":true,"channel":"telegram","to":"<chatId>"}. This plugin doesn't know or care what your gateway calls its fields — it just merges what you give it.

A Signal K notification's method array is the publisher saying how it wants to be surfaced. ["visual"] means display this, do not sound it. A publisher that does not ask for sound reaches MQTT and stops — at any severity. That is what keeps a blanket geofence warning off your phone without a path allowlist, which this plugin deliberately does not have. If something routes wrong, fix it at the publisher.

Two lanes, failing independently

The hard lane needs Signal K, this plugin, and the internet. The soft lane needs all of that plus a working gateway and a live model API. The alarm that matters most depends on the fewest things — so the siren goes straight to the Telegram Bot API and is never routed through the webhook for tidiness.

The hard lane never batches: deduplication is a comfort feature, a missed alarm is not. The soft lane batches inside coalesceSeconds so a burst of related transitions becomes one agent turn instead of six.

Delivery-path health

A blank token and an expired one both fail silently, which is how a push path dies unnoticed. After failureThreshold consecutive failures on a lane, the plugin raises notifications.notificationRouter.deliveryFailed.<lane> — a visual-only notification. It publishes to MQTT like any other forwardable notification (so it reaches whatever reads naturali/alerts/#, e.g. a voice agent or dashboard), but is never routed back out through the Telegram or webhook lanes — it can't attempt to page through the very lane that is down. Any success clears it.

Config

All fields optional; a lane with no credentials idles and logs why at startup.

| Field | Default | Notes | |---|---|---| | minState | alert | Minimum severity to forward anywhere | | mqttUrl | mqtt://localhost:1883 | Blank disables the MQTT output | | mqttUser / mqttPassword | — | Blank user means anonymous | | topicPrefix | naturali/alerts | | | telegramBotToken / telegramChatId | — | Hard lane | | hookUrl / hookToken | — | Soft lane; POSTs {"message": "..."} (plus anything set in hookBodyExtra) with a bearer token | | hookBodyExtra | — | Optional JSON object merged into that POST body, message always wins | | coalesceSeconds | 10 | Soft-lane batching window | | failureThreshold | 3 | Per lane |

MQTT envelope

Published retained at QoS 1:

{
  "path": "navigation.anchor",
  "state": "alarm",
  "message": "Anchor dragging",
  "timestamp": "2026-08-06T22:14:03Z",
  "position": { "latitude": 48.76021, "longitude": -123.05213 }
}

position is present only when a numeric fix is available. When a forwarded notification clears, a {"state": "normal", "message": "cleared"} envelope is published to the same topic — cleared notifications never page and never wake the agent.

Testing a lane

scripts/inject-test-notification.js injects a notification at any state and method over the delta WebSocket, which is the only thing that works — a PUT to the notifications REST path returns 404.

SIGNALK_TOKEN=... node scripts/inject-test-notification.js \
  --host localhost --path test.softLane --state warn --method visual,sound \
  --message "TEST - ignore"

Re-run with --state normal to clear.

License

MIT