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

@ekka-ai/shelves

v0.3.0

Published

Serve an EKKA shelf from your Fastify API: scaffold a typed shelf, mount the shelf protocol v1, and check it the way EKKA will

Readme

@ekka-ai/shelves

Serve an EKKA shelf from your own Fastify API.

A shelf is a small, typed, read-only view of your data, for example hr.salary: one person's pay for one month. An EKKA customer's AI plans read a shelf through their own EKKA Enclave. It calls your API with a key you issued, asks for exactly the records the plan is allowed to read, and checks every record it gets back. This package gives your API the three endpoints that make that work, under /ekka/shelves/v1/ (ping, manifest, invoke). It also gives you a CLI that scaffolds the shelf and then checks your API the way EKKA will.

Your code stays yours: you write the lookup from your data to the shelf's fields, and your auth decides who may call.

Use it

You need Node 22 or later, a Fastify 5 service, and TypeScript run through tsx. The example adds hr.salary (EKKA's predefined salary shelf) to a service that already has its own bearer-token auth.

0. Install

npm i @ekka-ai/shelves
npm i -D tsx            # the CLI loads your TypeScript shelf files through it

Run every command below from the service's root, with npx ekka-shelves … (the copy you just installed).

1. Scaffold the shelf

npx ekka-shelves add hr.salary      # a predefined shelf: its keys and fields are EKKA's
npx ekka-shelves add communications.email \
    --keys id:string:unique --fields id:string,subject:string,sent_on:date --verbs read   # a private shelf of your own

add writes three files under src/ekka/:

  • hr.salary.ts: a class with empty typed methods and its fixtures;
  • policy.ts: an empty role per verb;
  • index.ts: your provider and the list of shelves.

2. Fill in the three files

  • index.ts › provider.id: how EKKA names your service, in lowercase letters, digits and hyphens (for example acme-payroll). Keep offerVersion as it is until you change a shelf; then bump it.
  • policy.ts: a ROLE for each verb, one lowercase word (letters, digits, _), for example payroll_reader. A role is the name for "who may read this shelf". Your customer's EKKA admin maps it to their own roles when they import your service. An empty role refuses to start: it is a person's decision, never a default.
  • hr.salary.ts › read and list: your own code, from your own data to the shelf's fields. That code IS the mapping, and EKKA keeps no mapping table.
  • The fixtures in hr.salary.ts are the ONE record that check and EKKA's import probe ask for:
    • The record must EXIST in your data. It must be a SYNTHETIC person, never a real one. The scaffold names { id: 'fixture-id', month: '2000-01' }. Add that made-up row to every environment EKKA imports from, production included if EKKA imports from production (use currency XTS, the ISO code reserved for testing), or point both fixtures at a synthetic record you already have.
    • The list fixture names the SAME record by every unique key. Never {}: it would match every row, and the probe would carry real records to EKKA. Startup refuses it, and check fails if listing the fixture returns any other record.

3. Mount the plugin with YOUR auth

import { ekkaShelves } from '@ekka-ai/shelves';
import { provider, shelves } from './ekka/index.js';

await app.register(ekkaShelves({
  provider,
  shelves,
  authenticate: requireUser,                       // your existing bearer check: sets who the caller is, or throws 401
  authorize: (capability) => async (req) => {      // your capability guard: throw 403 if the caller lacks it
    if (!req.user?.scopes.includes(capability)) throw Object.assign(new Error('forbidden'), { statusCode: 403 });
  },
  capabilitiesOf: (req) => req.user?.scopes ?? [], // the capabilities the caller holds
  principalOf: (req) => req.user?.id ?? '',        // the caller's principal name
}));

The plugin never decides who a caller is; your hooks do.

4. One key per shelf: add the shelf's principal to your auth

npx ekka-shelves generate    # writes src/ekka/shelves.generated.ts; `generate --check` fails in CI when it is stale

For each shelf, your auth needs:

  • ONE principal, named ekka-shelf-<shelf id> (here ekka-shelf-hr.salary);
  • holding exactly ONE capability, shelf.<shelf id>.read (here shelf.hr.salary.read);
  • with its OWN key: a random secret of 32 bytes or more that your auth accepts as a bearer token, and nothing else.

shelves.generated.ts lists these names (SHELF_PRINCIPAL_CAPABILITIES), so your auth table can import them instead of typing them. Keep the key where you keep your other service secrets. When your customer's admin imports your service into EKKA (ekka shelf import <your https origin>), they paste that key at a hidden prompt. EKKA keeps it in the customer's vault and presents it only to your origin.

5. check: see what EKKA will see

check calls your REAL app in-process. It needs one small file that you write, src/ekka/check-app.ts:

// src/ekka/check-app.ts: your app, built as production builds it, and a key for each shelf's principal.
import { randomBytes } from 'node:crypto';

export default async () => {
  const key = randomBytes(32).toString('base64url');
  // Make YOUR auth accept `key` as the principal ekka-shelf-hr.salary with capability shelf.hr.salary.read.
  // For a token table read from the environment, set it BEFORE importing your app:
  process.env.API_TOKENS = JSON.stringify({ [key]: { id: 'ekka-shelf-hr.salary', scopes: ['shelf.hr.salary.read'] } });
  const { buildApp } = await import('../app.js');
  const app = await buildApp();
  await app.ready();
  return { app, bearerFor: (_shelfId: string) => key };
};
npx ekka-shelves check       # every line ok, or the line that is not, with what to change

6. Go live: how EKKA reaches your service

  • Deploy as usual. The shelf endpoints are served by your app, on your origin.

  • The origin EKKA is given must be a bare https origin, like https://payroll-api.example.com: no path, no query, no user info. EKKA follows no redirects, and it caps the size of an answer.

  • A public address works as it is. A private address (on the customer's own network) works only when the customer's admin imports with --reach private. localhost and 127.0.0.1 are never reachable: the import refuses them on purpose. To try it on your laptop, use check (step 5).

  • The customer's EKKA admin imports your service with ekka shelf import https://your-origin. The command:

    1. calls ping;
    2. shows the origin for them to confirm;
    3. asks for each shelf's key (step 4) at a hidden prompt;
    4. reads your manifest;
    5. probes each shelf once with your fixture, and once with a key you do not declare, which must be refused.

    They then map your roles to their own roles.

  • Change a shelf later (a role, a fixture, a field)? Bump offerVersion in src/ekka/index.ts. EKKA refuses a changed offer under the same version.

Real or synthetic data (the whole store). If ALL of a shelf's rows are generated (a staging or demo store), say so in its class: override readonly data = 'synthetic' as const. The customer's admin then sees "synthetic" beside the shelf, and every signed receipt says so. Leave it unset for a system of record, even when you have added one synthetic fixture person to it: data is about the store, and the fixture is always synthetic.

A shelf of your own (a private shelf)

When no EKKA shelf fits, declare your own shape:

npx ekka-shelves add acme.expense \
  --keys id:integer:unique,employee_id:string \
  --fields id:integer,employee_id:string,spent_on:date,currency:currency_code,amount_minor:money_minor,approved:boolean \
  --verbs read,list
  • Read keys: read by an identifier, never by a person's name: two people can share a name.
  • Money: an integer in the currency's minor unit (money_minor), always with a currency_code. There are no floats anywhere.
  • Types: string, integer, boolean, month (YYYY-MM), date (YYYY-MM-DD), currency_code (ISO 4217) and money_minor.
  • Id: a private id may not reuse one of EKKA's (hr.salary is EKKA's).

EKKA's own reference provider is a service like this. It serves hr.salary and a private reference.expense (an integer id key, money in minor units), both from synthetic data. Its fixtures are { id: 'fixture-0001', month: '2000-01' } and { id: 1 }, and check passes all 33 checks on it.

What the plugin guarantees

  • read takes EXACTLY the shelf's unique keys. list takes any of its declared keys. A key the shelf does not declare is refused (unknown_key), never ignored. list returns at most the page size asked for, never more than the shelf's cap, and a next_cursor.
  • Every record leaving your service is checked against the shelf's exact fields and types. A bad one becomes a provider_error, and neither its values nor your error's details leave your service.
  • manifest needs a key, and it lists only that key's shelves. A shelf that key does not serve, and a shelf that does not exist, get the same answer. Only ping is public.
  • Startup refuses a shelf with no role, no fixture, or a list fixture that could match real records.

Versions and license

  • 0.2.2 is the first published version.
  • Proprietary: use is permitted for EKKA customers under their EKKA agreement; no redistribution (LICENSE).