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

@reticulum/dacar

v1.6.1

Published

JavaScript implementation of Dacar, a Decentralized Access Control system for Reticulum

Readme

@reticulum/dacar — JavaScript implementation

A JavaScript implementation of the Dacar 1.0-RC6 specification: decentralized, offline-first access control for Reticulum mesh networks.

Dacar is a tuple-based authorization system (inspired by Google Zanzibar) built on an LWW-Element-Set CRDT. Each node evaluates permissions locally against a replicated, eventually-consistent authorization state, with delegation chains that terminate at configured Root Trust Anchors.

This is a modern ES-module package with JSDoc type annotations that runs on browsers, Node.js, Deno, and Bun with no build step.

Dependencies

| Need | Choice | Why | | ---- | ------ | --- | | Ed25519 sign/verify (§5.2) | @reticulum/core Identity (Web Crypto) | The canonical Reticulum JS stack | | HMAC-SHA256 / SHA-256 (§3.3, §6.1) | Web Crypto crypto.subtle | Standard, runtime-portable | | MessagePack (§5.3) | @reticulum/core MsgPack | Reuses the canonical stack's encoder |

@reticulum/core is the base dependency of the pure core; the optional transport adapters additionally use @reticulum/lxmf (the LXMRouter), @reticulum/rfed (the RFedClient), and core's Destination/Link.

Note on MessagePack + 64-bit HLCs: a packed HLC (physical_ms << 16) exceeds Number.MAX_SAFE_INTEGER, so it is represented as a bigint. The HLC is always encoded as a bigint (uint64) and normalized with BigInt() on decode, so it round-trips losslessly.

Install

cd javascript
npm install

Layout

src/
  hlc.js          §5.1  Hybrid Logical Clocks (bigint, 64-bit packed, big-endian)
  namespace.js    §3.3  Namespace Label Privacy: salted HMAC-SHA256 hashing,
                        object segmenting, wildcard flag, hashed-object matching
  tuple.js        §3.1, §6.1  hashed authorization Tuple + SHA-256 Tuple Hash
  threshold.js    §4.1  N-of-M Threshold Groups + 16-byte Group ID
  operation.js    §5.2, §5.3  signed Operation (single + multi-sig), pre-image,
                        MessagePack transport payload
  crdt.js         §6, §9  LWW-Element-Set state, merge (Remove wins ties),
                        Time-Horizon Tombstone Pruning, intake rejection
  config.js       §4, §10  Root Trust Anchors, Privacy Salts (Primary + Legacy),
                        Authoritative Identity, deletion horizon
  engine.js       §7    recursive delegation evaluation, hashed hypotheses,
                        multi-salt shared work bound
  challenge.js    §8    Strict Consistency Challenge (hashed, multi-salt) +
                        signed Freshness Receipts
  verifier.js     §5.2, §11.2.4  verify-on-ingest: IssuerKeyset, KeyResolver,
                        Keyring, verifyOperation()
  delta.js        §11.2.4  DeltaReceiver — transport-agnostic receive boundary
                        (decode → verify → apply)
  naming.js       §8, §11  RNS naming constants (fixed discriminators +
                        configurable RFed topic)
  index.js              public API
  transport/      §8, §11.2, §11.3  optional RNS/RFed/LXMF adapters (opt-in):
    rnsIdentity.js   §3.1, §11.2.4  RnsIdentityResolver (recall → verify key)
    rnsChallenge.js  §8  Challenge over an RNS Link (server endpoint +
                        client transport + establishLink)
    lxmfSync.js      §11.2/§11.3  targeted LXMF Delta delivery + Paper Messages
    rfedSync.js      §11.1  RFed many-to-many convergence
    index.js               transport barrel
test/             node:test smoketests for every module (incl. transport-*)
scripts/test.sh   cross-runtime runner (node / deno / bun)

Quick start

import {
  Action,
  Clock,
  Config,
  Engine,
  NamespaceHasher,
  Operation,
  StateVector,
  Tuple,
} from "@reticulum/dacar";
import { Identity } from "@reticulum/core";

const anchor = await Identity.generate();          // your Root Trust Anchor
const ROOT = anchor.identityHash;                   // 16-byte hash

const hasher = new NamespaceHasher(); // default salt is FAIL-OPEN; supply a real 32-byte salt!
const state = new StateVector();
const clock = new Clock();

// Bootstrap: the root anchor grants "read" on "sensor:wind".
const op = await new Operation({
  tuple: await Tuple.fromPlaintext({
    objectId: "sensor:wind", relation: "read", grantee: ROOT, issuer: ROOT, hasher,
  }),
  action: Action.GRANT,
  hlc: clock.now(),
}).sign(anchor);
state.apply(op);

const engine = new Engine(new Config({ rootTrustAnchors: [ROOT] }), state);
console.log(await engine.evaluate("sensor:wind", "read", ROOT)); // true

Transport layer

The pure core has no transport: it is wired to the Reticulum transports by the optional @reticulum/dacar/transport subpath. Importing the core (@reticulum/dacar) never pulls it in. The transport adapters build on @reticulum/core, @reticulum/lxmf, and @reticulum/rfed (since reticulum-js 0.8.2, LXMF and rfed are packages of their own rather than core subpaths).

Every transport funnels received bytes through the same verify-on-ingest seam (DeltaReceiver.applyPayload(), §11.2.4): a Delta is decoded, authenticated by Ed25519 signature, and only then merged — regardless of whether it arrived over RFed, LXMF, or a scanned QR code.

For bulk / full-state convergence (e.g. RFed catch-up), pack many signed Deltas into one message and ingest them as a batch with DeltaReceiver.applyPayloads() — each element is still independently verified-on-ingest, so a forged element is dropped without aborting the rest. This is the secure alternative to StateVector.merge(), which is trusted-local-only (snapshot/restore of a node's own state) and must never be fed network bytes:

const batch = DeltaReceiver.packPayloads([opA.toPayload(), opB.toPayload()]);
const applied = await rx.applyPayloads(batch); // → # of Deltas verified + applied
import {
  RnsIdentityResolver,
  LxmfDeltaDelivery,
  RfedDeltaSync,
  RnsChallengeServer,
  RnsLinkTransport,
  establishLink,
} from "@reticulum/dacar/transport";

| Adapter | Spec | Role | | ------- | ---- | ---- | | RnsIdentityResolver | §3.1, §11.2.4 | Resolves a single-identity Issuer hash to its 64-byte public key via Destination.recall (the announce store); Threshold Groups & out-of-band identities fall through to an optional fallback resolver. Usable directly as a KeyResolver. | | LxmfDeltaDelivery | §11.2, §11.3 | Targeted, forward-secret Delta delivery over LXMF (store-and-forward to offline nodes), plus LXMF Paper Message export/import for air-gapped/optical (QR) transport. | | RfedDeltaSync | §11.1 | Many-to-many CRDT convergence via RFed (RFedClient): publish + receive signed Deltas on a shared, deployment-overridable channel, routed through verify-on-ingest — never the unauthenticated merge(). | | RnsChallengeServer / RnsLinkTransport / establishLink | §8 | The Strict Consistency Challenge over a real RNS Link: an authoritative endpoint answering dacar.auth.v1 requests, and the client-side Transport callable (+ helper to open a Link) for ChallengeClient. |

The Issuer Hash must be the canonical RNS identity hash. RNS defines an identity hash as SHA-256(P)[:16] where P is the 64-byte public key (X25519_pub ‖ Ed25519_pub). RnsIdentityResolver recalls exactly that hash; any other value cannot be authenticated and is dropped. (Threshold Group IDs are exempt — they are resolved by explicit keyset registration, not recall.)

RFed convergence

import { RFedClient } from "@reticulum/rfed";
import { DeltaReceiver, StateVector } from "@reticulum/dacar";
import { RfedDeltaSync } from "@reticulum/dacar/transport";

const client = new RFedClient({ identity, rns });
const sync = new RfedDeltaSync({
  receiver: new DeltaReceiver(state, resolver),
  client,
  // topic: "dacar.policy.v1",  // deployment-overridable default
});
await sync.subscribe(nodeHash);          // cache the channel's stamp cost
await sync.listen();                     // receive live fanout Deltas
await sync.publish(deltaPayload, nodeHash);

Strict Consistency Challenge

import { ChallengeClient } from "@reticulum/dacar";
import { RnsChallengeServer, RnsLinkTransport, establishLink } from "@reticulum/dacar/transport";

// Server side: expose the Authoritative Identity on dacar.auth.v1.
await RnsChallengeServer.create({ identity: authority, server, rns });

// Client side: open a Link and challenge it (partition → DENY).
const dest = await Destination.OUT("dacar.auth.v1", DestType.SINGLE, authority, rns);
const link = await establishLink(dest);
const client = new ChallengeClient(config, state, authorityPublicKey, new RnsLinkTransport(link));
await client.authorize("sensor:wind", "calibrate", granteeHash);

Tests

Tests use node:test and run under every installed runtime (node, deno, bun). The runner fails if none is installed:

npm test            # runs under each installed runtime
npm run test:node   # node only
npm run test:deno   # deno only
npm run test:bun    # bun only

Publishing

Published to both registries as @reticulum/dacar:

# JSR
cd javascript && deno publish

# npm
cd javascript && npm publish