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

@nolag/core

v0.4.0

Published

The authorization domain of NoLag, as a library

Readme

nolag-core

The authorization and configuration core of NoLag, under Apache 2.0.

nolag-core answers one question: may this actor touch this topic?

It is a library, not a service. It owns projects, apps, rooms, lobbies, access scopes, actors, tokens and signing keys, and it exposes facades that answer what a broker needs in order to accept a connection and route a message. Nothing else.

There is no transport here, and no opinion about who is calling. Mounting it behind authentication is the host's job: NoLag mounts these facades behind its own, and the example host in this repository mounts them behind none. What core does keep is the authentication of actors, which is the product: whether an access token is real, whether a client token was signed by the right key, and what either may reach.

Status: early development. Nothing is released yet, the schema and HTTP contract will change, and there is no stability guarantee. Do not run this in production.

Why it exists

NoLag's data plane (kraken) and every client SDK are already open source. The piece missing from a self-hosted stack was the logic that decides who may do what. This repository is that piece, so the whole system can be run without depending on NoLag's hosted service.

Run your own broker, your own kraken, your own core, your own Postgres. NoLag runs the same container image published from this repository. No fork, no separate proprietary build.

Where it sits

        clients (js, react-native, go, python SDKs)
                          |
                      kraken                    <- data plane, Apache 2.0
                          |
          may this actor touch this topic?
                          |
    ┌─────────────────────────────────────────┐
    │  your host: routing + your auth         │  <- you write this, or use example/
    │  ┌───────────────────────────────────┐  │
    │  │  @nolag/core                      │  │  <- this repo, Apache 2.0
    │  └───────────────────────────────────┘  │
    └─────────────────────────────────────────┘
                          |
                     PostgreSQL

There is no external message broker in that picture, and none is required: kraken's default fan-out is in-process, so a single node needs nothing but itself. Point it at MQTT instead when you want a broker you already operate.

Kraken caches authorization decisions, so core is not in the path of every message. It is consulted when a connection is established, periodically to confirm a session is still valid, and when an actor subscribes to something it has not been granted yet.

Mounting it

import { CoreModule, coreEntities, allCoreMigrations, AuthzFacade } from "@nolag/core";

@Module({
  imports: [
    TypeOrmModule.forRoot({
      // Explicit arrays, not a glob. A glob relative to your own source will
      // never reach node_modules, and core's tables would silently not exist.
      entities: [...coreEntities, ...myEntities],
      migrations: [...allCoreMigrations, ...myMigrations],
    }),
    CoreModule.forRoot({ signingKeyEncryptionKey: process.env.SIGNING_KEY }),
  ],
})
export class MyHostModule {}

Core binds to TypeORM's default connection. It does not open one of its own, so its tables have to be on the DataSource you already have. If your host owns entity classes for these same tables, register both: TypeORM maps two classes to one table without complaint, and a write through one class only touches the columns that class declares.

Then inject AuthzFacade behind whatever authentication you already have.

The contract

Four questions the broker asks, and the facade methods that answer them:

| Broker calls | Facade | | --- | --- | | on connect | validateActor(accessToken) | | periodically, for a live session | revalidateActor(actorTokenId) | | on a subscribe it has not cached | checkRoomAccess(actorTokenId, pattern) | | on subscribe and unsubscribe | updateSubscription(...) |

Responses must go through the exported toBroker* adapters rather than serialising a DTO. That file is the wire contract, and routing everything through it is what stops a field rename from silently changing what every deployed broker receives.

Plus configuration facades for project, app, room, lobby, access scope, actor token and signing key, and whole-project export and import, so state can be moved in and out without a database dump.

See docs/ARCHITECTURE.md for the resolution model, the topic addressing scheme and the schema.

What it is not

Deliberately absent, and staying absent:

  • billing, plans, subscriptions, metering, usage aggregation
  • organizations, users, teams, invitations
  • broker fleet management, version registries, upgrade orchestration
  • NoLag's hosted portal and app builder
  • any authentication of its callers, which is the host's job

The repository ships an example host and a small admin UI over it, both covered below. Neither is the NoLag portal, and neither is meant for production.

Core enforces limits it is given as plain numbers. It has no concept of what they cost or where they came from. Anything that only exists because someone runs this as a commercial service lives outside this repository.

Trying it

The compose stack exists so you can see the whole system working in a few minutes on your own machine. It is not a production deployment, and it is not trying to be one. See what it is not before you point anything real at it.

./quickstart/quickstart.sh

That generates .env, brings up Postgres, core, kraken and the admin UI, and imports a demo project. The first run compiles kraken from source, which takes a few minutes; after that it is seconds.

When it finishes:

| | | | --- | --- | | Admin UI | http://localhost:3401 | | Core API | http://localhost:3400, with OpenAPI at /swagger | | Broker | ws://localhost:8410/ws |

The demo project's access tokens are written to quickstart/credentials.json. They are shown once, because core stores only their hashes.

Nothing asks you for a credential, because the example host authenticates nobody. Every published port binds to 127.0.0.1 for exactly that reason.

Connecting is then the same as against hosted NoLag:

import { NoLag } from "@nolag/js-sdk";

const client = NoLag(accessToken, { url: "ws://localhost:8410/ws" });
await client.connect();
client.subscribe("chat/general/messages");

Stop with docker compose down, or docker compose down -v to discard the data too.

The example host

example/ is the shortest host that does anything useful: it owns the DataSource, mounts CoreModule, and exposes the facades through thin controllers. About two hundred lines, and worth reading before you write your own.

It installs @nolag/core from a packed tarball rather than compiling it alongside, so a missing export or peer fails the build there instead of quietly working.

It authenticates nobody, and says so at every boot. That is not an omission: core has no opinion about who may call it, and an example with a token check is an example people copy into production.

The admin UI

A small browser surface over the example host: list projects, read one, create one from a configuration document, and delete one.

Do not expose it, or the host it talks to. Anyone who reaches either can read and destroy every project and mint credentials for any of them. Set CORS_ORIGINS if you serve the UI from somewhere other than the compose default; * is rejected rather than honoured.

Without any of it

The library is the deliverable. Drop example/, ui/ and the compose file entirely, write your own host, and nothing about the authorization path changes.

What the quickstart is not

It is a demonstration, not a deployment. Everything below is a deliberate omission, not an oversight, and the list is here so nobody has to discover them the hard way:

  • No authentication at all on the example host, and no TLS anywhere. Everything is plaintext HTTP over loopback. Reaching the port is the same as owning every project behind it.
  • Secrets sit in a plaintext .env that the script generates and chmods to 600. That is not secret management.
  • Postgres runs in a container with a local volume and no backups, no replication and no tuning.
  • One node. No clustering, no failover, no horizontal scale. Kraken's in-process fan-out means a restart drops every connection.
  • Message history is off, and the store is in-memory. Anything kraken did record would die with the container.
  • The admin UI has no accounts and no login, because the host behind it has none either.

The part worth keeping in a real deployment is the library and the shape of the wiring: the host owns the DataSource, passes core's entities and migrations into it, and mounts the facades behind real authentication. The rest belongs to whatever you already use to run things.

Verifying a deployment

docker compose up -d --wait
npm run test:stack

19 tests drive the running stack with a real @nolag/js-sdk client: authentication, a pub/sub round trip, restricted apps, private rooms, and isolation between two tenants.

The library has its own suites, which need Postgres but not the whole stack:

./scripts/test-db.sh     # create and migrate the test database
npm test                 # unit
npm run test:e2e         # against real Postgres
npm run test:package     # pack, install into a scratch project, boot it

test:package is the one that catches what nothing else can. Everything else tests the source; that one tests the artefact, the way a consumer gets it. A NestJS library can compile, pass every test, and still be broken on install: a peer shipped as a dependency gives the consumer two copies of TypeORM and two metadata registries, a files omission makes an import resolve to nothing, and neither is visible from inside the repository.

The suite starts by checking that a token core never issued is refused. Kraken's default auth backend is a static token file with an allow-all switch, and a stack wired that way accepts everything, so a happy-path test would pass green while proving nothing. If that first check fails, every test in the file fails with the reason rather than reporting a mostly-passing run.

Licence

Apache License 2.0. See LICENSE and NOTICE.

Contributing

Issues are welcome; code contributions are not accepted. See CONTRIBUTING.md for why, and for what that does and does not mean for the licence on the code you already have.