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

@forgesworn/fold-kit

v0.4.0

Published

Circle primitives for private Nostr groups nobody operates: epoch keys, link invites, removal by rekey, device credentials.

Readme

@forgesworn/fold-kit

Circle primitives for private Nostr groups that nobody operates: a room key per epoch, sealed to each device that stays; link invitations as capabilities; member removal by rekey; device credentials so a person approves a device once and it signs from then on.

Fold-kit is extracted from KithMoot so that other ForgeSworn clients can share one circle model. KithMoot's wire format does not change: every derivation label and event shape moved byte-identical from the pinned source commit and is pinned by known-answer vectors (see EXTRACTION.md). Framework-free: owns no storage, UI, relay defaults or identity keys, and no relay I/O of its own - callers inject a transport.

Status: T1 extraction complete (see EXTRACTION.md). Not yet published to npm under this API; pin an immutable Git commit until it is (see Install). KithMoot itself has not cut over to this kit yet - it still carries its own copy of these modules.

Install

npm install @forgesworn/fold-kit

Not yet published to npm. Until it is, pin an immutable Git commit:

{
  "dependencies": {
    "@forgesworn/fold-kit": "github:forgesworn/fold-kit#<commit>"
  }
}

Peer dependencies

{
  "peerDependencies": {
    "nostr-tools": ">=2.24.2 <3",
    "@noble/hashes": "^1.8.0",
    "@noble/curves": "^2.0.1"
  }
}

Peers, not dependencies, so a consumer such as KithMoot keeps a single copy of nostr-tools (guarded by its own overrides and version-guard test) and of the noble libraries, at the same versions KithMoot already pins. @noble/hashes is pinned to its 1.x major only: its 2.x major changed its hkdf signature to reject a string info argument (this kit's HKDF calls pass strings) and its exports map no longer resolves the bare subpaths this kit and KithMoot both import without a .js suffix, so 2.x cannot satisfy this kit's imports as written.

ESM only ("type": "module"). Node.js >=22.13.

Exports

Two entry points: @forgesworn/fold-kit (everything) and @forgesworn/fold-kit/lane (just the lane helpers, for a consumer that only wants to classify a relay as public/sheltered/direct without pulling in the rest).

Identity, verification, hex

  • hexEquals, normaliseHex - constant-shape hex comparison and lower-casing
  • verifyEventUncached, boundedEventVerifier - Nostr event signature checks
  • localIdentity, ParticipantIdentity, UnsignedEvent - the signer seam every codec below is built on

Wire kinds and types

  • KINDS - the circle layer's kind numbers (CREDENTIAL, CHAT, INVITATION_REQUEST, INVITATION_GRANT, INVITATION_RETIREMENT, GROUP_INVITATION, ROOM_REKEY, EPOCH_REQUEST, EPOCH_GRANT) - a subset of KithMoot's full registry. CHAT (1460) is included because a downstream app's own board events are designed to share KithMoot's chat kind, so a relay cannot tell a board from a chat (see docs/extraction-plan-excerpt.md).
  • DeviceCredential, AccessTier, AgentRule, RoomPolicy, KindredProof - the link-layer wire types
  • RelayTransport - the transport seam (publish/subscribe/close) every codec below that talks to a relay takes as a dependency. This kit declares its own interface rather than importing KithMoot's relay pool, which has not moved here (see EXTRACTION.md).

Rooms, credentials, access

  • generateRoomSecret, deriveRoom, encodeJoinUrl, decodeJoinUrl, parseRoomPolicy - the legacy v1 join URL and room id/key derivation
  • createDeviceCredential, verifyDeviceCredential, PERSON_CREDENTIAL_MAX_SECONDS - room-scope and person-scope device credentials. createDeviceCredential throws RestampedCredentialExpiryError for a person-scope credential a restamping signer would make unverifiable everywhere (see CHANGELOG.md) - catch it and retry with a shorter expiresAt margin
  • issueKindredProof, evaluateAccess - kindred-tier admission
  • sanitiseDisplayName, MAX_DISPLAY_NAME_LENGTH - defused display names
  • safeRelayUrls, safeIceUrls, assertNetworkHintBounds, and the related MAX_* bounds - link envelope network hints

Links and invitations

  • parseRoomLink, encodeRoomLink, RoomLink - the v1/v2/v3 link envelope
  • createRoomInvitation, roomInvitation, deriveInvitationId, encodeInvitationRequest, decodeInvitationRequest, verifyInvitationDelegation, encodeInvitationGrant, decodeRoomAdmissionGrant, encodeInvitationRetirement, decodeInvitationRetirementNotice, hostRoomInvitation, requestRoomAdmission - the v2 live rendezvous invitation, its delegation chain and retirement
  • encodePersistentInvitation, decodePersistentInvitation, requestPersistentRoomAdmission - the v3 stored group invitation (1463). Pass endsAt (unix seconds, after now and at most 30 days on) for a conference room: the encrypted body carries ends and the event a NIP-40 expiration tag, so relays drop it when the room ends; the decoder returns endsAt and refuses a tag that disagrees with the body
  • Room relays: pass relays (one to eight distinct safe relay URLs in canonical normalizeURL form, e.g. wss://relay.example.com/) and the encrypted body carries them after ends. Every member's pool includes them, so members never land on disjoint relays. The decoder returns relays and refuses the whole envelope on a malformed list; requestPersistentRoomAdmission keeps the relays of the newest signed copy that names any. isInvitationRelays, requireInvitationRelays and MAX_INVITATION_RELAYS are the rule
  • withExpiration, isRoomEnds, requireRoomEnds, MAX_ROOM_ENDS_SECONDS - the conference-room expiration rule: add the end as an expiration tag, keep an earlier one, lower a later one. encodeInvitationRetirement takes endsAt, and the epoch encoders, hostRoomEpoch and requestRoomEpoch take expiresAt, to tag what they sign the same way

Epochs (removal by rekey)

  • deriveEpoch, generateEpochSecret - per-epoch id/key derivation
  • encodeRekeyEvent, peekRekeyEvent, decodeRekeyEvent - the durable rekey notice (1462), sealed per remaining device
  • encodeEpochRequest, decodeEpochRequest, encodeEpochGrant, decodeEpochGrant, epochRequestAdmission, deriveEpochRequestKey - catch-up for a device that missed a rekey
  • hostRoomEpoch, requestRoomEpoch, EpochRefusedError - the live request/grant desk
  • canonicalAdmins, signAdmins, verifyAdmins - the authority's signed admin list
  • canonicalChannels, signChannels, verifyChannels, CHANNEL_NAME, RESERVED_CHANNELS - the authority's signed channel list

Scoped labels and sub-key certificates (a consuming app's own keys)

  • deriveScoped - derive an app-defined { id, key } pair from an epoch key under an app's own labelled namespace (e.g. myapp/v1/board/<id>/update), so an app can ride the same epoch as the roster and the chat - and rotate on the same rekey - without ever deriving a key that could be mistaken for a KithMoot channel. Refuses this kit's own protocol namespace and any label outside SCOPED_LABEL_PATTERN.
  • createSubKeyCertificate, verifySubKeyCertificate, SUB_KEY_CERTIFICATE_SCOPE - a small, locally-signed statement that a credentialled device minted a particular app-derived key (a deriveScoped output, typically) for a particular scoped id. Rides inside a signed message's ciphertext alongside the device credential it depends on; never published on its own, and never accepted by verifyDeviceCredential (its scope: "sub" is a value that function already refuses). Verification is strict and canonical: exactly four two-element tags in a fixed order, empty content, an expiration in canonical decimal form compared as the exact tag string (not numerically) against the device credential passed in - verifySubKeyCertificate reads that credential's own device and expiration tags itself, rather than trusting a caller to have copied them out correctly.

Channel (main entry, and deriveChannel only)

  • deriveChannel, CHANNEL_ID_INFO, CHANNEL_KEY_INFO, MAX_CHANNEL_NAME_LENGTH - a named channel's id/key, derived from the room key. ChatLog and the chat event codecs stay in KithMoot; only the derivation moved.

Lane (@forgesworn/fold-kit/lane)

  • LANES, LANE_MEANING, LANE_LABEL, LANE_GLYPH, isLane, laneOfRelayUrl, laneOfRelays, weakestLane, isDowngrade - classifies a relay as public, sheltered or direct

How it relates to its siblings

  • covey-kit: circle state derived from a root, latest-wins configuration, per-recipient gift wraps.
  • roost-kit: transport for private circles (gift wrap, relay fan-out, offline outbox).
  • fold-kit: symmetric epoch keys with a pinned authority, sealed rekeys and link invitations. Suited to high-rate encrypted streams such as chat and collaborative documents, where a wrap per recipient per message is too costly.

Nothing is shared in code between the three today: the key models are incompatible (a static epoch secret with sealed rekeys here, versus a deterministic reseed from a root in covey-kit), the trust models differ (pinned authority here versus caller-enforced roles in covey-kit), and covey-kit/roost-kit pin a different nostr-tools version than this kit and KithMoot share.

Development

npm install
npm run build       # compile TypeScript into dist/
npm test            # run the Vitest suite, including both vector files
npm run typecheck    # type-check src/ and test/ (matching KithMoot, vectors/ is not tsc-checked)
npm run vectors      # run only the vector-verification suites
npm run diff-source  # compare moved modules against the pinned source commit (needs FOLD_KIT_SOURCE_DIR)
npm run generate-fold # regenerate vectors/fold-vectors.json (needs a prior build)
npm run bundle-check # esbuild browser bundle check (needs a prior build)
npm run check        # typecheck + test + diff-source

There is no separate lint script.

Licence

MIT