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

@abrum/web-runtime

v0.1.1

Published

Browser-facing abrum runtime for typed, signed twins over a station.

Downloads

64

Readme

@abrum/web-runtime

Browser-facing abrum runtime for typed, signed twins over a station.

The runtime keeps the kernel model intact: data is still signed twins, schemas are schema twins, and Rooms remain the sync, storage, and access boundary. Physical membership comes from the per-Room store; relations describe semantic graph edges. The developer API hides the normal boilerplate so app code can work with typed local data.

Core Shape

import { abrum } from "@abrum/web-runtime";

export const notesApp = abrum.app("notes", {
  note: abrum.entity({
    text: abrum.string(),
    createdAtMs: abrum.date().index(),
    updatedAtMs: abrum.date(),
  }).named("schema-note-v1").type("note").roomScoped(),
});

This compiles to:

  • a schema twin for schema-note-v1
  • object twins with content.type === "note"
  • JSON Schema metadata for required fields, indexes, unique fields, and abrum app identity

Normal app code should not manually create schema or object twins. Create a contains relation only when the application models an actual hierarchy or a selectively shared subtree.

Field Builders

abrum.string()
abrum.number()
abrum.boolean()
abrum.date()
abrum.json()
abrum.any()

Modifiers:

abrum.string().optional()
abrum.string().index()
abrum.string().indexed()
abrum.string().unique()

Notes:

  • Fields are required by default.
  • abrum.date() accepts a timestamp, ISO string, or Date; Date is stored as an ISO string.
  • abrum.number() currently accepts safe integers because canonical twin blocks require safe integers.
  • .index() and .unique() are schema metadata today; station-backed indexes are the next layer.

Runtime Usage

import { AbrumRuntime } from "@abrum/web-runtime";
import { notesApp } from "./notes.schema";

const runtime = await AbrumRuntime.open({
  station: "http://127.0.0.1:8792",
  storageKey: "abrum.notes.owner:http://127.0.0.1:8792",
});

await runtime.createUser("Robin"); // local Device Key; no recovery phrase
await runtime.pair("Robin");

await runtime.createEntity(notesApp, "note", {
  text: "hello",
  createdAtMs: Date.now(),
  updatedAtMs: Date.now(),
});

createEntity creates and pushes the schema twin, object twin, and space containment relation in one call.

Rust/WASM Core

The public TypeScript API is not meant to become a second protocol implementation. Protocol-critical work should live in Rust:

  • identity derivation
  • canonical TwinBlock serialization
  • CID calculation
  • Ed25519 signing
  • signed HTTP request payloads
  • X25519 identity derivation
  • HKDF Room-key derivation
  • XChaCha20-Poly1305 seal/open, key wrap/unwrap, and conveyed secrets
  • schema/object/relation/user-principal twin constructors

crates/abrum-wasm exposes those abrum-core functions as a small WASM ABI. The browser runtime accepts a protocol core, so app code does not change when the generated WASM package is loaded:

import init, * as wasm from "@abrum/wasm";
import { AbrumRuntime, createAbrumWasmProtocolCore } from "@abrum/web-runtime";

await init();

const runtime = await AbrumRuntime.open({
  station: "http://127.0.0.1:8792",
  core: createAbrumWasmProtocolCore(wasm),
});

Until a generated WASM package is installed, the runtime keeps a TypeScript fallback with the same AbrumProtocolCore interface. That fallback is a development convenience, not the long-term source of truth.

React Usage

@abrum/react builds on this package:

import { useAbrumDb } from "@abrum/react";
import { notesApp } from "./notes.schema";

export function Notes() {
  const db = useAbrumDb(notesApp);
  const notes = db.note.useMany({
    order: ["createdAtMs", "desc"],
  });

  async function addNote(text: string) {
    await db.note.create({
      text,
      createdAtMs: Date.now(),
      updatedAtMs: Date.now(),
    });
  }

  return notes.data.map((note) => (
    <article key={note.$.cid}>
      {note.text}
    </article>
  ));
}

The query is realtime by default. The runtime subscribes to station events and the React layer incrementally pulls twin-log entries when the station reports a change.

Query Options

const recent = db.note.useMany({
  where: {
    text: { contains: "hello" },
    createdAtMs: { gt: Date.now() - 86_400_000 },
  },
  order: ["createdAtMs", "desc"],
  limit: 50,
});

Current query evaluation is client-side over the readable twin log for the selected space. This is correct for the first web runtime, but large production spaces need station projections and indexes behind the same API.

Raw Escape Hatches

The high-level API is the default path. The raw API remains available for system code, migrations, custom protocols, and debugging:

const schema = await runtime.schemaTwin("custom-schema", jsonSchema);
const [object] = await runtime.objectInRoomTwins(schema.cid, content);
const relation = await runtime.relationTwin({
  source: object.cid,
  target: otherCid,
  relationType: "references",
  inheritsAccess: false,
});

await runtime.putTwins([schema, object, relation]);
const log = await runtime.pull();

Identity And Spaces

The runtime owns the browser user identity for the current storage key:

const { user } = await runtime.createUser("Robin");

// Product sessions are enrolled by another active ABRUM device and handed to
// the app by its trusted host. `restoreUser(phrase)` remains only as a one-time
// compatibility import for historical mnemonic-as-seed browser sessions.

Pairing a user with a station creates the user's hosted space and stores the selected space:

await runtime.pair("Robin");
await runtime.loadSpaces();
runtime.selectSpace(spaceId);

Rooms are the default access and sync boundary. roomScoped() entities are stored in the selected Room and inherit its root authorization from physical Room membership; no technical relation Twin is generated.

Production Direction

This package is intentionally small. The standard-worthy direction is:

  • app schemas as code
  • typed entity create/query/update/delete
  • realtime by default
  • raw twin escape hatches
  • station-backed indexes and projections behind the same query API
  • production schema lock so unknown app schemas cannot be created by arbitrary clients
  • relation and action DSLs built on the same app descriptor

The kernel should stay twin-native. Application code should stay data-native.