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

@pharoshub/guard

v1.1.0

Published

Seal your published site so a changed page cannot pretend to be yours — plus honeypot traps and honeytokens.

Readme

@pharoshub/guard

Two things, inside the application they protect.

Hallmark seals your published pages so a page altered after it left your build cannot pass as genuine. Deception puts traps in your own app that fire the moment someone probes you.

Neither blocks, redirects, challenges, or slows a visitor. Blocking is your edge provider's job. This is the part that tells you something is happening while it is still happening.

Hallmark — sealing what you publish

Every common web control assumes the attacker is outside. Once a hosting account, build server or CDN is compromised, a firewall sees legitimate requests and a bot filter sees a normal visitor. The realistic attack then is not defacement but one altered line — a phone number replaced, a payment form inserted, an account number changed by two digits.

Add it to your build, after your site is generated:

import { hallmark } from "@pharoshub/guard/hallmark";

await hallmark({ dir: "dist" });

That writes two files into your output:

  • _integrity.json — the signed fingerprint list
  • integrity.js — the browser verifier

Then load the verifier on your pages:

<script src="/integrity.js" defer></script>

Only hashes leave your infrastructure. Your build hashes its own output and sends a list of fingerprints; the files never reach us.

The signing key is not yours to hold, and not ours to leak onto your servers. It is never placed on your origin, your build system, your CDN, or in our database. Someone who takes over all of it can change whatever they like — they cannot produce a signature that verifies. Verification uses a public key that can only check, so anyone can audit a page independently.

It reports; it does not block. A mismatch is usually innocent — a corporate proxy rewriting HTML, antivirus, an ISP banner, or a deploy landing while a visitor had the old page open. What distinguishes an attack is scale: one visitor is their network, thousands across unrelated networks is the file.

It fails open. No keys, no network, our service down, subscription lapsed — the build prints a warning and finishes. A security feature that can stop you deploying is a worse problem than the one it solves.

Known limits

  • The verifier is served by the origin it checks, so an attacker with complete control of that origin can remove it. It catches compromises that alter content without auditing every script.
  • It checks the HTML document and same-origin scripts and stylesheets. Third- party scripts are not sealed — they are not yours to seal.
  • It detects change; it does not prevent it.

Deception — traps in your own app

Install

npm install @pharoshub/guard
PHAROS_SITE_KEY=...
PHAROS_SECRET=...

Both come from the console when you add a site. The secret signs your reports; treat it like a database password.

Express

import { createGuard } from "@pharoshub/guard";

const guard = createGuard({
  siteKey: process.env.PHAROS_SITE_KEY,
  secret: process.env.PHAROS_SECRET,
});

app.use(guard.middleware()); // mount first, before your own routes

Next.js

// middleware.ts
import { createNextGuard } from "@pharoshub/guard/next";

const guard = createNextGuard();

export async function middleware(request: Request) {
  return (await guard(request)) ?? NextResponse.next();
}

export const config = { matcher: "/:path*" };

The clone beacon

Put the tag in your document head. It reports the origin the page was actually served from — if that is not your domain, someone has published a copy, and for a hotel that copy is usually taking bookings under your name.

<script src={`https://www.pharoshub.cloud/b/${siteKey}.js`} async defer />

If you run a Content-Security-Policy with a nonce, pass it: guard.beaconTag(nonce).

Honeytokens

const token = guard.honeytoken("stripe");

Returns a credential that looks real and is never valid. Put it where an attacker will only find it by looking where they should not — a stale config file, an HTML comment, a decoy admin page. Nobody legitimate has it, so its use is not a signal to weigh. It is proof.

What it sends

Only requests that were already suspicious. A normal request never reaches the reporting path.

| Field | Example | | --- | --- | | path | /.env | | method | GET | | source_ip | 203.0.113.9 | | user_agent | python-requests/2.31 | | referer | — |

Request bodies are not captured unless you pass captureBodies: true, and even then only for trap routes. A body on your real routes contains your guests' details, and those are yours, not ours.

Failure

Every path fails open, by design:

  • missing key or secret → the guard becomes a no-op, and says so once at startup
  • collector unreachable → events queue in memory (500 max, newest kept) and retry
  • anything throws → the request continues as if the guard were not installed

The worst outcome of a broken guard is missing telemetry. It is never a broken site.

Tuning traps

createGuard({ traps: [...DEFAULT_TRAPS, "/staff-portal", "/backup-2019.zip"] });

The best traps are the ones specific to you — a path that looks like it belongs to your stack, that no link points at. Your plan sets how many you may run.