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

@parhelia/capture

v0.4.13005

Published

Parhelia's capture sidecar: real Chromium screenshots of CMS pages without an open editor. Install with `npx @parhelia/capture install`.

Readme

@parhelia/capture

Parhelia's capture sidecar: real Chromium screenshots and DOM captures of CMS pages for agents, without an editor open in anyone's browser. It long-polls Parhelia outbound (no inbound port), renders each page top-level in its own sandboxed browser behind an egress proxy, and drives it with the controller page the Parhelia host serves. The capture logic ships with the host; the sidecar carries none, so it needs no upgrade when Parhelia does.

Node 22 or later. Plan: docs/capture-sidecar-plan.md.

Install (Settings › Capture service › Add)

Prerequisites: Node 22 or later with npm; root (sudo) on Linux with systemd, an Administrator PowerShell on Windows; outbound HTTPS to Parhelia and the rendering hosts. An administrator clicks Add under Settings › Capture service, which shows the Node bootstrap line for the OS and a paste-ready command with a one-time pairing code:

sudo npx --yes @parhelia/capture install --origin https://parhelia.internal.example --pair XXXXX-XXXXX-XXXXX-XXXXX

--origin is the address this machine reaches Parhelia at. The key is generated on this machine; Parhelia stores only its SHA-256. Pipeline-provisioned hosts skip the code: the host's connection string parhelia.capture (key=pcs1_…;capabilities=cms,public-web;update=notify) names a key, and install --key-file <file> uses it.

What install sets up on Linux: the users parhelia-capture (the worker) and parhelia-capture-browser (the browser host, whose only group is parhelia-capture-ipc); the code under /opt/parhelia-capture/versions/<version> with the current link, root-owned and read-only to both; the key /etc/parhelia-capture/worker.key (0600, the worker's); Chromium's libraries and an emoji font on apt systems; the units parhelia-capture-worker, parhelia-capture-browser-host (no network of its own, see below) and parhelia-capture-updater with its timer; and the command /usr/local/sbin/parhelia-capture. On Windows: the code under %ProgramFiles%\Parhelia Capture, the data and key under %ProgramData%\Parhelia Capture with inheritance off, the browser's outbound firewall rule, and three scheduled tasks.

parhelia-capture status        # versions, services, last poll, public websites, the updater's last run
parhelia-capture doctor        # names what is wrong (Node, origin, SSO, key, permissions, sandbox, proxy or CA)
parhelia-capture update        # an update now (also for "Notify only" workers); --from <tarball> for air-gapped ones
parhelia-capture uninstall     # removes everything; revoke the worker under Settings › Capture service

Updates

The Parhelia host names the sidecar version it wants, with the tarball's integrity hash, each browser archive's SHA-256 and a security minimum (written by the release pipeline, frontend/scripts/write-capture-manifest.mjs). The privileged updater installs nothing whose hash the host did not name; it installs the new version beside the current one, runs its self-test (a test capture, and the isolation proof when public websites are on), stops the worker between jobs, switches current, and waits for the new version's first poll, else it rolls back. Each worker's policy in Settings is Automatic (the default) or Notify only. Below the security minimum, or with a browser past its age, screenshots of public websites stay off by themselves; CMS captures continue.

What each job gets

  • A browser of its own (the sidecar's Chromium build, sandbox on) whose every connection goes through the job's egress proxy: the Parhelia origin, the page's head and the host's media origins, plus public addresses; private, loopback, link-local and metadata addresses are refused at the point of connection (each redirect hop included).
  • One cookie: the job-bound capture cookie from the poll answer, on the Parhelia origin. No login, no editor session.
  • The host's controller page and the target page; every call between them is authorized by the worker (the job's own pages, their main frames, the attempt still active).

Screenshots of public websites (public-web)

The agent tool capture-url-screenshot takes screenshots of public pages. Such a page is untrusted and the sidecar usually sits inside the network, so the browser for it runs in a separate browser host process that has no network of its own: on Linux a systemd unit with PrivateNetwork=yes and an empty view of every directory sockets live in, apart from its own socket directory. Every connection it makes goes through a Unix socket to the worker's egress proxy. That proxy resolves each name itself, refuses private, loopback, link-local and metadata addresses, the host's blocked ranges, and the addresses of Parhelia and of this machine, and connects only to the address it checked. A public-web job gets no cookie.

# the worker, offering public-web once the browser host is connected and its isolation is proven
npx @parhelia/capture start --origin https://parhelia.internal.example --key-file /etc/parhelia-capture/worker.key --web
# the browser host, inside its hardened unit (print the unit's properties with `unit-properties`)
npx @parhelia/capture browser-host --socket-dir /run/parhelia-capture/browser-host
# the isolation self-test on its own
npx @parhelia/capture doctor --profile public-web --origin https://parhelia.internal.example --key-file /etc/parhelia-capture/worker.key

The worker offers public-web only after the self-test passes, and repeats it when the browser host reconnects and once an hour. The test first checks that the worker itself can fetch a nonce from Parhelia (another host) and one from its own loopback. With the proxy switched off, the real browser must then reach neither. The browser host's view must hold no socket but its own, D-Bus must be unreachable, and Chromium must run sandboxed. With the proxy on, Parhelia's address must be refused. When Parhelia runs on the same machine, name a probe on another host with --probe-url. On Windows, a sidecar on the machine that runs Parhelia refuses public-web by name, because loopback is not filtered there.

Local development: npm run capture (repository root) runs a worker from source against :5090 with a dev key; scripts/run-scenario.ps1 <scenario> -Capture starts one with the scenario and gives the host the matching key.