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

keyquill-relay

v3.6.0

Published

Zero-knowledge E2E encrypted WebSocket relay for pairing a browser with a mobile LLM wallet. Ships a browser client, a Cloudflare Durable Object, and a Hono route factory.

Readme

keyquill-relay

Zero-knowledge E2E-encrypted WebSocket relay for pairing a PC browser with a mobile LLM wallet.

Note: payload shape tracks keyquill-mobile, not the extension broker

RelayInnerRequest currently carries { provider, messages, systemPrompt, maxTokens } — the same wire shape the keyquill-mobile plugin accepts, not the capability-first shape used by [email protected] and [email protected]+. That's intentional for now: relay is a pure transport whose payload is defined by the mobile endpoint it talks to. When mobile migrates to the broker architecture (see the note in the mobile README), this relay's RelayInnerRequest and RelayInnerResponse will follow. Until then, relay operates as-is.

When the mobile app holds the LLM API keys (via keyquill-mobile) and the user wants to use those keys from a PC browser, keyquill-relay provides a secure bridge:

 PC browser                Relay server              Phone
┌───────────┐              ┌──────────┐             ┌──────────────┐
│           │  QR pairing  │          │             │              │
│  client   │─────────────>│   DO     │<───────────>│  native app  │
│ (browser) │              │(ciphertext│  WebSocket │ (Keychain/KS)│
│           │◄─────────────│  only)   │             │              │
└───────────┘   WebSocket  └──────────┘             └──────────────┘
     │                                                      │
     └────────── ECDH P-256 + HKDF + AES-GCM ────────────────┘
                 (server never sees plaintext)

The relay server sees only ciphertext. Pairing token → single-use, 5-minute expiry.

Install

pnpm add keyquill-relay
# or, if you're using it in a Cloudflare Worker:
pnpm add keyquill-relay hono

Package layout

| Entry | Use | |---|---| | keyquill-relay/client | Browser-side: PhoneRelayClient and QR helpers | | keyquill-relay/server | Server-side: RelaySessionDO (Cloudflare Durable Object) + createRelayRoutes (Hono factory) | | keyquill-relay (root) | Wire protocol types only (no runtime code) |

Server usage (Cloudflare Workers + Hono)

import { Hono } from "hono";
import { createRelayRoutes } from "keyquill-relay/server";
export { RelaySessionDO } from "keyquill-relay/server";

type Env = {
  RELAY_SESSIONS: DurableObjectNamespace;
  ALLOWED_ORIGINS?: string;
};

const app = new Hono<{ Bindings: Env }>();

app.route(
  "/relay",
  createRelayRoutes<Env>({
    getDurableObject: (env) => env.RELAY_SESSIONS,
    allowedWsOrigins: (env) => [
      "https://your-app.example.com",
      ...(env.ALLOWED_ORIGINS ?? "").split(",").filter(Boolean),
    ],
  }),
);

export default app;

wrangler.jsonc:

{
  "durable_objects": {
    "bindings": [{ "name": "RELAY_SESSIONS", "class_name": "RelaySessionDO" }]
  },
  "migrations": [{ "tag": "v1", "new_classes": ["RelaySessionDO"] }]
}

Client usage (browser)

import { PhoneRelayClient } from "keyquill-relay/client";

const client = new PhoneRelayClient({
  relayUrl: "wss://your-relay.example.com/relay",
});

// 1. Generate pairing QR
const { pairingToken, qrPayload } = await client.createPairing();
// ... render qrPayload as QR code ...

// 2. After the phone scans and accepts:
await client.waitForPaired(pairingToken);

// 3. Send an AI request (E2E encrypted end to end)
const response = await client.sendRequest({
  provider: "openai",
  messages: [{ role: "user", content: "Hello" }],
});

Protocol

  • Key exchange: ECDH P-256 between browser and phone.
  • Key derivation: HKDF-SHA-256 with a protocol-specific info label (keyquill-relay-v1 by default; override per app if desired).
  • Symmetric: AES-GCM-256, nonce per message.
  • Pairing token: 16 bytes of CSPRNG-backed entropy, single use, 5-minute TTL.
  • Session: 30-minute idle timeout (configurable server-side).

All crypto uses Web Crypto only (no Node-specific APIs) — runs in browsers, Cloudflare Workers, and Deno.

Security properties

  1. Relay server sees ciphertext only.
  2. Pairing token is single-use and short-lived.
  3. Session key is ephemeral — lost when either peer disconnects.
  4. Replay protection via AES-GCM nonce + sequence numbering.

License

MIT