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

@industrialalgebra/schubert-tsukoshi

v0.5.0

Published

Pure TypeScript geometric access control via Schubert calculus — zero-dependency extraction of the Schubert Rust crate, with impossibility detection in the browser.

Readme

schubert-tsukoshi

Pure-TypeScript geometric access control via Schubert calculus — zero runtime dependencies, with impossibility detection in the browser.

License: Apache-2.0

schubert-tsukoshi is a TypeScript extraction of the Schubert Rust crate's access-control model. Capabilities are Schubert conditions on a Grassmannian; access is the intersection of those conditions, and the intersection number counts exactly how many valid configurations exist. The headline feature — detecting that a policy is geometrically impossible (the σ₂·σ₁₁ = 0 case) — works in the browser with no backend.

It follows the @cliffy-ga/tsukoshi pattern: pure TypeScript, zero dependencies, works everywhere (browser, Node, Deno, React Native).

Install

npm install @industrialalgebra/schubert-tsukoshi

Publish: the @industrialalgebra npm org exists; this package is publish-ready (run npm publish from schubert-tsukoshi/).

Quick start

import { AccessController } from "@industrialalgebra/schubert-tsukoshi";

const acl = new AccessController("gr24"); // Gr(2,4): a 4-dimensional policy space

acl.registerCapability({ id: "read",   partition: [1],   kind: "read"  });
acl.registerCapability({ id: "write",  partition: [2],   kind: "write" });
acl.registerCapability({ id: "manage", partition: [2,1], kind: "manage" });

const alice = acl.createPrincipal("alice");
acl.grant(alice, "read");
acl.grant(alice, "manage");

const decision = acl.check(alice, ["read", "manage"]);
// => { kind: "granted", configurations: 1 }

The killer feature: impossibility detection

A principal can hold every required capability and still be denied, because the conditions are geometrically incompatible. This catches policy conflicts that pass a naive set-membership check:

const acl = new AccessController("gr24");
acl.registerCapability({ id: "write", partition: [2],   kind: "write" });
acl.registerCapability({ id: "dwide", partition: [1,1], kind: "custom" });

const mallory = acl.createPrincipal("mallory");
acl.grant(mallory, "write");
acl.grant(mallory, "dwide");          // set-membership: ✓ both held

acl.check(mallory, ["write", "dwide"]);
// => { kind: "impossible", conflicting: ["write", "dwide"] }
//    σ₂ · σ₁₁ = 0 on Gr(2,4) — the policy is geometrically void.

Notably, σ₂·σ₁₁ is not impossible on the larger Gr(3,6) — the bigger box admits the product. Same code, different Grassmannian, different verdict.

Decisions

check() returns a discriminated union:

| kind | Meaning | | ----------------- | ----------------------------------------------------------------------- | | "granted" | Access permitted; configurations = Schubert intersection number. | | "impossible" | All caps held, but geometrically incompatible (conflicting ids). | | "underconstrained" | Conditions don't pin a finite config set (dimension > 0). | | "denied" | Principal lacks a required capability (missing id) — set-membership. |

Capability tokens (./crypto)

Ed25519-signed capability and grant tokens — a TypeScript mirror of the Rust crypto module, with a wire format byte-compatible with the Rust crate. Tokens issued in Rust verify here and vice-versa.

The ./crypto subpath depends on @noble/ed25519 and @noble/hashes (audited, standard). The core . entry remains zero-dependency.

import {
  Issuer,
  Verifier,
  grantToBytes,
  grantFromBytes,
} from "@industrialalgebra/schubert-tsukoshi/crypto";

// Persist the issuer by its 32-byte seed (store securely, e.g. 0600 file).
const issuer = Issuer.fromSeedHex(process.env.ISSUER_SEED!);
const grant = issuer.issueGrant("alice", [
  { id: "memory:read",  partition: [1] },
  { id: "memory:write", partition: [2] },
]);

// v0.5.0 — grant lifecycle options: pin the nonce, or set a signed expiry.
const session = issuer.issueGrant(
  "alice",
  [{ id: "memory:read", partition: [1] }],
  { expiresAt: sessionEndUnix }, // random nonce by default
);

// Verifiers only need the public key.
const verifier = new Verifier(issuer.publicKey());
verifier.verifyGrant(grant);           // throws if signature invalid / expired
verifier.verifyGrantAt(session, now);   // deterministic clock (tests, replay)
verifier.may(grant, [1]);              // true — geometric containment

// Wire format roundtrips and is Rust-compatible:
const bytes = grantToBytes(grant);
grantFromBytes(bytes);

Interop guarantee: the cross-validation test suite (src/crypto/tokens.test.ts) issues tokens from a fixed seed in both Rust and TypeScript and asserts byte-identical output. Regenerate the Rust vectors with cargo run --example tsukoshi_crypto_vectors --features crypto.

Supported Grassmannians

Precomputed tables ship for three policy spaces:

| Tag | Gr(k,n) | Dimension | Use case | | ------ | -------- | --------- | ----------------------- | | gr24 | Gr(2,4) | 4 | Standard RBAC | | gr36 | Gr(3,6) | 9 | Complex multi-tenant | | gr48 | Gr(4,8) | 16 | Enterprise policy space |

To add more, regenerate the tables from the Rust crate:

cargo run --example generate_ts_lr_tables > schubert-tsukoshi/src/lr-tables.ts

The generator uses amari-enumerative's exact schubert_product, so the TypeScript tables are byte-faithful to the Rust math — no transcription risk.

How it works

Schubert classes are multiplied via precomputed Littlewood-Richardson coefficient tables. A check folds the position and required classes into a single Schubert polynomial, then reads the coefficient of the point class σ_{(n−k)^k}:

  • coefficient > 0granted (that many configurations)
  • coefficient == 0, codimension sum == dimimpossible
  • codimension sum < dimunderconstrained

This mirrors the Littlewood-Richardson branch of Schubert's AccessController::check.

Distributed grants (./protocols)

A replicated capability-grant set — GrantCRDT — for synchronizing who has what capability across leaderless replicas, built on @cliffy-ga/tsukoshi's VectorClock. This is the trusted-replica counterpart to the proof-carrying tokens in ./crypto:

  • ./crypto (GrantToken) — untrusted clients present a signed bearer token (v0.5.0: signed expiry + per-issuance nonce; renewal = re-issue).
  • ./protocols (GrantCRDT) — trusted replicas converge on a shared grant set and answer access queries from merged state. v0.5.0 tombstones: revokeGrant(nonceHex) kills one specific issuance everywhere — a grow-only set merged by union, so no merge order can resurrect it (unlike the add-wins concurrent rule above, which is exactly why tombstones are a separate set).
import { GrantCRDT } from "@industrialalgebra/schubert-tsukoshi/protocols";

const hub = new GrantCRDT("hub");
hub.grant("alice", { id: "memory:read",  partition: [1] });
hub.grant("alice", { id: "memory:write", partition: [2] });

const edge = new GrantCRDT("edge");
edge.merge(hub);                  // edge converges to hub's grant set
edge.may("alice", [1]);           // true — geometric containment over merged state
edge.revoke("alice", "memory:write");

// Incident response (v0.5.0): tombstone one bearer by its hex nonce
// (toHex(grant.nonce), where toHex comes from the ./crypto subpath).
hub.revokeGrant(leakedNonceHex);
hub.isGrantRevoked(leakedNonceHex);   // true — everywhere, eventually

// Snapshot/restore for transport:
const snap = edge.toJSON();
const restored = GrantCRDT.fromJSON(snap, "edge-2");

Semantics: state-based last-writer-wins map keyed by (principal, capability). The later vector clock wins; on concurrent ops grant wins over revoke (add-wins), with nodeId as a deterministic tiebreak. Merge is commutative, associative, and idempotent, so replicas reach strong eventual consistency regardless of delivery order.

The ./protocols subpath depends on @cliffy-ga/tsukoshi (for VectorClock) but not on @noble/ed25519 — its may() uses the shared, dependency-free partitionsLe. (@cliffy-ga/tsukoshi will migrate to the @industrialalgebra scope during its refactor.)

License

Apache-2.0. © Industrial Algebra.