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

signalk-sereno-safety-report

v0.3.7

Published

Maritime safety report for the boat's current position: NOAA tsunami bulletins graded against where you are, earthquakes, cyclones, marine forecast, health and travel advisories. Free sources, no API key. Early release, not a substitute for official warni

Readme

Sereno Safety Report

Early release. Version 0.3.7. The data sources are live and public and the verdict is computed by a fixed rule set, but coverage and thresholds haven't been exercised in every ocean or every season.

The two tsunami warning centers it reads cover the Pacific basin and the Caribbean and nowhere else. Outside those, the report says so rather than reporting an all clear. See "What this tool considers a tsunami emergency".

Treat it as one input among several. It's not a substitute for official forecasts, warnings, or your own judgment, and it's not a watchkeeper.

Issues and corrections are welcome, particularly from outside the Pacific.

A SignalK server plugin that builds a maritime safety report for the position SignalK already has, and serves it to two places: Sereno's Report tab and a webapp page inside SignalK.

Every source is free and needs no subscription. Position never leaves the boat except as the coordinates each public API needs to answer.

What it reports

| Section | Source | |---|---| | Tsunami bulletins | NOAA PTWC and NTWC Atom feeds | | Earthquakes | USGS FDSN event service | | Cyclones, volcanoes, floods | GDACS (JRC / UN) | | Marine forecast | Open-Meteo marine and forecast APIs | | Lightning outlook | Open-Meteo CAPE, lifted index and WMO codes | | Nearest buoy | NOAA NDBC realtime2 | | Health | ReliefWeb country RSS and CDC travel notices | | Travel advisories | US State Department and UK FCDO | | Local security | Google News, two queries | | Regional wire | RNZ Pacific | | World headlines | BBC World | | Emergency contacts | Local table, works with no connection |

Coverage isn't even everywhere, and it's better to know that before you rely on it. Earthquakes, cyclones, marine forecast, buoys, health and travel advisories are global. Tsunami bulletins cover the Pacific basin and the Caribbean only, and outside those the report says so rather than reporting an all clear. The regional wire is RNZ Pacific and returns nothing outside the Pacific. The offline emergency contact table currently holds the South Pacific and New Zealand and Australia, and falls back to "VHF Channel 16 for distress" elsewhere. Additions for other regions are welcome.

How the verdict is decided

By a deterministic rule set in plugin/rules.js and plugin/seismic.js, and never by a language model. Every number behind it lives in THRESHOLDS in plugin/config.js, and the seismic ones can be changed in the plugin's config screen without touching the code.

If a Gemini key is configured, the model writes two or three plain paragraphs around a verdict it can't change. If that call fails, is rate limited, or the boat is offline, the report still renders with the verdict and every table intact.

The plugin is never a safety input. Nothing it produces can arm, silence or influence an anchor alarm, and there's no API by which it could.

There are three levels: No Action Required, Advisory, and Action Required. The report shows the highest one anything reached.

What this tool considers a tsunami emergency

The answer is narrower than most people expect.

The USGS tsunami flag isn't a warning

USGS marks large earthquakes in oceanic regions with a tsunami flag so its event page can link to NOAA. USGS says plainly that the flag doesn't indicate whether a tsunami did or will exist. It's a geography flag.

This report shows it in the earthquake table as "In a NOAA tsunami notification region" and it never raises an alert on its own, at any magnitude and any distance.

Only PTWC and NTWC can raise a tsunami alert

A bulletin has to pass two gates.

Gate 1, what kind of message is it:

| Message | Level | |---|---| | Information statement, no threat, cancellation | Listed in the table, no alert | | Watch | Advisory | | Advisory | Advisory | | Threat message | Advisory | | Warning | Action Required, if it passes gate 2 |

Gate 2, is it about you. The plugin works out from the vessel position which center speaks for your water, on every run:

| Where you are | Authoritative center | |---|---| | Alaska, the US West Coast, Canada | NTWC | | The rest of the Pacific basin, and the Caribbean | PTWC | | Anywhere else | Neither |

A warning reaches Action Required when it declares a basin-wide threat, when it names the country you're in, or when it comes from NTWC and you're inside NTWC's area. A live warning that can't be tied to your position is carried as an Advisory that says so, rather than as an evacuation order for a coast eight thousand miles away. A message from a center that doesn't cover you and doesn't mention you is listed in the table and marked "No, other area".

If you're outside the Pacific and the Caribbean, neither center covers you. The report says that where the bulletin table would be. It doesn't print "no bulletins" as though someone had looked. Use your own regional tsunami service, and set Authoritative tsunami warning center in the config if you want to change what this plugin treats as authoritative.

Earthquakes with no bulletin yet

| Case | Condition | Level | |---|---|---| | Near field | Within 600 nm, magnitude 6.5 or more, 70 km deep or less | Action Required | | Far field | Magnitude 7.5 or more, 70 km deep or less, in a NOAA notification region, no bulletin yet | Advisory, saying a bulletin is expected | | Everything else | Any magnitude, any distance | Listed in the earthquake table only |

Near field escalates before a warning center has spoken, because a local wave arrives before any bulletin does. For a distant source the bulletin arrives hours before the water, so waiting for it costs nothing.

If a covering center has issued any bulletin since the earthquake, including an information statement saying there's no threat, their answer replaces this one and the earthquake rule stands down. The two paths are never added together.

Sequences

Earthquakes within 100 km and 48 hours of each other are reported as one sequence carrying its largest event, not as one line each. An aftershock sequence is one thing that happened.

Changing any of it

Every threshold above is in the plugin's config screen with its default. An absent setting keeps its default, so upgrading never changes behavior until you change a number.

Install

From the SignalK App Store, or for development:

git clone <repo> signalk-sereno-safety-report
cd signalk-sereno-safety-report
npm install
npm link
cd ~/.signalk
npm link signalk-sereno-safety-report

Restart the SignalK server, enable the plugin in the admin UI, and optionally paste a Gemini API key into its config screen.

Pin the Gemini model. Don't use a -latest alias. Measured August 2026: an alias moved onto a model with thinking enabled by default, and the summary came back truncated with no error raised. Choose the model from a live ListModels call against your own key rather than guessing, since not every model is available on every key.

Configuration

Everything here is optional. With an empty config the plugin runs, and the only thing you lose is the written summary.

Report

| Setting | Default | What it does | |---|---|---| | Report title | Safety Report | Heading on the page. | | Units | nautical-metric | Meters, nautical miles, Celsius. The other option, nautical-imperial, gives feet, statute miles and Fahrenheit. Wind stays in knots, pressure in hPa and earthquake depth in km in both. | | Minimum seconds between runs | 60 | A request inside this window returns the existing report and starts nothing, so a stuck client can't hammer the upstream sources. | | Run timeout in seconds | 180 | Wall clock ceiling for one run. A dead source ends the run rather than hanging it. | | Health report window, days | 30 | How far back to read ReliefWeb country reports for the Health section. It was a year, which filled the section with superseded weekly updates that read as current. Raise it for the longer background picture. |

Written summary

| Setting | Default | What it does | |---|---|---| | Gemini API key | empty | Leave blank and the report renders with every table and the verdict, just without the prose. The free tier is ample for on-demand use. | | Gemini model | gemini-2.5-flash | Pin a specific model. See the warning under Install. |

Tsunami and earthquake escalation

These are the numbers behind "What this tool considers a tsunami emergency". They're in the config screen rather than buried in the source, so you can see what the tool treats as an emergency and change it.

| Setting | Default | What it does | |---|---|---| | Authoritative tsunami warning center | Automatic | Which NOAA center speaks for your water. Automatic works it out from the vessel position on every run, so it follows the boat. The other options force PTWC, force NTWC, or set every bulletin to reference only. | | Near field range, nautical miles | 600 | A shallow, large earthquake inside this range is Action Required on its own, without waiting for a bulletin. | | Near field magnitude | 6.5 | Smallest magnitude that can do that. | | Far field magnitude | 7.5 | A shallow earthquake at least this large in a NOAA notification region, with no bulletin yet, raises an Advisory saying one is expected. It never raises Action Required on its own. | | Shallow depth limit, km | 70 | Deeper than this and an earthquake isn't treated as a tsunami source at any magnitude or distance. | | Sequence radius, km | 100 | Earthquakes within this distance of each other are one sequence, not one line each. | | Sequence window, hours | 48 | How far apart in time two earthquakes can be and still count as the same sequence. | | Bulletin window, hours | 24 | How far back to read PTWC and NTWC traffic. Older messages are ignored entirely. |

A setting you haven't touched keeps its default, so upgrading changes no numbers and an older config file needs no edit.

Endpoints

Served under /plugins/signalk-sereno-safety-report/:

| Path | Method | Purpose | |---|---|---| | /health | GET | Liveness. No network work. | | /report | POST | Start a run. No body. | | /report | GET | Status envelope, with HTML when ready. | | /report.html | GET | The report on its own, for debugging. |

status is empty, working, ready or error. A POST inside the minimum interval returns the current status with throttled: true and retry_after_s, and starts nothing.

When a report exists, the envelope also carries:

| Field | Meaning | |---|---| | level | The verdict, one of No Action Required, Advisory, Action Required. | | generated | When the run finished, ISO 8601. | | position | The position the report describes, to five decimal places. | | position_note | Present only when the fix was old. Says how old, in words. | | html | The report, on GET /report only. |

Every response also carries version, and the report footer and the webapp status line show it. If a report isn't behaving the way this README describes, check that number first. Anything below 0.3.0 is the old rule set, whatever the App Store says is installed.

The verdict on the envelope is the same one the page shows. There's one evaluation per run and every surface reads it.

CORS headers are set on every response including errors, because the page calling these is served by Sereno on a different device.

Access and authentication

Requires SignalK 2.31.1 or later. Older servers can't scope a plugin route below admin, so on those every endpoint here needs an admin account. The plugin detects that at startup and reports it in its config screen rather than failing silently, but there's no way to work around it from the plugin side.

SignalK requires admin authentication on plugin routes by default. This plugin registers its routes at lower levels instead, using the server's own permission model:

| Path | Method | Level | |---|---|---| | /health | GET | readonly | | /report | GET | readonly | | /report.html | GET | readonly | | /report | POST | readwrite |

Reading is readonly. Starting a run is readwrite, because it spends upstream API quota and replaces the stored report.

So a device calling these from elsewhere on the boat, such as Sereno, needs a scoped SignalK account rather than an admin one. Create a readwrite user in SignalK's security settings and give that device its token. It can be revoked on its own without touching anything else, and it grants nothing beyond what the level allows.

On a server with security disabled, or one too old to support per-route access levels, the routes register directly on the router and everything works with no credentials. Both paths are covered by the test suite.

OPTIONS is never behind auth, since a browser preflight carries no credentials by definition.

One thing worth knowing: readonly means whatever your server means by it. A SignalK configured to allow anonymous reads will serve /health and GET /report to anyone on the network without a token. That's the server's setting, not the plugin's, and this plugin doesn't override it.

Position

SignalK is the only position source. The POST carries no body and the plugin never accepts coordinates from a caller, so the report can never describe somewhere the boat isn't because a caller passed a stale value.

The position is read two ways, because signalk-server offers two and they don't read the same object. getSelfPath reads a cached reference to the self vessel, getPath reads the full model. When the cached one is stale the first comes back empty while the REST API still serves a position, so the plugin asks both before deciding there's nothing there.

If SignalK has no position, the run doesn't start and the error says so. If SignalK has a position under a different vessel context, the error says that instead, and names the context. Those are different faults: one is a GPS that isn't sending, the other is a GPS whose data is filed under a vessel that isn't this one. The plugin reads vessels.self and nothing else.

If the fix is old, the report still generates. Nothing is refused over fix quality, because a stale position is still the best answer available and the crew can judge it. It's labeled instead: the page carries a line above the tables saying how old the fix is, and the status envelope carries the same sentence as position_note, so a caller reading only the JSON is told just as plainly as a reader of the page.

Testing

npm test

Runs offline. No SignalK server, no network. It covers the tsunami classifier, the escalation rules, warning center coverage, sequence clustering, the lightning gate, the rest of the rule set, place resolution, feed parsing, the renderer and the endpoint plumbing.

The escalation rules carry named regression fixtures, including the September 2026 report that these rules were written to correct: three earthquakes in one Alaskan aftershock sequence, all carrying the USGS tsunami flag, read from Fiji. Each fixture is driven from a payload and a position through to rendered HTML, so a rule that's right in one place and wrong in another fails the suite.

Dependency notes

Two choices here were made by measurement, not preference, and both are easy to get wrong.

Reverse geocoding uses all-the-cities. Don't substitute offline-geocode-city. Measured August 2026: it returns Great Yarmouth, United Kingdom for an anchorage in Ba, Fiji, and Sortland, Norway for Opua, New Zealand. A wrong country code silently poisons the ReliefWeb feed, the travel advisories and the emergency contacts, and the report looks entirely normal while doing it.

Timezone uses @photostructure/tz-lookup. geo-tz's default dataset consolidates zones that have had identical offsets since 1970, so it returns Pacific/Honolulu for a boat anchored off Papeete. The offset is right and the name is wrong, and the name is what the report prints in its header. geo-tz/all fixes that at roughly 70 MB of data.

Neither dependency downloads anything at install time, which matters because the SignalK App Store doesn't run install-time lifecycle scripts.


Sereno, standing watch. https://www.svsabado.com