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

@serve.zone/nupst

v7.0.0

Published

Network UPS Shutdown Tool - Monitor UPS devices via SNMP or NUT/UPSD and orchestrate graceful, cancellable shutdowns during power emergencies

Readme

@serve.zone/nupst

NUPST is a Network UPS Shutdown Tool for servers that need predictable behavior when power gets ugly. It monitors UPS devices through SNMP or NUT/UPSD, evaluates per-device and grouped power conditions, and runs ordered actions such as Proxmox guest shutdown, webhooks, scripts, and host shutdown.

Actions are driven by declarative conditions with sustained-duration support: a 20-second power blip never shuts anything down, pending actions are visible in nupst service status, and every destructive step cancels automatically when power returns - including an already-scheduled host shutdown (shutdown -c).

Issue Reporting and Security

For reporting bugs, issues, or security vulnerabilities, please visit community.foss.global/. This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a code.foss.global/ account to submit Pull Requests directly.

Why It Exists

Power-failure automation must be boring, explicit, and local-first. NUPST ships as a single executable (a Node.js single executable application) that can sit on a Proxmox host or another Linux server, poll one or more UPS devices, and execute a controlled shutdown sequence before batteries are exhausted.

It is useful when you need:

  • SNMP monitoring for network UPS units.
  • NUT/UPSD monitoring for USB UPS units exposed by Network UPS Tools.
  • Multiple UPS devices on one daemon.
  • Redundant or non-redundant UPS groups.
  • Blip-proof triggers: conditions can require a sustained on-battery duration.
  • Cancellable countdowns before destructive actions, with automatic cancel when power returns.
  • Proxmox VM/LXC shutdown before host shutdown.
  • Webhook or script notifications, including pending/executed/cancelled lifecycle events.
  • Live action state in nupst service status and a small authenticated HTTP JSON status endpoint.
  • Pause/resume for maintenance windows (pause also cancels a scheduled shutdown).
  • Configuration migration from older NUPST formats.

Installation

Install the released binary:

curl -sSL https://code.foss.global/serve.zone/nupst/releases/download/latest/install.sh | sudo bash

Already running NUPST v6.0.0 or earlier? Those versions fetch the install script from raw/branch/main, which is no longer readable without a Gitea account, so their nupst update fails. Run the one-liner above once to move to the release-asset installer; every later sudo nupst update works normally.

Install a specific version:

curl -sSL https://code.foss.global/serve.zone/nupst/releases/download/latest/install.sh | sudo bash -s -- --version v6.0.0

Every file the installer needs is a Gitea release asset, so the commands above work without a Gitea account and without repository source access. If you prefer to resolve the release tag yourself instead of relying on the latest asset alias, ask the public releases API first (no jq needed):

NUPST_TAG=$(curl -sSL https://code.foss.global/api/v1/repos/serve.zone/nupst/releases/latest \
  | tr ',{}' '\n\n\n' | grep -m1 '"tag_name"' | sed -e 's/.*"tag_name"[[:space:]]*:[[:space:]]*"//' -e 's/".*//')
curl -sSL "https://code.foss.global/serve.zone/nupst/releases/download/${NUPST_TAG}/install.sh" | sudo bash -s -- --version "${NUPST_TAG}"

Check what the installer would do without changing anything:

curl -sSL https://code.foss.global/serve.zone/nupst/releases/download/latest/install.sh | bash -s -- --dry-run

Install through the npm package wrapper with pnpm:

pnpm add --global @serve.zone/nupst

The npm package installs no uninstaller, so nupst uninstall applies to installations made by install.sh only. Remove an npm or pnpm installation with sudo nupst service disable and pnpm remove --global @serve.zone/nupst; the configuration stays in /etc/nupst.

The installer places the binary under /opt/nupst/nupst, creates a symlink in /usr/local/bin or /usr/bin, and preserves existing /etc/nupst/config.json when upgrading. It verifies the binary and the uninstaller against the SHA256SUMS.txt release asset before installing them, and places uninstall.sh next to the binary so sudo nupst uninstall keeps working offline.

sudo nupst update uses the same release assets: it resolves the latest tag through the public releases API and runs that release's install.sh pinned to the resolved version.

Quick Start

Create the first UPS config interactively:

sudo nupst ups add

Test connectivity:

nupst ups test --debug

Install and start the systemd service:

sudo nupst service enable
sudo nupst service start

Inspect status and logs:

nupst service status
nupst service logs

CLI Reference

nupst <command> [subcommand] [options]

Global options:

| Option | Purpose | | ----------------- | ---------------------------------- | | --version, -v | Show version. | | --help, -h | Show help. | | --debug, -d | Enable detailed SNMP/UPSD logging. |

Service commands:

| Command | Purpose | | ---------------------- | --------------------------------------------------------- | | service enable | Install and enable the systemd service. Requires root. | | service disable | Stop, disable, and remove the service. Requires root. | | service start | Start nupst.service. | | service stop | Stop nupst.service. | | service restart | Restart the service. | | service status | Show service and UPS state. | | service logs | Follow live service logs. | | service start-daemon | Run the daemon process directly. Used by systemd/testing. |

Configuration commands:

| Command | Purpose | | ----------------------------------- | --------------------------------------------------------------------------------------------------- | | ups add | Add a UPS device interactively. | | ups edit [id] | Edit a UPS device. | | ups remove <id> | Remove a UPS device. Alias: rm. | | ups list | List UPS devices. Alias: ls. | | ups test | Test configured UPS connections. | | group add | Create a UPS group. | | group edit <id> | Edit a group. | | group remove <id> | Remove a group. Alias: rm. | | group list | List groups. Alias: ls. | | action add <target-id> | Add an action to a UPS or group. | | action edit <target-id> <index> | Edit an action by array index. | | action remove <target-id> <index> | Remove an action. Alias: rm. | | action list [target-id] | List actions globally or for one target. Alias: ls. | | feature httpServer | Configure the optional JSON status endpoint. Aliases: http-server, http. | | config show | Display the active configuration. | | config explain | Explain in plain language what will happen during a power event. | | pause [--duration <time>] | Suppress actions while polling continues. Cancels pending countdowns and a scheduled host shutdown. | | resume | Resume action execution. Conditions re-evaluate from scratch. | | upgrade | Upgrade NUPST from the latest release. Requires root. | | uninstall | Remove an installation made by install.sh (runs the uninstall.sh beside the binary). Requires root. |

Pause durations support values such as 30m, 2h, and 1d. The maximum pause duration is 24 hours.

Configuration Model

NUPST stores configuration at:

/etc/nupst/config.json

The daemon loads and migrates this file at startup. It also watches for configuration changes and reloads normal config edits without a service restart; pending countdowns of unchanged actions survive a reload.

Current normalized config version is 5.0. Older configs (including the 4.x triggerMode/thresholds action model) are migrated automatically and preserve their previous behavior exactly.

Minimal shape:

{
  "version": "5.0",
  "checkInterval": 30000,
  "defaultShutdownDelay": 5,
  "upsDevices": [
    {
      "id": "ups-main",
      "name": "Main UPS",
      "protocol": "snmp",
      "snmp": {
        "host": "192.168.1.100",
        "port": 161,
        "version": 2,
        "community": "public",
        "timeout": 5000,
        "upsModel": "apc",
        "runtimeUnit": "ticks"
      },
      "groups": ["rack-a"],
      "actions": [
        {
          "id": "stop-guests",
          "type": "proxmox",
          "when": {
            "onBattery": { "afterMinutes": 3 },
            "batteryBelow": 30,
            "runtimeBelow": 15
          },
          "proxmoxMode": "auto",
          "proxmoxHaPolicy": "haStop"
        },
        {
          "id": "halt-host",
          "type": "shutdown",
          "when": { "batteryBelow": 20, "runtimeBelow": 10 },
          "shutdownDelay": 5
        }
      ]
    }
  ],
  "groups": [
    {
      "id": "rack-a",
      "name": "Rack A",
      "mode": "nonRedundant",
      "actions": []
    }
  ]
}

Run nupst config explain to see this rendered as a plain-language timeline.

Protocols

SNMP supports these UPS model profiles:

| Config value | Typical use | | ------------ | ------------------------ | | cyberpower | CyberPower devices. | | apc | APC/PowerNet devices. | | eaton | Eaton/Powerware devices. | | tripplite | Tripp Lite devices. | | liebert | Liebert/Vertiv devices. | | custom | Custom OID mapping. |

SNMP versions 1, 2, and 3 are supported. SNMPv3 supports noAuthNoPriv, authNoPriv, and authPriv with MD5/SHA authentication and DES/AES privacy.

UPSD/NIS supports UPS devices exposed by NUT:

{
  "protocol": "upsd",
  "upsd": {
    "host": "127.0.0.1",
    "port": 3493,
    "upsName": "ups",
    "timeout": 5000,
    "username": "optional",
    "password": "optional"
  }
}

Action System

Every action declares when conditions and optional delays. The lifecycle is:

conditions satisfied -> [delayMinutes countdown] -> execute -> [OS shutdown delay]
        |                        |                                  |
        v                        v                                  v
   short blip:            conditions clear:                power returns:
   nothing arms          countdown cancelled            `shutdown -c` cancels

Actions are evaluated in array order. Put Proxmox shutdown actions before host shutdown actions so guests get time to stop cleanly.

Action types:

| Type | Purpose | | ---------- | ------------------------------------------------------------------------- | | shutdown | Schedule host shutdown via shutdown -h +N (wall messages, cancellable). | | webhook | Send an HTTP GET or POST request. | | script | Execute a script from /etc/nupst/. | | proxmox | Shut down Proxmox VMs and LXC containers. |

Conditions (when): the action arms when ANY configured condition is satisfied.

| Condition | Behavior | | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------- | | onBattery.afterMinutes | On battery continuously for N minutes. 0 arms immediately on power loss; values >= 2 make the action blip-proof. | | batteryBelow | Battery capacity below N percent. Only satisfiable while ON battery - a low battery on grid power is charging, not an emergency. | | runtimeBelow | Estimated runtime below N minutes. Only satisfiable while ON battery. | | unknownFor | UPS unknown/unreachable for N minutes (fail-safe). | | statusChanged | Event: any power status transition. Notification actions only. | | powerRestored | Event: any non-online status transitions to online. Notification actions only. | | everyPoll | Event: every poll (~checkInterval). Notification actions only. | | actionLifecycle | Event: a sibling action on the same target went pending, executed, or was cancelled. Notification actions only. |

Event conditions fire instantly and are refused for destructive types (shutdown, proxmox): a bare power flicker can never trigger them.

Delays - two distinct, composable knobs:

| Option | Behavior | | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | delayMinutes | Cancellable countdown between arming and execution. Visible in status output; cancels automatically if conditions clear. Default 0. | | shutdownDelay (shutdown only) | OS-level shutdown -h +N delay: broadcasts wall messages and blocks logins. NUPST cancels it via shutdown -c if power returns before it elapses. Defaults to defaultShutdownDelay (5). |

Important semantics:

  • Edge-triggered with latching: an action fires once per condition episode. It re-arms only after all its conditions clear.
  • Cancel-on-restore: pending countdowns and an already-scheduled host shutdown are cancelled when conditions clear (power returns). A restarted daemon adopts a previously scheduled shutdown and can still cancel it.
  • Pause: nupst pause cancels pending countdowns and a scheduled host shutdown, and suppresses new arming. After nupst resume, conditions that still hold arm again (with their normal delays).
  • Emergency override: while a host shutdown is scheduled, NUPST keeps polling; if any UPS drains below the emergency runtime threshold (5 minutes), it forces an immediate halt.
  • Config edits: hot-reloaded. Pending countdowns survive unrelated edits; editing or removing an armed action cancels its work.
  • The daemon warns at startup about destructive actions configured to fire instantly on any power blip (onBattery.afterMinutes: 0 with no countdown).

Groups

Groups coordinate conditions across multiple UPS devices:

| Mode | Behavior | | -------------- | ----------------------------------------------------------------------------------------------------- | | redundant | Threshold actions trigger only when all group members are on battery and below the action thresholds. | | nonRedundant | Threshold actions trigger when any group member is on battery and below the action thresholds. |

For destructive battery-level group actions such as shutdown and proxmox, NUPST suppresses execution while any required group member is unreachable. This avoids acting on incomplete data during network failures. The explicit unknownFor fail-safe is exempt: it exists precisely for that scenario.

Recipes

Two-stage Proxmox host (guests first, host after):

"actions": [
  {
    "id": "stop-guests",
    "type": "proxmox",
    "when": { "onBattery": { "afterMinutes": 3 }, "batteryBelow": 30, "runtimeBelow": 15 },
    "proxmoxMode": "auto"
  },
  {
    "id": "halt-host",
    "type": "shutdown",
    "when": { "batteryBelow": 20, "runtimeBelow": 10 },
    "shutdownDelay": 5
  }
]

A 20-second blip does nothing: onBattery needs 3 sustained minutes and the battery never drains to the thresholds. A real outage stops guests at t+3m and halts the host when the battery degrades - and everything cancels if power returns first.

Two-node Proxmox cluster: give each node its own nupst with the config above, and use delayMinutes on the proxmox action (instead of or in addition to afterMinutes) if you want a visible, cancellable countdown you can abort with nupst pause. Add a notification webhook so you hear about it:

{
  "id": "notify-ops",
  "type": "webhook",
  "when": { "statusChanged": true, "powerRestored": true, "actionLifecycle": true },
  "webhookUrl": "https://ops.example.com/hooks/power"
}

USB UPS via NUT: expose the UPS through a local NUT/upsd (usbhid-ups driver), then use "protocol": "upsd" in the UPS config. Thresholds live in nupst and are evaluated daemon-side - device firmware quirks around persisting battery.charge.low do not matter here.

Proxmox Integration

The Proxmox action supports three modes:

| Mode | Behavior | | ------ | -------------------------------------------------------------------------------- | | auto | Prefer local qm/pct CLI tools when available as root, otherwise use the API. | | cli | Force local CLI mode. Requires root on a Proxmox host. | | api | Force Proxmox REST API mode with token auth. |

Useful options:

| Option | Purpose | | -------------------- | ------------------------------------------------------------ | | proxmoxHost | API host. Defaults to localhost. | | proxmoxPort | API port. Defaults to 8006. | | proxmoxNode | Node name. Defaults to system hostname. | | proxmoxTokenId | API token ID, for API mode. | | proxmoxTokenSecret | API token secret, for API mode. | | proxmoxExcludeIds | VM/CT IDs to skip. | | proxmoxStopTimeout | Graceful stop timeout in seconds. Defaults to 120. | | proxmoxForceStop | Force-stop guests that do not shut down. Defaults to true. | | proxmoxInsecure | Skip TLS verification in API mode. Defaults to true. | | proxmoxHaPolicy | none or haStop for HA-managed resources. |

HTTP Status API

Enable interactively:

sudo nupst feature httpServer

When enabled, the endpoint returns cached daemon status and requires a bearer token or token query parameter.

curl -H "Authorization: Bearer <token>" http://localhost:8080/ups-status
curl "http://localhost:8080/ups-status?token=<token>"

Response shape:

{
  "upsDevices": [
    {
      "id": "ups-main",
      "name": "Main UPS",
      "powerStatus": "online",
      "batteryCapacity": 100,
      "batteryRuntime": 45,
      "outputLoad": 23,
      "outputPower": 115,
      "outputVoltage": 230,
      "outputCurrent": 0.5
    }
  ],
  "paused": false,
  "actions": [
    {
      "actionId": "halt-host",
      "actionType": "shutdown",
      "targetKind": "ups",
      "targetId": "ups-main",
      "targetName": "Main UPS",
      "state": "pending",
      "armedCause": "onBattery",
      "executeAt": 1780000000000
    }
  ],
  "scheduledShutdownAt": null
}

Action state is one of idle, holding (duration condition accumulating), pending (cancellable countdown), or latched (executed; for shutdown actions with a future scheduledShutdownAt, the OS shutdown is still cancellable). Webhook payloads carry an event field naming the condition or lifecycle event (onBattery, batteryBelow, powerRestored, actionPending, actionCancelled, ...), plus a lifecycle object for sibling-action notifications.

Runtime Behavior

  • Default polling interval is 30 seconds.
  • UPS devices become unreachable after 3 consecutive polling failures.
  • Failure counters are capped at 100.
  • Shutdown delay defaults to 5 minutes unless overridden per action.
  • Pause state is stored at /etc/nupst/pause.
  • Live runtime state (UPS statuses, action lifecycle, scheduled shutdown) is written to /run/nupst/state.json each poll. nupst service status reads it, the HTTP endpoint serves the same snapshot, and a restarted daemon adopts a still-scheduled shutdown from it.
  • Normal monitoring talks to configured UPS, NUT, and Proxmox targets. Upgrade and release checks contact code.foss.global.

Installed system paths:

| Path | Purpose | | ------------------------------------------ | ------------------------------------------------ | | /opt/nupst/nupst | Installed binary. | | /usr/local/bin/nupst or /usr/bin/nupst | Symlink to the binary. | | /etc/nupst/config.json | Main configuration. | | /etc/nupst/pause | Pause state. | | /run/nupst/state.json | Daemon runtime state (tmpfs; cleared on reboot). | | /var/cache/nupst | Cache of the service (systemd CacheDirectory=). | | /etc/systemd/system/nupst.service | systemd unit. |

Supported Platforms

Release binaries are Node.js single executable applications built by @git.zone/tssea:

| Asset | Platform | | ----------------------- | ----------------------------------------- | | nupst-linux-x64 | Linux x86_64 | | nupst-linux-arm64 | Linux ARM64 | | nupst-macos-x64 | macOS Intel | | nupst-macos-arm64 | macOS Apple Silicon | | nupst-windows-x64.exe | Windows x86_64 (not smoke-tested on Windows) |

The Linux binaries need glibc 2.28 or later, libstdc++ and libatomic (libatomic1 on Debian and Ubuntu). Alpine and other musl systems are not supported. Each binary ships with <asset>.third-party-notices.txt and .json next to it in the release.

The Linux binaries embed the native inotify watcher of @push.rocks/smartwatch (sidecar smartwatch-rust, a static musl executable) and extract it on first use below the cache directory (/var/cache/nupst for the service, ~/.cache/smartsea otherwise); the config watcher requires it on Linux. macOS and Windows watch the configuration with chokidar.

Development

NUPST is a Node.js ESM package written in TypeScript. Deno is not supported.

pnpm install               # dependencies; the binary postinstall skips itself in a checkout
pnpm build                 # type check and compile ts/ to dist_ts/
node dist_ts/index.js help # run the CLI from the compiled sources
pnpm test                  # tstest suite (test/test.integration.node.ts needs .nogit/env.json and a real UPS)
pnpm run build:binary      # tsbundle + tssea: dist/binaries/nupst-<os>-<arch>[.exe]

Source map:

| Path | Purpose | | ------------------------- | ------------------------------------------------------------------- | | ts/index.ts | CLI and single executable entry point. | | ts/plugins.ts | Every module dependency (Node.js built-ins and npm packages). | | ts/cli.ts | Command parser and help output. | | ts/nupst.ts | Main facade wiring protocol clients, daemon, systemd, and handlers. | | ts/daemon.ts | Config loading, migration, polling loop, and effect application. | | ts/action-conditions.ts | Condition evaluator (levels, durations, events, group modes). | | ts/action-lifecycle.ts | Pending/cancel lifecycle state machine. | | ts/action-describe.ts | Plain-language descriptions for status output and config explain. | | ts/runtime-state.ts | /run/nupst/state.json reader/writer. | | ts/snmp/ | SNMP protocol implementation and OID sets. | | ts/upsd/ | NUT/UPSD client. | | ts/actions/ | Shutdown, webhook, script, and Proxmox action executors. | | ts/cli/ | Interactive command handlers and the condition wizard. | | ts/migrations/ | Config format migrations. | | ts/http-server.ts | Optional authenticated JSON status endpoint. |

License and Legal Information

This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the license file.

Please note: The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.

Trademarks

This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.

Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.

Company Information

Task Venture Capital GmbH
Registered at District Court Bremen HRB 35230 HB, Germany

For any legal inquiries or further information, please contact us via email at [email protected].

By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.