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

service-plane

v0.3.0

Published

Ability-first Service Plane primitives for schema-backed Cap'n Web RPC, STS capability tokens, OpenAPI, and MCP projections.

Readme

service-plane

Ability-first service APIs for TypeScript services.

service-plane gives independently deployed services one shared model:

  • Services define schema-backed abilities.
  • The control plane issues short-lived capability tokens.
  • Cap'n Web carries RPC method calls over HTTP-batch, WebSocket, or Cloudflare bindings.
  • Schemas validate inputs and outputs, using the validation library you already use.
  • Published abilities can become OpenAPI or MCP tools from the control plane.
  • Request ids and structured JSON logs correlate plane and service calls out of the box.

Service authors define abilities. Hono stays the HTTP shell for middleware, discovery, and adapter routes.

The library is written against web-standard globals only (crypto.subtle, fetch/Request, TextEncoder, timers) and runs on Node 20+, Cloudflare Workers, Deno, and Bun. That claim is exercised in CI on every push: the full test suite runs on Node 20/22/24 and inside real workerd isolates (via @cloudflare/vitest-pool-workers), and a bundled smoke — HTTP-batch end to end, streaming over a session transport, token verification accepting and rejecting — runs on Deno and Bun.

Install

npm install service-plane hono @hono/capnweb capnweb

Ability schemas come from a validation library you choose; service-plane does not bundle or require any particular one. Add whichever you already use — anything implementing Standard Schema and its Standard JSON Schema companion:

npm install arktype     # or zod, or @vinejs/vine, or valibot + @valibot/to-json-schema

See Choosing A Validation Library for versions and the one wrapper Valibot needs. Code samples in this README and the docs use Zod so they stay concrete — that is an arbitrary choice, not a default.

Minimal Service

import { RpcTarget } from 'capnweb';
import * as z from 'zod';
import {
  ServicePlaneService,
  abilityMethod,
  defineAbility,
  defineCapabilities,
  jwksFromServiceBinding,
} from 'service-plane/service';

type Env = {
  ASANA_CONNECTIONS: DurableObjectNamespace;
  CONTROL_PLANE: Fetcher;
};

const capabilities = defineCapabilities({
  serviceId: 'asana',
  scopes: [{ id: 'asana.tasks.write', title: 'Create Asana tasks' }],
});

const asanaTasks = defineAbility({
  id: 'asana.tasks',
  title: 'Asana Tasks',
  exposure: 'published',
  access: 'plane',
  scopes: ['asana.tasks.write'],
  methods: {
    createTask: abilityMethod({
      input: z.object({
        connectionId: z.string(),
        name: z.string().min(1),
        projectId: z.string(),
      }),
      output: z.object({
        id: z.string(),
        url: z.string().url(),
      }),
      scopes: ['asana.tasks.write'],
      rest: { method: 'post', path: '/asana/tasks', summary: 'Create an Asana task' },
      mcp: { name: 'asana_create_task', description: 'Create a task in Asana' },
    }),
  },
  handler: ({ context, identity }) => new AsanaTasksHandler(context.env, identity),
});

class AsanaTasksHandler extends RpcTarget {
  constructor(
    private readonly env: Env,
    private readonly identity: { serviceId: string },
  ) {
    super();
  }

  async createTask(input: { connectionId: string; name: string; projectId: string }) {
    const id = this.env.ASANA_CONNECTIONS.idFromName(`${this.identity.serviceId}:${input.connectionId}`);
    const connection = this.env.ASANA_CONNECTIONS.get(id);
    return connection.createTask(input);
  }
}

export default new ServicePlaneService<{ Bindings: Env }>({
  id: 'asana',
  title: 'Asana Service',
  version: '0.2.0',
  auth: {
    issuer: 'control-plane',
    jwks: (c) => jwksFromServiceBinding(c.env.CONTROL_PLANE),
  },
  capabilities,
  abilities: [asanaTasks],
});

This service mounts:

GET /.well-known/service-plane/service.json
ALL /rpc/asana.tasks

Minimal Control Plane

import {
  ServicePlaneControlPlane,
  cloudflareServiceBinding,
  hmacServiceClientAuth,
} from 'service-plane/control-plane';

export default new ServicePlaneControlPlane({
  signingKeys: (env) => [{ kid: '2026-07', secret: env.STS_SIGNING_SECRET }],
  authenticateCaller: (c) =>
    hmacServiceClientAuth({
      clients: [{ clientId: 'workflow-runner', secret: c.env.WORKFLOW_RUNNER_SECRET }],
    })(c),
  services: (c) => [
    cloudflareServiceBinding({
      id: 'asana',
      binding: c.env.ASANA,
      grants: [{ caller: 'workflow-runner', scopes: ['asana.tasks.write'] }],
    }),
  ],
});

The control plane mounts:

POST /.well-known/service-plane/capability-token
GET  /.well-known/service-plane/jwks.json
GET  /openapi.json
POST /rpc/mcp                                    (MCP streamable HTTP)

The plane serves the OpenAPI document; to render it, mount a Hono UI extension (e.g. @hono/swagger-ui or @scalar/hono-api-reference) on plane.app pointed at /openapi.json.

For this compact local-development walkthrough, the service above leaves ingress disabled so the caller below can connect directly. Do not use this direct topology as the production boundary. Production services should enable ingress: {} and route ability calls through the control-plane broker; direct non-brokered tokens are then rejected with 403 before handler creation. See Service-Plane Ingress.

Minimal Local Caller

import {
  abilitySession,
  cloudflareServiceBindingRpc,
  controlPlaneHmacTokenRequester,
  type AbilityRpc,
} from 'service-plane/service';

declare const env: {
  ASANA: Fetcher;
  CONTROL_PLANE: Fetcher;
  WORKFLOW_RUNNER_SECRET: string;
};

const asana = await abilitySession<AbilityRpc<typeof asanaTasks>>({
  abilityId: 'asana.tasks',
  callerServiceId: 'workflow-runner',
  targetServiceId: 'asana',
  scopes: ['asana.tasks.write'],
  requestToken: controlPlaneHmacTokenRequester({
    clientId: 'workflow-runner',
    clientSecret: env.WORKFLOW_RUNNER_SECRET,
    controlPlaneUrl: 'https://control-plane.internal',
    fetch: env.CONTROL_PLANE,
  }),
  transport: cloudflareServiceBindingRpc(env.ASANA),
});

await asana.createTask({
  connectionId: 'conn_123',
  name: 'Follow up',
  projectId: 'proj_456',
});

Agent Skill

The repo ships an APM package with a service-plane skill that teaches coding agents the ability model, the security boundaries, and where to find deeper reference material. It is distributed through this Git repo by the APM CLI, independently of npm.

Install the APM CLI once (instructions), then install the skill into a consumer project:

apm install JUVOJustin/service-plane

Or pin it as a dependency so every teammate gets the same version. Minimal apm.yml in the consumer repo:

name: my-project
version: 1.0.0
dependencies:
  apm:
    - JUVOJustin/service-plane
apm install

Either way the skill deploys to your agent's native location (.claude/skills/ for Claude Code, .agents/skills/ for Copilot, Cursor, and others; commit apm.lock.yaml to keep installs reproducible). From there the agent activates it automatically whenever a task touches service-plane code — no prompting needed. The skill source lives in .apm/skills/service-plane/; its references are synced copies of docs/.

Docs