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

@jai47/hj2

v0.3.3

Published

Hyperion Just-in-time Journey — CLI-first VPS helper: nginx, Certbot, and Node/static site publishing on Ubuntu/Debian

Readme

hj2

CLI-first VPS deployment engine for Ubuntu/Debian.

Deploy Node.js and static applications to any VPS with automatic Nginx configuration, Let's Encrypt TLS, systemd process management, versioned releases, blue-green deployments, and instant rollback — all from a single CLI.

npm i -g @jai47/hj2

cd my-project
hj2 init --domain app.example.com
sudo hj2 deploy --email [email protected]

Your app is now live at https://app.example.com with HTTPS, reverse proxy, systemd supervision, and rollback support.


Table of Contents


Install

npm i -g @jai47/hj2

This installs the hj2 binary globally. Requires Node.js ≥ 18.


Quick Start

Static site (Vite, Next.js export, plain HTML)

cd my-vite-app
hj2 init --domain www.example.com
sudo hj2 deploy --email [email protected]

Node.js app (Express, Fastify, Next.js server, etc.)

cd my-api
hj2 init --domain api.example.com
sudo hj2 deploy --email [email protected]

One-shot proxy or static publish (no hj2.yml needed)

# Reverse proxy to a running app on port 3000
sudo hj2 publish --domain api.example.com --port 3000 --email [email protected]

# Serve a static directory
sudo hj2 publish --domain www.example.com --static ./dist --email [email protected]

How It Works

When you run hj2 deploy, the engine follows this pipeline:

Source → Build → Release → Start → Health Check → Proxy → TLS → Activate
  1. Detect — reads hj2.yml for runtime, build, and start commands.
  2. Copy — rsyncs project files into a versioned release directory.
  3. Install — runs npm ci --omit=dev (or npm install) for Node.js projects.
  4. Build — executes the configured build command.
  5. Release — creates a timestamped release under /var/lib/hj2/apps/<name>/releases/.
  6. Start — writes a hardened systemd unit (dedicated system user, sandboxing) and starts the process via a wrapper script (Node.js apps) or copies built output to a web root (static apps).
  7. Health check — polls the configured health endpoint until it responds HTTP 200.
  8. Proxy — writes an Nginx server block with reverse proxy (or static file serving).
  9. TLS — requests a Let's Encrypt certificate via Certbot.
  10. Activate — symlinks current → releases/<id> and records the deployment.

If any step fails and autoRollback is enabled (default), the previous release is automatically restored.


Commands

Setup & Publish (Phase 1)

Quick commands for simple Nginx + Certbot site management without a full deployment pipeline.

hj2 setup

Install and enable Nginx, Certbot, and the Certbot Nginx plugin. Run once on a fresh server.

sudo hj2 setup

hj2 publish

Publish a domain as a reverse proxy or static site in a single command.

# Reverse proxy
sudo hj2 publish --domain api.example.com --port 3000 --email [email protected]

# Static files
sudo hj2 publish --domain www.example.com --static ./dist --email [email protected]

# HTTP only (skip TLS)
sudo hj2 publish --domain dev.example.com --port 4000 --no-ssl

# Overwrite existing site
sudo hj2 publish --domain api.example.com --port 3001 --email [email protected] --force

| Flag | Description | |---|---| | -d, --domain <host> | Domain name (required) | | -p, --port <n> | Local port to reverse-proxy | | -s, --static <path> | Directory of static files to serve | | -e, --email <addr> | Email for Let's Encrypt (cached after first use) | | --no-ssl | Skip Certbot; serve HTTP only | | -f, --force | Overwrite an existing site for this domain |

hj2 list

List all sites managed by hj2.

hj2 list

hj2 remove

Remove a managed site and its Nginx config.

sudo hj2 remove --domain api.example.com

# Keep the TLS certificate
sudo hj2 remove --domain api.example.com --keep-cert

Init & Deploy

hj2 init

Detect the current project and generate an hj2.yml configuration file.

cd my-project
hj2 init

The init command inspects package.json and project structure to auto-detect:

  • Runtimenode or static
  • Build command — from scripts.build
  • Start command — from scripts.start, scripts.start:prod, or falls back to node server.js / node index.js
  • Output directorydist (Vite) or out (Next.js export)
  • Port — defaults to 3000 for Node.js apps
# Set a domain during init
hj2 init --domain app.example.com

# Overwrite existing hj2.yml
hj2 init --force

# Init a different directory
hj2 init --cwd /path/to/project

hj2 deploy

Build and deploy the app according to hj2.yml.

sudo hj2 deploy

# Override domain
sudo hj2 deploy --domain staging.example.com

# Provide Let's Encrypt email
sudo hj2 deploy --email [email protected]

# Skip TLS
sudo hj2 deploy --no-ssl

# Force blue-green deployment
sudo hj2 deploy --blue-green

# Disable blue-green (stop-then-start)
sudo hj2 deploy --no-blue-green

# Deploy from a different directory
sudo hj2 deploy --cwd /path/to/project

| Flag | Description | |---|---| | -d, --domain <host> | Override the domain in hj2.yml | | -e, --email <addr> | Let's Encrypt email | | --no-ssl | Skip TLS certificate | | --blue-green | Force blue-green deployment | | --no-blue-green | Disable blue-green (restart in place) | | --cwd <path> | Project directory (default: current directory) |

hj2 rollback

Roll back to the previous release. No rebuild required — the previous release directory is reactivated instantly.

sudo hj2 rollback          # rollback current project (reads hj2.yml)
sudo hj2 rollback myapp    # rollback a specific app by name

App Lifecycle

hj2 status [app]

Show the current status of an app.

hj2 status
hj2 status myapp

Output:

app: myapp
domain: app.example.com
runtime: node
release: 20260827-183200
port: 3000
systemd: active

hj2 logs [app]

View journal logs for an app's systemd unit.

hj2 logs myapp
hj2 logs myapp -n 50    # last 50 lines

hj2 restart [app]

Restart the app's systemd service.

sudo hj2 restart myapp

hj2 start [app] / hj2 stop [app]

Start or stop the app's systemd service.

sudo hj2 start myapp
sudo hj2 stop myapp

hj2 app remove [app]

Remove an app entirely — stops the systemd unit, deletes all releases, and unregisters the app.

sudo hj2 app remove myapp

hj2 deployment list [app]

List all releases for an app.

hj2 deployment list myapp

Domains

hj2 domain add <domain>

Add or bind a domain to an app, port, or static directory.

# Bind to a registered app
sudo hj2 domain add app.example.com --app myapp

# Bind to a port
sudo hj2 domain add api.example.com --port 3000 --email [email protected]

# Bind to static files
sudo hj2 domain add docs.example.com --static ./docs --email [email protected]

# Dry run — show what would happen without making changes
sudo hj2 domain add app.example.com --app myapp --dry-run

| Flag | Description | |---|---| | --app <name> | Bind to a registered app | | -p, --port <n> | Proxy port | | -s, --static <path> | Static files directory | | -e, --email <addr> | Let's Encrypt email | | --no-ssl | Skip TLS | | -f, --force | Overwrite existing site | | --dry-run | Print actions without changing the system |

hj2 domain remove <domain>

Remove a managed domain.

sudo hj2 domain remove app.example.com

# Keep the Nginx site files
sudo hj2 domain remove app.example.com --keep-nginx

hj2 domain list

List all managed domains.

hj2 domain list

hj2 domain verify <domain>

Check DNS resolution and warn on mismatches.

hj2 domain verify app.example.com

Environment Variables

Manage per-app environment variables.

# Set a variable
sudo hj2 env set myapp NODE_ENV production

# List all variables
hj2 env list myapp

# Remove a variable
sudo hj2 env remove myapp NODE_ENV

Secrets

Secrets are stored base64-encoded with restrictive file permissions (0600), separate from application metadata.

# Set a secret
sudo hj2 secret set myapp STRIPE_SECRET_KEY sk_live_xxx

# List secret keys (values are never printed)
hj2 secret list myapp

# Remove a secret
sudo hj2 secret remove myapp STRIPE_SECRET_KEY

Server Management

hj2 server provision

SSH into a remote server and install Nginx, Certbot, and the hj2 directory structure.

hj2 server provision --host 1.2.3.4
hj2 server provision --host 1.2.3.4 --user deploy

# Dry run — print the remote provisioning script without executing
hj2 server provision --host 1.2.3.4 --dry-run

hj2 server harden

Enable UFW firewall and allow ports 22 (SSH), 80 (HTTP), and 443 (HTTPS).

sudo hj2 server harden

# Dry run
sudo hj2 server harden --dry-run

hj2 server update

Run apt-get upgrade on the server.

sudo hj2 server update

hj2 server status

Show Nginx systemd status.

hj2 server status

hj2 server doctor

Run host health checks (OS, resources, network, proxy, TLS).

hj2 server doctor

Services

Install and manage database and cache services via apt.

# Install a service
sudo hj2 service install postgres
sudo hj2 service install redis
sudo hj2 service install mysql
sudo hj2 service install mongodb

# List tracked services
hj2 service list

# Start / stop / remove
sudo hj2 service start postgres
sudo hj2 service stop postgres
sudo hj2 service remove postgres

Supported services: postgres, redis, mysql, mongodb.


Backups

Create and manage tar.gz backups of app releases.

# Create a backup
sudo hj2 backup create myapp

# Create and upload to S3
sudo hj2 backup create myapp --s3

# List local backups
hj2 backup list

# Restore a backup
sudo hj2 backup restore <id>

# Configure backup destination
sudo hj2 backup configure --destination /mnt/backups
sudo hj2 backup configure --s3-bucket my-backups

Git Deploy

Clone (or pull) a Git repository and deploy in one step.

sudo hj2 git-deploy [email protected]:user/project.git

# Specify branch, domain, and email
sudo hj2 git-deploy [email protected]:user/project.git \
  --branch main \
  --domain app.example.com \
  --email [email protected]

# Use an existing checkout
sudo hj2 git-deploy [email protected]:user/project.git --cwd /opt/project

GitHub Integration

# Store GitHub token and repo
sudo hj2 connect github --token ghp_xxx --repo user/project

# Print the webhook URL for an app
hj2 connect webhook myapp

Multi-Server

Manage multiple server profiles for staging/production deployments.

# Add server profiles
hj2 servers add --name production --host 1.2.3.4
hj2 servers add --name staging --host 5.6.7.8 --user deploy --environment staging

# List profiles
hj2 servers list

# Deploy to a specific server (rsyncs project and runs hj2 deploy remotely)
hj2 servers deploy --server production --domain app.example.com

# Remove a profile
hj2 servers remove staging

API

Start a local HTTP control API (and optional UI).

hj2 api start                          # http://127.0.0.1:7422/
hj2 api start --host 0.0.0.0 --port 8080

Doctor

Run comprehensive health checks across the host and all managed apps.

hj2 doctor

Checks include OS support, CPU/memory/disk, DNS, ports, Nginx status, TLS certificates, runtime availability, process health, and backup recency.


Configuration (hj2.yml)

hj2 init generates this file automatically. Edit it to customize your deployment.

# Generated by hj2 init — edit as needed
name: myapp
runtime: node
domain: app.example.com
build: "npm run build"
start: "npm start"
port: 3000
healthPath: /health
shared:
  - database/sqlite
  - public/uploads
deploy:
  autoRollback: true
env:
  NODE_ENV: "production"

Fields

| Field | Type | Default | Description | |---|---|---|---| | name | string | from package.json | App identifier (used for systemd unit, release dirs) | | runtime | node | static | auto-detected | Deployment strategy | | domain | string | — | Domain to serve (can be overridden with --domain) | | build | string | — | Build command (e.g. npm run build) | | start | string | npm start | Start command (Node.js apps only) | | output | string | dist | Static output directory (static apps only) | | port | number | 3000 | Port for the Node.js process | | healthPath | string | / | Health check endpoint | | shared | string[] | — | Relative paths persisted across releases (see Shared persistent volumes) | | deploy.autoRollback | boolean | true | Automatically rollback on deploy failure | | env | object | — | Environment variables injected into the systemd unit |

Static site example

name: docs
runtime: static
domain: docs.example.com
build: "npm run build"
output: dist
deploy:
  autoRollback: true

Node.js API example

name: api
runtime: node
domain: api.example.com
build: "npm run build"
start: "npm start"
port: 4000
healthPath: /health
deploy:
  autoRollback: true
env:
  NODE_ENV: "production"

Production Features

hj2 includes three production-oriented capabilities for Node.js apps: per-app process isolation, pinned Node versions via fnm, and persistent shared directories that survive deploys and rollbacks.

Process isolation

Every Node.js app runs under its own dedicated system user (hj2-<appname>), not as root. On first deploy, hj2 creates the user and group, assigns ownership of release and shared directories, and writes a hardened systemd unit.

Systemd hardening applied to Node apps:

| Option | Purpose | |---|---| | User / Group | Run as hj2-<appname> | | ProtectSystem=full | Read-only mount for most of /usr, /boot, /etc | | PrivateTmp=true | Private /tmp for the process | | NoNewPrivileges=true | Block privilege escalation | | ReadWritePaths | Allow writes only under the app's release, shared, and metadata trees |

Static sites are served directly by Nginx and do not use systemd isolation.

Inspect the generated unit after deploy:

systemctl cat hj2-myapp

Node version management

If your project includes a .nvmrc or .node-version file, hj2 uses fnm (Fast Node Manager) to install and activate that version for dependency install, build, and runtime.

Install fnm on the server (once per host):

curl -fsSL https://fnm.vercel.app/install | bash
# reload shell or source ~/.bashrc
fnm --version

Pin a version in your repo:

echo "20.11.0" > .nvmrc
# or
echo "20.11.0" > .node-version

During hj2 deploy:

  1. hj2 reads .nvmrc or .node-version from your project root.
  2. fnm installs the version if missing.
  3. npm ci / npm install and the build command run under that Node version.
  4. A start wrapper (.hj2-start.sh) sets up fnm before executing your start command.
  5. The Node version is recorded in release metadata (release.json).

If no version file is present, hj2 uses the system node on PATH (same as before).

Release metadata and CLI:

hj2 deployment show myapp 20260831-071215
# includes nodeVersion when pinned

Shared persistent volumes

Use shared in hj2.yml to keep data across deploys — SQLite databases, user uploads, local caches, etc. Paths are relative to your project root.

name: myapp
runtime: node
domain: app.example.com
start: "node server.js"
port: 3000
shared:
  - database/sqlite
  - public/uploads

How it works:

  1. hj2 creates /var/lib/hj2/apps/<name>/shared/<path> on the server (once).
  2. Before starting a release, hj2 replaces matching paths inside the release directory with symlinks into shared/.
  3. Your app reads and writes the same files on every deploy.
  4. Rollback re-links symlinks so the previous release uses the same persistent data.

Example layout after deploy:

/var/lib/hj2/apps/myapp/
├── shared/
│   ├── database/sqlite/app.db    ← persistent
│   └── public/uploads/photo.jpg  ← persistent
├── releases/20260831-120000/
│   ├── database/sqlite → ../../shared/database/sqlite
│   └── public/uploads → ../../shared/public/uploads
└── current → releases/20260831-120000

Rules:

  • Paths must be relative (no .. or absolute paths).
  • Empty directories in a new release are replaced by symlinks; existing files in shared/ are preserved.
  • Ownership is set to the app's system user so the process can read and write.

Full Node.js example with all production features:

name: api
runtime: node
domain: api.example.com
build: "npm run build"
start: "node dist/server.js"
port: 4000
healthPath: /health
shared:
  - data/sqlite
  - storage/uploads
deploy:
  autoRollback: true
env:
  NODE_ENV: "production"

Add .nvmrc in the repo root, install fnm on the server, then:

sudo hj2 deploy --email [email protected]

Deployment Strategies

Standard Deploy

For first-time deployments or when blue-green is disabled. The app is stopped, the new release is activated, and the app is restarted.

sudo hj2 deploy
sudo hj2 deploy --no-blue-green

Blue-Green Deploy

When a previous release exists, hj2 automatically uses blue-green deployment:

  1. The current release stays live on its assigned port.
  2. The new release starts on an alternate port.
  3. Health checks run against the new release.
  4. If healthy, the proxy switches traffic to the new release.
  5. The old release is stopped.
  6. The previous release is kept for instant rollback.
sudo hj2 deploy              # auto blue-green when a previous release exists
sudo hj2 deploy --blue-green  # force blue-green

Automatic Rollback

If a deployment fails at any step (build, health check, proxy), hj2 automatically:

  1. Stops the failed release.
  2. Reactivates the previous release.
  3. Restores proxy traffic.

No rebuild is needed — the previous release directory is used as-is.

# Manual rollback
sudo hj2 rollback myapp

Disable auto-rollback in hj2.yml:

deploy:
  autoRollback: false

Server Filesystem Layout

/etc/hj2/
├── config.json          # Global config (Let's Encrypt email, etc.)
├── sites.json           # Managed Nginx sites registry
├── apps.json            # Registered app metadata
├── ports.json           # Port allocations
├── servers.json         # Multi-server profiles
├── connect.json         # GitHub / webhook config
├── services.json        # Tracked database/cache services
├── backup.json          # Backup configuration
└── secrets/             # Encrypted secrets (mode 0600)

/var/lib/hj2/apps/<name>/
├── releases/
│   ├── 20260827-173000/
│   ├── 20260827-180500/
│   └── 20260827-183200/
├── current -> releases/20260827-183200
├── shared/              # Persistent data (see shared: in hj2.yml)
│   ├── database/sqlite/
│   └── public/uploads/
└── metadata/
    └── history.json

/var/www/hj2/<domain>/    # Static site web roots
/var/log/hj2/             # Audit log

Each release is a self-contained directory. The current symlink points to the active release, enabling instant rollback by re-pointing the symlink.


Requirements

| Requirement | Details | |---|---| | OS | Ubuntu or Debian with systemd | | Node.js | ≥ 18 on the server (system Node, or pinned via fnm — see Node version management) | | fnm | Required only when using .nvmrc / .node-version | | Root | Required for mutating commands (deploy, publish, setup, server harden, etc.) | | DNS | A/AAAA records pointing to the server for TLS | | Ports | 80 and 443 open for Let's Encrypt and HTTPS |

Non-mutating commands (list, status, logs, domain list, doctor, etc.) do not require root.


License

MIT