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

@altimist/did-publisher

v0.6.0

Published

Mounts altimist-id's Resolver API on altimist-web's resolver-surface paths (DID document, revocations, team issuer keys). Phase 2a of F-010.

Readme

@altimist/did-publisher

Proxy altimist-id's Resolver API to the public did:web URLs (<handle>.altimist.com/.well-known/did.json, altimist.com/users/<handle>/did.json, altimist.com/.well-known/revocations.json, altimist.com/.well-known/team-issuers/<team>.json). Two consumption shapes:

  • routeResolverRequest() — single fetch handler dispatcher for Cloudflare Workers / Bun.serve / Deno.serve. The recommended path (ADR-012 Option W).
  • didJsonHandler / revocationsHandler / teamIssuersHandler — Next.js App Router route-handler factories. v0.1 API; retained for any consumer that wants to mount per-route handlers under file-based routing.

Decision rationale in ADR-011 (original placement on altimist-web, never deployed) and ADR-012 (Cloudflare Worker placement, current direction).

Install

npm install @altimist/did-publisher

Usage — Cloudflare Workers (recommended)

// src/index.ts
import { routeResolverRequest } from '@altimist/did-publisher';

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const response = await routeResolverRequest(request, {
      origin: env.ALTIMIST_ID_ORIGIN,    // e.g. "https://altimist.id"
      apex: env.ALTIMIST_ID_APEX,        // "altimist.com" / "staging.altimist.com"
      payProxySecret: env.FINTERNET_PAY_PROXY_SECRET, // optional (F-024); omit to fall back to platform IP
      trustCloudflareHeaders: true, // ONLY when this genuinely IS a CF Worker behind Cloudflare (see below)
    });
    return response ?? new Response('Not Found', { status: 404 });
  },
};

routeResolverRequest() returns:

  • A Response for the resolver-surface URLs (see the route table below).
  • null for everything else — the caller decides what to do (404, fall through to another origin, etc).

Usage — Next.js App Router (v0.1 API)

In a Next.js project that owns *.altimist.com, mount three route handlers:

// app/.well-known/did.json/route.ts
import { didJsonHandler } from '@altimist/did-publisher';

export const GET = didJsonHandler({
  origin: process.env.ALTIMIST_ID_URL!,        // e.g. "https://altimist.id"
  apex: process.env.RESOLVER_APEX || 'altimist.com',
});
// app/.well-known/revocations.json/route.ts
import { revocationsHandler } from '@altimist/did-publisher';

export const GET = revocationsHandler({
  origin: process.env.ALTIMIST_ID_URL!,
});
// app/.well-known/team-issuers/[team]/route.ts
import { teamIssuersHandler } from '@altimist/did-publisher';

export const GET = teamIssuersHandler({
  origin: process.env.ALTIMIST_ID_URL!,
});

What each route does

| Path | Behaviour | Cache | CORS | |---|---|---|---| | <handle>.<apex>/.well-known/did.json | Proxies ${origin}/api/resolver/did/<handle> | altimist-id's, forwarded (measured 2026-09-07: public, max-age=0, s-maxage=60, must-revalidate) | altimist-id's, forwarded | | <apex>/users/<handle>/did.json | Proxies ${origin}/api/resolver/did/<handle>?form=path (F-011 path-form DID hosting) | altimist-id's, forwarded | altimist-id's, forwarded | | <apex>/.well-known/revocations.json | Proxies ${origin}/api/resolver/revocations | altimist-id's, forwarded (measured 2026-09-07: public, max-age=0, s-maxage=30, must-revalidate) | altimist-id's, forwarded | | <apex>/.well-known/team-issuers/<team>.json | Proxies ${origin}/api/resolver/team-issuers/<team> | altimist-id's, forwarded (measured 2026-09-07: public, max-age=0, s-maxage=300, must-revalidate) | altimist-id's, forwarded | | <handle>.<apex>/finternet-pay/v1/descriptors (POST) | Proxies ${origin}/api/pay/<handle>/descriptors — the altimist-id F-024 signed payment-descriptor endpoint. Body forwarded verbatim; x-finternet-pay-proxy + CF-Connecting-IP forwarded only when both payProxySecret and trustCloudflareHeaders are set (and the request carries CF-Connecting-IP). Non-POST → 405 locally (never proxied) | no-store (never cached — money path, at any status, including guards/405) | none — see below | | <apex>/users/<handle>/finternet-pay/v1/descriptors (POST) | Path-form of the above (mirrors F-011 path-form DID hosting) | no-store | none |

trustCloudflareHeaders — read before setting. CF-Connecting-IP is only authentic when Cloudflare's own edge terminates the connection: CF strips or overwrites any client-supplied value before the Worker sees it. Set trustCloudflareHeaders: true only in a deployment that is provably a genuine Cloudflare Worker sitting directly behind Cloudflare's network (like altimist-com-router). Never set it in a Bun/Deno (or any) self-hosted deployment reachable directly from the public internet — there an attacker can forge CF-Connecting-IP, and with a matching payProxySecret the proxy would forward that forged IP as genuine, which altimist-id would then trust. Left unset (the default) the header pair is never forwarded and altimist-id safely falls back to its platform-observed IP.

<apex> is configured per environment (altimist.com for production, staging.altimist.com for staging). Handle URLs require a single-label subdomain (patrick.altimist.com); apex paths require the host to be exactly the apex (returns 404 otherwise).

Cache policy is altimist-id's, and is forwarded rather than restated

This package sets no cache policy of its own (v0.5.0). Every resolver response carries Cache-Control verbatim from altimist-id, whatever that is at the time; the "currently" values in the table above are altimist-id's, quoted for orientation, not a contract this package enforces. The one exception is the FinternetPay money path, which is unconditionally no-store at any status.

It was not always so, and the reason is worth keeping. Up to v0.4.0 this package held its own copies of the three cache strings. altimist-id then removed stale-while-revalidate=86400 from did.json — cache purge is a confirmed no-op in production, so swr let the CDN serve a revoked device key to a third-party verifier for up to 24 hours — shipped it, and verified it on its own route. The header a verifier actually received never changed, because DID_CACHE here overrode it. A second copy of a policy is drift by construction; forwarding removes the class rather than the instance.

This did not by itself close the revocation-staleness hole, and altimist-id has since closed it. When v0.5.0 shipped, altimist-id still sent stale-while-revalidate=86400 on revocations.json, which does not deliver the 60s upper bound on revocation propagation F-010's non-functional AC states when the origin is degraded — and because the old constant here held the same string, forwarding changed nothing on that route. It became a one-line change in altimist-id needing no publish here, which was the point, and that change has landed: measured 2026-09-07, none of the three read routes sends stale-while-revalidate at all.

Two consequences worth knowing:

  • A Cache-Control is forwarded at any status, not only 200. altimist-id answers an unknown handle with an explicit no-store, and dropping that is worse than passing it on.
  • Responses this package generates itself carry no cache header at all: the no-handle and wrong-host guards, and the synthesised 503 below.

CORS policy is altimist-id's too, and is forwarded the same way

This package sets no CORS policy of its own (v0.6.0). Every proxied resolver response carries upstream's Access-Control-* headers verbatim — today access-control-allow-origin: *, access-control-allow-methods: GET, HEAD, OPTIONS and access-control-max-age: 86400.

This is the second half of the same row-51 defect, and it was live in production until v0.6.0. Measured 2026-09-07:

https://altimist.id/api/resolver/did/rich       200, access-control-allow-origin: *
https://rich.altimist.com/.well-known/did.json  200, no ACAO, x-alt-cache: miss

x-alt-cache: miss proves the response was built fresh, so it was neither a stale cache nor deploy lag. The response builder rebuilt the Response from scratch with a two-header allowlist — Content-Type plus Cache-Control — and dropped every other upstream header silently. altimist-id had been sending the CORS headers all along and they never reached a caller, so no browser on any other origin could resolve an Altimist DID: a cross-origin fetch without an Access-Control-Allow-Origin is unreadable, and did:web resolution is inherently cross-origin.

The rules mirror the cache-policy ones exactly:

  • Collected by prefix (access-control-), not by a list of header names. A named list is what caused the bug; a second one would only move it — altimist-id adding Access-Control-Expose-Headers would arrive as another silent drop.
  • Forwarded unvalidated, including a policy this package would consider wrong. Filtering or correcting upstream's CORS decisions would be a second opinion about them, which is the class row 51 is about.
  • Nothing is invented. In particular there is no Access-Control-Allow-Credentials: it is illegal per the Fetch standard alongside the * altimist-id sends, and a browser rejects such a response rather than relaxing.
  • Forwarded at any status, so a browser can read altimist-id's own 404.
  • Responses this package generates itself carry no CORS header, for the same reason they carry no cache header — there is no upstream response to forward one from. So a cross-origin caller asking a host that carries no handle at all (the apex, a multi-label host) sees a CORS failure rather than a readable 404.
  • Nothing but the access-control- family rides along. altimist-id is a Next.js app and sends a Vary naming four RSC-internal headers, plus X-Powered-By; neither means anything on a did:web document.
  • The money path gets no CORS headers at any status. altimist-id sends it none, it is a POST needing a preflight this package does not answer, and granting a cross-origin read of a payment descriptor is altimist-id's decision to make, not a proxy's to infer.

Known gap, in altimist-id rather than here. Measured 2026-09-07, altimist-id sends no Access-Control-* on its resolver 404s (/api/resolver/did/<unknown> answers 404 with Cache-Control and nothing else). Forwarding faithfully means a cross-origin browser resolving an unknown handle still sees a CORS failure instead of a readable {"error":"unknown handle"}. Closing that is a change to altimist-id's resolver routes; this package will forward it the day it ships, with no publish needed here.

Failure modes

  • Upstream 4xx (404 unknown handle, etc.): pass through with the same status and upstream's Cache-Control and Access-Control-* if it sent them.
  • Upstream 3xx: returns 503. Redirects are never followed — "upstream" means the configured origin and nothing else, because since v0.5.0 upstream also chooses the cache policy. (redirect: "manual" plus a status check, not redirect: "error", which workerd refuses.)
  • Upstream 5xx or timeout: returns 503 so consumer apps fail-closed (DID resolution unavailable). No cache header and no CORS header — a fail-closed response this package invented carries no policy it invented.
  • A body that never fully arrives: returns 503. A partial read is a transport failure, never a 200 {}.

Cloudflare cache purge

altimist-id is responsible for purging the edge cache on every mutation (device enrolment, revocation, etc.). This package is read-only — see altimist-id's src/lib/cache-purge.ts for the purge side.

License

UNLICENSED — internal Altimist.