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

@substrat-run/dev-issuer

v0.1.6

Published

A local OpenID Connect provider whose only shortcut is that you pick a user instead of typing a password. Dev only — never deployed, never a dependency of a vertical's runtime.

Readme

@substrat-run/dev-issuer

A local OpenID Connect provider you sign into by picking a name.

It exists so a Substrat vertical needs no dev-only auth branch. Point a vertical at this issuer and it is an ordinary relying party running its production login; point it at Auth0, Keycloak or your own issuer instead and nothing in the vertical changes but OIDC_ISSUER.

Full documentation: https://substrat.net/reference/dev-issuer

Never deploy it. Its signing key is checked into this repository, deliberately — see Why the key is public below. Nothing but a loopback relying party may trust it.

Why it exists

The obvious way to move between users locally is a header — x-principal: greta — and it is a bad trade twice over. It is an impersonation bypass one environment variable away from being live, and it forks the auth path: the login you exercise all day is one no deployment runs, so the real one is only ever tested in production. Moving the user-picker out of the vertical and into an issuer removes both. The vertical keeps exactly one auth path.

What it is

  • A real provider. Discovery, JWKS, Authorization Code + PKCE, a signed ID token with nonce, RP-initiated logout. The single shortcut is that /authorize renders a list of people instead of a password field.
  • Stateless. The authorization code is a short-lived JWT carrying subject, client, redirect URI, nonce and PKCE challenge — no code store, no session store. Two consequences: restarting invalidates nothing, and with no SSO cookie the picker appears on every /authorize, which is what makes switching user one click rather than a logout dance.
  • Loopback-only redirects. Anything but http://localhost/127.0.0.1/[::1] is refused unless explicitly allowed, so a stray instance cannot be used as an open redirector.
  • Web-standard. jose + Web Crypto, no node:* in the issuer itself.

Running it

substrat-dev-issuer --personas src/personas.ts    # defaults to :8879, or $ISSUER_PORT

--personas names a module exporting PERSONAS: DevPersona[]. Point it at the vertical's own file so the picker's cast and the identity links the seed writes are the same array:

import type { DevPersona } from '@substrat-run/dev-issuer';

export const PERSONAS: DevPersona[] = [
  { sub: 'dev|greta', name: 'Greta', email: '[email protected]', note: 'workshop-admin' },
  { sub: 'dev|mans',  name: 'Måns',  email: '[email protected]',  note: 'mechanic' },
];

sub is the join. The issuer asserts it; your seed binds it to a principal with linkIdentity. Keep the values stable and readable — they end up in a directory that outlives a restart.

The relying-party half

devLogin is the other side, for a vertical's dev server — the two steps a hosted deployment also performs, written once rather than per demo:

import { devLogin } from '@substrat-run/dev-issuer';

const login = devLogin({ directory: host.admin, actor: staff, provider: 'oidc:dev-issuer' });

app.on(['GET', 'POST'], '/api/auth/*', (c) => login.handle(c.req.raw)); // login/callback/logout
const caller = await login.caller(req.headers);   // { principal, tenantId, scopeId, … } | null

caller() verifies the request (session cookie, or a bearer for a script) and resolves the sub through your identity directory. subject() returns the issuer's answer unresolved, for verticals that know which tenant to ask about themselves.

Without a browser

Tests and headless scripts mint tokens directly:

curl -XPOST localhost:8879/dev/token -d '{"sub":"dev|greta"}'   # → id_token + access_token

This is impersonation, and its address is the point: a process bound to localhost that is never deployed, rather than a flag inside the thing that ships.

Why the key is public

A relying party verifies an ID token against the issuer's JWKS, so the issuer needs a keypair. Generating one at boot would invalidate every session on every restart, and every /dev/token bearer a test run is holding. So the key is fixed — and fixed means public, since it lives in this repository and anyone can mint a token it will validate.

That is the right posture for a process that binds to localhost, hands out logins by clicking a name, and is never deployed. It is also exactly why no production relying party may point at it: the trust anchor is a file in a public repo.

Status

Pre-release (0.x): interfaces change without notice. hono is a peer dependency.