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

@metorial-outpost/proxy

v0.1.1

Published

An adapter-based proxy runtime for Outpost instances, exposed as a single [Hono](https://hono.dev) app. Every adapter runs on the same port; each is either a plain Hono app (a nested service) or a proxy adapter that forwards requests upstream via [`@metor

Downloads

24

Readme

@metorial/outpost-proxy

An adapter-based proxy runtime for Outpost instances, exposed as a single Hono app. Every adapter runs on the same port; each is either a plain Hono app (a nested service) or a proxy adapter that forwards requests upstream via @metorial/outpost-fetch, signed per the Metorial Outpost Signature Protocol.

Installation

npm install @metorial/outpost-proxy
yarn add @metorial/outpost-proxy
bun add @metorial/outpost-proxy
pnpm add @metorial/outpost-proxy

Usage

import { OutpostAuth } from '@metorial-outpost/auth';
import { createOutpostFetch } from '@metorial-outpost/fetch';
import { createOutpostProxy, createProxyAdapter } from '@metorial-outpost/proxy';
import { Hono } from 'hono';

let auth = new OutpostAuth({ credential: myOutpostCredential, defaultService: 'metorial.proxy' });
let outpostFetch = createOutpostFetch({ auth });

let testApp = new Hono();
testApp.get('/hello', c => c.text('handled locally'));

let proxy = createOutpostProxy({
  adapters: [
    { path: '/test', app: testApp },
    createProxyAdapter({ path: '/abc', fetch: outpostFetch, target: 'https://parent.example.com' }),
    createProxyAdapter({ path: '/', fetch: outpostFetch, target: 'https://parent.example.com' })
  ]
});

export default { fetch: proxy.fetch, port: 8080 };

createOutpostProxy mounts every adapter's app onto one shared Hono instance with .route(), sorted from most to least specific path -- a / adapter, if present, always matches last so it can act as a catch-all fallback.

Health checks

Every proxy always answers GET /ping (pong, text/plain) and GET /healthz ({"status":"ok"}), regardless of which adapters are mounted -- the common paths load balancers, orchestrators, and uptime checks probe by convention. These are exact routes, so they always win over a / adapter's own catch-all, no matter the registration order.

Adapters

An OutpostProxyAdapter is just { path, app }. Since a Hono app's .fetch is already a WinterCG-compatible (Request) => Promise<Response>, a nested Hono service needs no special treatment -- hand in your own Hono instance directly, as testApp above does.

For a proxy adapter, use createProxyAdapter:

createProxyAdapter({
  path: '/abc',
  fetch: outpostFetch,
  target: 'https://parent.example.com',
  service: 'metorial.proxy',
  rewritePath: (path, c) => path,
  trustProxy: false,
  proxyContext: c => ({ ip: '...', user_agent: '...' })
});

Request bodies are buffered in full before forwarding -- signing a request means hashing its whole body, so true streaming pass-through isn't supported, matching outpost-fetch's own body contract. Hop-by-hop headers (Host, Connection, Content-Length, Transfer-Encoding) are stripped; everything else, including Authorization, is forwarded and signed as-is.

Client IP (trustProxy)

Every proxied request signs a proxy_context (see @metorial/outpost-signature) carrying the caller's IP and User-Agent.

By default (trustProxy unset or false), the IP is not read from X-Forwarded-For or similar headers -- those are trivially spoofable by the caller. Instead it's read from the actual TCP socket, via whichever runtime is serving the app:

  • Bun -- server.requestIP(), which needs the app served with Bun.serve({ fetch: app.fetch }) (Bun passes its Server as the 2nd argument to fetch, which Hono forwards as c.env).
  • Node -- the raw http.IncomingMessage's socket, which needs the app served with @hono/node-server's serve(app) (it passes { incoming, outgoing } as c.env).

If neither is detected, the IP is simply omitted rather than trusting an unverified header. (This duck-types c.env locally instead of importing hono/bun/@hono/node-server -- hono/bun's barrel eagerly touches the global Bun at import time via an unrelated SSG helper, which breaks under non-Bun test runners such as vitest-under-Node.)

Set trustProxy: true (or { ipHeader: '...' }, default header X-Forwarded-For) only when requests genuinely pass through a trusted upstream proxy that sets or strips that header itself -- otherwise a client can claim any IP it likes.

Nested Outposts (guardNestedOutpostAccess)

An Outpost placed behind another (spec §59) is a fully stand-alone Outpost with its own credentials and Instances -- nesting is a network-routing fact, not a distinct signing mechanism. guardNestedOutpostAccess gates a proxy adapter's traffic: it verifies an incoming request exactly as any other Outpost request (@metorial/outpost-server's verifyOutpostRequest, spec §42), and if the signed outpost_id differs from this Outpost's own, checks that the nested Outpost's capability manifest access is equal to or narrower than this Outpost's own (spec §61.2) before letting it through. A request with no Metorial-Outpost-Signature header at all (a plain client, not a relayed one) is left untouched -- there is nothing nested to check.

import { decodeCredentialEnvelope } from '@metorial-outpost/credential-envelope';
import {
  createOutpostManifestResolver,
  createProxyAdapter,
  guardNestedOutpostAccess
} from '@metorial-outpost/proxy';

let credential = decodeCredentialEnvelope(myOutpostCredential);

let resolveOutpostManifest = createOutpostManifestResolver({
  endpoint: credential.endpoint,
  basePath: '/outpost',
  service: 'metorial.outpost',
  fetch: context.fetch,
  cache: context.cache
});

let app = new Hono();
app.use(
  '/*',
  guardNestedOutpostAccess({
    tokens: context.tokens,
    service: 'metorial.proxy',
    selfOutpostId: credential.outpost_id,
    selfManifest: context.manifest,
    resolveOutpostManifest
  })
);
app.route(
  '/',
  createProxyAdapter({ path: '/', fetch: outpostFetch, target: 'https://parent.example.com' }).app
);

Wire it ahead of a proxy adapter's own route, on whichever adapter handles ordinary application traffic that a nested Outpost's Instances might send. guardNestedOutpostAccess also populates c.set('outpostAuth', ...) once it authenticates a request, which createProxyAdapter then reads to carry the original client's proxy_context and this request's outpost_chain through to the next hop, instead of re-deriving proxy_context from this hop's immediate TCP peer (which for a relayed request is just the previous Outpost, not the real original client).

What this package does not do

  • Resolve manifests or check authorization state against a real database. resolveOutpostManifest and the optional resolver passed to guardNestedOutpostAccess are exactly @metorial/outpost-server's own hooks -- see that package for what they resolve against.

License

This project is licensed under the Apache License 2.0.