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

kampodine

v0.6.0

Published

Kamal-style deploys for Alpine + Podman hosts, built on kamal-proxy

Readme

kampodine

kamal + podman + alpine — Kamal-style deploys for Alpine + Podman hosts, built on kamal-proxy.

Why this exists

kamal is a great deploy tool with one hard requirement pair: Docker engine and systemd on the target host. If you run Alpine Linux — or a strict-podman fleet with no Docker anywhere — kamal cannot deploy for you:

  • Alpine ships no systemd at all. Not in main, community, or edge (verified on v3.22). OpenRC is the init system, period.
  • Not every fleet runs Docker. Rootful Podman under OpenRC supervise-daemon is a perfectly good way to host containers — kamal just can't talk to it.
  • kamal-proxy was never the problem. It's a container image — completely init-agnostic. Host routing, ACME TLS, and gapless target swaps via kamal-proxy deploy work fine on an Alpine + Podman host.

kampodine keeps kamal-proxy as the TLS edge and replaces everything around it with Alpine-native pieces:

| kamal assumes | kampodine uses | |---|---| | Docker engine | Podman (rootful, no Docker anywhere) | | systemd units | OpenRC supervise-daemon services | | Container registry | podman save \| ssh podman load — a plain SSH stream | | kamal proxy orchestration | kamal-proxy itself, unchanged |

What it does

Same mental model as kamal: build → ship → health-gate → cutover → instant rollback.

kampodine deploy:
  podman build --build-arg BUILD_ID=<git sha>        # committed tree only
  podman save <img>:<sha>  |  ssh <host> podman load
  podman tag <img>:<sha> <img>:latest                # on the VM
  env file generation -> /etc/<app>/env (0600)       # from your secret store
  rc-service <app> restart
  health endpoint must report the DEPLOYED sha       # health gate
  kamal-proxy deploy <host> -> <target>              # gapless cutover
  public smoke: health + build-id endpoints
  • kampodine config — per-instance profiles in ~/.kampodine/config.json (dir 0700, file 0600): init --name prod --host root@<ip> --ssh-key <path> [--proxy-host <h>] [--group main] [--set-default], list (+ --group cosmetic filter, default marker, sshKey shown as PATH only), show, set-default, remove (the last profile needs --force). FLAT — no inheritance between profiles (the schema leaves room for a future defaults block; nothing reads one). Resolution everywhere: --profile flag | KAMPODINE_PROFILE | defaultProfile | the legacy env vars — a profile's host/sshKey/proxyHost feed the same ladders every subcommand already uses
  • kampodine metrics — one-shot VM snapshot over one SSH round-trip: load + CPUs, memory, disk with an images/data/other breakdown, per-container CPU/MEM, uptime, top-5 by RSS; --warn-disk <pct> (default 90) exits 1 at the threshold (the deploy gate's verdict logic); --watch N to loop. status --verbose folds the same snapshot in. One-shot CLI view only — continuous monitoring belongs to Beszel
  • kampodine backup — OCI Object Storage backup family (--bucket, default esellar-libsql-backups; oci config-file --profile or --instance-principal auth ONLY — never stored credentials): list (sizes + timestamps; LIST-denied prints the missing-policy guidance), download (GET works without LIST; sha256/MD5 verification against object metadata when present; output 0600; fingerprint summaries, never contents), verify (read-only: pipes the local .db — or every .db member of a .tgz, extracted to a scratch dir — over ssh stdin into the VM's /usr/local/bin/restore-verify; NEVER writes into /data/tenants), and restore-plan (prints the documented stop/swap/start sequence; never executes anything)
  • kampodine deploy — stream deploy; --version <sha> restreams an existing build; --rollback [sha] is an instant image-tag rollback (the previous image stays on the VM for exactly this). Every run appends to the local deployment ledger and ends with a total-deployments footer; a disk guard reads the VM's df before the build (LOUD warning above 90%, --require-disk <pct> fails closed)
  • kampodine deploy <lifecycle> — the deployment lifecycle as native subcommands (same --host/--ssh-key resolution as deploy): list merges the VM's sha-tagged images (running one marked) with the local ledger (~/.kampodine/deployments.jsonl — append-only history of every deploy/rollback: ts, host, sha, tag, result, duration, git subject; never secrets) into a table with a Total deployments: N footer; prune [--keep N] [--dry-run] reclaims VM disk by removing old sha-tagged images while ALWAYS keeping the running image, ts-rollback, and the newest N (the 2026-10-08 disk-full outage is the reason this exists); logs [--lines N], restart (rc-service, deploy's own restart path), and shell (interactive sh, or exec -- <cmd>) end the hand-rolled-ssh era
  • kampodine env — manage the remote app env file (/etc/kampodine/env, 0600 root) without ever printing a secret: list shows KEY + fingerprints only (value length + first 2 chars), push --file <env> uploads over ssh stdin into a 0600 temp + atomic mv, pull streams the raw payload to stdout or --out (written 0600). Host/key resolution is identical to deploy: --host root@<ip> | KAMPODINE_HOST, --ssh-key <path> | KAMPODINE_SSH_KEY | ssh-agent. env fingerprint previews the masking for any local file — values never leave stdin
  • kampodine dns — OCI DNS records for the post-sslip.io era: records, add --name <label> --type A|AAAA|CNAME --value <target> [--ttl 300], rm. Shell out to the oci CLI; auth is the OCI config file (--profile) or --instance-principal ONLY — kampodine never accepts, stores, or logs credential material. add merges into the existing RRSet (round-robin survives), rm filters it; types/ttl/value are validated locally before any provider call
  • kampodine bluegreen status|init|provision|flip|rollback — reserved public IP management for a two-instance blue/green pair on OCI: zero DNS change, health-gated flips with an ACME-first cutover (reserved IP assigned → cert issued for its hostname → served sha verified) and automatic rollback. A guest anchor watcher installed by vm-prepare holds the flip's IP half — add-only and inert until a flip writes its anchor config. Every sub-step has its own --help with usage + examples
  • kampodine status — deployment count (from the ledger), live health through the proxy, and — with --host — VM disk usage (loud warning above 90%), the sha-tagged image count with a reclaimable-by-prune estimate, a service roll call (app, kamal-proxy, walshipper if present), plus the blue/green pair view; --verbose adds the full metrics snapshot
  • kampodine vm-prepare — first-run bootstrap of a bare Alpine host: sshd hardening (fresh VMs pass unattended), busybox-wget health probes (the golden image ships no curl), the blue-green anchor watcher, the podman stack, and OpenRC supervise-daemon units for app + kamal-proxy
  • kampodine image-import — golden qcow2 → OCI custom image. Caveat: imported custom images boot BIOS and are rejected by Ampere (A1) shapes — use bluegreen provision for ARM targets
  • kampodine migrate — sqlite migrations over SSH

Help everywhere: kampodine --help (or bare kampodine) prints a grouped command index (DEPLOY / DEPLOY LIFECYCLE / INFRA / DNS / ENV / CONFIG / METRICS / BACKUP, vercel-style); every command — and every deploy-lifecycle/bluegreen/env/dns/config/backup sub-step — answers --help with usage + examples. No subcommand silently does nothing on --help.

Host init auto-detection: on first connect kampodine probes rc-service (Alpine/OpenRC) vs systemctl (systemd) and caches the verdict in the active profile entry; service restarts and the status roll call emit the right commands for the detected init (svc_action). Alpine behavior is byte-identical to always. Groundwork for non-Alpine targets — no per-distro bootstrap.

Kamal parity

| kamal | kampodine | |---|---| | kamal deploy | kampodine deploy | | kamal rollback | kampodine deploy --rollback [sha] | | kamal details | kampodine bluegreen status | | kamal proxy … | kamal-proxy container, unchanged | | kamal app logs | kampodine deploy logs | | kamal app exec | kampodine deploy shell (or deploy shell exec -- <cmd>) | | (no equivalent) | kampodine deploy list / deploy prune / deploy restart / status | | kamal reboot / app boot | rc-service <app> restart over SSH (init-aware: systemctl on systemd hosts) | | kamal's per-env config files | kampodine config profiles (--profile everywhere) | | (no equivalent) | kampodine metrics / backup list|download|verify|restore-plan |

Building the expected image

kampodine's target host is a converged Alpine image — Alpine (aarch64 or x86_64) with the podman runtime, OpenRC services, and kernel settings the deploy flow relies on. The reference build pipeline:

  1. packer + qemu (UEFI) boots the Alpine virt ISO. Boot is driven over the serial console, not VNC — the ISO's VGA tty has no getty; a serial bootstrap helper handles the login prompt and ash's ESC[6n cursor query, then types one line that brings up DHCP and fetches the bootstrap script.
  2. install runs setup-disk under UEFI (ESP + grub-efi, --removable fallback for hosts with fresh NVRAM), sanitizes apk repos, installs python3 (ansible runtime) + openssh, enables boot-critical OpenRC services, bakes your public key, reboots.
  3. ansible converge installs the podman stack — podman, netavark, aardvark-dns, iptables (netavark needs it), the cgroups service (cloud VMs boot without one), ip_unprivileged_port_start=80 sysctl — and lays down supervise-daemon templates for app + kamal-proxy. The same playbook converges the golden image and repairs live drift: one convergence path, the image is by construction what the playbook produces.
  4. ship the qcow2 to your cloud. On OCI: import from a launched instance (or boot-volume seeding) — raw QCOW2 object imports land x86-flagged and won't boot on Ampere. kampodine image-import + kampodine vm-prepare handle that path.
  5. First deploy issues TLS (ACME) via kamal-proxy — no cert carry.

Verify a build by booting the qcow2 locally (qemu UEFI boot, serial console) before importing — the image should reach a working sshd with the podman stack installed.

Shipping the image: mirror vs no-mirror

Two ways to get the built container image onto the target — kampodine automates the second; the first is a documented recipe:

No-mirror (default): the SSH stream. podman save | ssh podman load over the deploy's own SSH connection. Nothing exists between you and the VM: no registry, no tunnel, no extra ports, no credentials beyond SSH.

  • ✅ zero infrastructure; works air-gapped; nothing to keep alive
  • ✅ sha-tagged images land atomically; podman load dedupes layers by digest
  • ⚠️ each deploy streams the full image tar; no cross-deploy layer cache

Mirror (optional): any OCI registry. podman push the sha-tagged image to a registry the VM can reach; the VM podman pulls and retags :latest — the health-gate/cutover tail is identical. Works with hosted registries (GHCR, Docker Hub, cloud registries) or a local one mirrored over an SSH reverse tunnel (ssh -R 5000:127.0.0.1:5000).

  • ✅ layer caching (fast repeated deploys), multi-target fan-out, CI-native
  • ⚠️ requires registry reachability + credentials on the VM
  • ⚠️ macOS note: AirPlay listens on :5000 by default — pick another port for a local registry

Rule of thumb: one VM and no existing registry → stream. Multiple targets, CI-driven deploys, or a registry you already run → mirror. Registry-path automation is on the roadmap; today the stream is the automated path.

Prerequisites

Deploy machine: node ≥ 20, podman, ssh key access to the target, oci CLI (for bluegreen / image-import / dns / backup), jq (for dns record surgery

  • profile config reads), and whatever secret-resolution your env-file step uses (kampodine is agnostic; the reference setup uses varlock + pass).

Target: a converged Alpine + Podman + OpenRC host (see above), reachable over ssh as root, with kamal-proxy running.

Status

Proven on production deployments (v0.2.x). Defaults are still opinionated — service/container names, env-file path, deploy-host env — and genericizing them is the top roadmap item. Design reference + roadmap: kampodine.md. Release history: CHANGELOG.md.

License

Copyright (c) 2026 talha7k — AGPL-3.0 (see LICENSE).

Copyleft with teeth: anyone who uses, modifies, or hosts kampodine — including as a network service — must publish their source under AGPL-3.0, and original attribution must be preserved.