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

@better-compute/provider-hetzner

v0.3.0

Published

Hetzner providers for better-compute: Hetzner Cloud servers as VPS instances and offers.

Readme

@better-compute/provider-hetzner

Hetzner providers for better-compute: Hetzner Cloud servers as VPS instances, and Hetzner Robot dedicated servers bootstrapped into bcd nodes.

Install

npm install @better-compute/provider-hetzner

It is not part of the better-compute umbrella: install it next to better-compute (or the @better-compute/* packages it works with). It depends on @better-compute/core and @better-compute/vps.

Usage

import { betterCompute } from 'better-compute';
import { vps } from 'better-compute/vps';
import { hetznerCloud } from '@better-compute/provider-hetzner';

betterCompute({ /* database, iam, secret, baseURL, … */ modules: [vps({ providers: [hetznerCloud] })] });

// An account is data, validated by the provider's manifest. The token is a SecretRef, never a value.
await client.providerAccounts.create({
  tenantId,
  provider: 'hetzner-cloud',
  name: 'main',
  config: { token: 'vault://kv/hcloud', location: 'fsn1' },
});

hetznerCloud serves Hetzner Cloud servers as the instances of class: 'provider' pools. hetznerRobotProvider({ ssh }) turns dedicated servers into bcd nodes of metal-host or vps-host pools through vps's providerAccounts.bootstrap.

Hetzner Cloud

  • Config is { token: SecretRef, location: 'fsn1' | 'nbg1' | 'hel1' | 'ash' | 'hil' | 'sin' }. The token is resolved through the host's secret resolver on each request.
  • Idempotent create. The Hetzner API has no idempotency keys. Instead, each server carries better-compute.io/idempotency-key=<instance id> and better-compute.io/managed=true labels. create first looks the key up. If a concurrent create wins the unique server name, it returns the winner.
  • Server names are hostname-safe (inst_01… → inst-01…, at most 63 characters).
  • Offers come from /server_types and /datacenters, both paginated.
    • They include gross hourly prices in EUR for the account's location, or the queried region.
    • available comes from that location's datacenters.
    • Deprecated types are skipped.
    • GPU and metal queries return nothing, since Hetzner Cloud has neither.
  • Power: on → poweron, off → poweroff, graceful-off → shutdown, reset/cycle → reset, graceful-restart → reboot.
  • Other operations: reinstall → rebuild. list yields only the servers this provider manages.
  • Errors
    • A 404 is absent: get answers undefined, and delete and power throw NOT_FOUND.
    • A 429 is RATE_LIMITED, with retryAfterMs taken from RateLimit-Reset.
    • Everything else is PROVIDER_ERROR, with details: { status, code } holding Hetzner's error code.
  • Transport: hetznerCloudProvider({ fetch, endpoint }) swaps the transport. The tests use an in-memory emulator of the API, and the same hook can record or replay real traffic.

The package passes providerConformanceCases() from @better-compute/vps.

Hetzner Robot

hetznerRobotProvider({ ssh }) (provides: ['metal']) turns the dedicated servers an account already has into metal-host nodes that run bcd ("platform-owned provider machines"). hetznerRobot is the same provider without an SSH executor: offers, claims, power and rescue, but not identify or bootstrap.

  • Config: { user, password: SecretRef, rescueKeyFingerprint, rescueKey: SecretRef, image }. The fingerprint names an SSH key stored in Robot, and rescueKey is its private key. image defaults to Ubuntu-2404-noble-amd64-base.tar.gz.

  • Offers are the servers with an empty name that are not cancelled.

    • Specs come from robotProducts (GEX44, GEX130, AX41-NVMe, AX52, AX102, EX44) or options.products, because Robot's API reports only product names. An unknown product reports 1 vCPU and 1 GiB until you configure it.
    • available means Robot says the server is ready.
  • create claims instead of ordering. It names the server bc-<idempotency key>, and a retry finds the same claim. A server someone has named for another use is never offered or claimed (CONFLICT).

  • delete releases the claim (clears the name and deactivates rescue).

    • Ordering and cancelling servers are contractual actions, left to people.
    • get treats unclaimed servers as absent.
  • Power maps to Robot resets: reset/cycle → hw, graceful-restart → sw, graceful-off/on → power, off → power_long. Robot cannot report power state.

  • rescue activates Linux rescue with the account's key, then hardware-resets into it.

  • identify(machine) boots rescue (unless it already answers) and reads /sys/class/dmi/id/product_uuid over SSH, so the bootstrap token can be bound to the machine (expect.productUuid; the provisioned attestor refuses unbound tokens). It leaves the server in rescue for bootstrap.

  • bootstrap(machine, { joinToken, bcdUrl, sha256, controlPlaneUrl, pins }) runs in these steps:

    1. It reuses a rescue system that already answers, else activates rescue and waits for SSH, through the injected SshExecutor (one hardware reset for identify + bootstrap).
    2. It writes the single-use provisioned join token over SSH stdin into a umask 077 file.
    3. It runs bootstrapScript, which calls installimage with software RAID 1 over the NVMe disks, else the SATA disks.
    4. The post-install script, built in rescue, places the token at /etc/bcd/join-token (0600). It downloads bcd over https and checks its sha256. It installs bcd-join.service, a oneshot that runs robotJoinArgv(controlPlaneUrl, pins) while the token file exists, and bcd.service (bcd node), then shreds itself.
    5. The server reboots and rescue is deactivated. On first boot bcd join --server … --attestor provisioned --serving-ca-pin … --join-signing-pin … --join-encryption-pin … reads the gateway from the control plane's well-known document, refuses it if its pins differ from the ones on the command line, joins, writes /etc/bcd/config.toml and removes the token. Re-runs on a joined node succeed without a token. bcd's own tests parse these command lines with bcd's flags, so the two sides cannot drift.

    The token never appears in user data, in argv or in the script sent over SSH.

bcd is not released as a binary yet, so bcdUrl and sha256 must point at a bcd build you host. The Robot provider's tests run against an emulated Robot API, and can also syntax-check the bootstrap script with a POSIX shell.

Documentation

License

Apache License 2.0