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

@myjoypin/node-ucan

v0.1.0

Published

UCAN for Node.js

Readme

UCAN for Node.js

This is a Node.js module to work with UCAN authorization tokens. It is a wrapper over the rs-ucan Rust library. To learn more about UCANs and how you might use them in your application, visit https://ucan.xyz or read the spec.

Current UCAN Specification is v0.10.0spec.

Installing @myjoypin/node-ucan

This module contains prebuilt platform-specific binaries for Node.js v18 on Windows x64 and Linux x64 (can be used in Docker containers without Rust setup). For other platforms it will build the binary from source.

To build from source, you must have the Rust toolchain installed. Check a supported version of Node and Rust. If you don't already have Rust installed, or don't have a supported version, go to the Rust web site for installation instructions.

npm i @myjoypin/node-ucan

# to rebuild
npm rebuild @myjoypin/node-ucan --foreground-scripts

Usage

UCANs can act the same as a classic JWT (Bearer) token, and also can grant a user the full control over their resources, with ability to delegate rights.

Example of delegation:

import { createDid, invokeUcan, verifyUcan } from "@myjoypin/node-ucan"

// secret part
// here are the server, Alice and Bob
const serverDidDocument = createDid()
const aliceDidDocument = createDid()
const bobDidDocument = createDid()

// public part
const serverDid = serverDidDocument.id
const aliceDid = aliceDidDocument.id
const bobDid = bobDidDocument.id

// Server provides some rights to Alice.

const aliceToken = await invokeUcan({
  issuer: serverDidDocument.verificationMethod[0],
  audience: aliceDid,
  // 1 day
  expiration: Math.ceil((Date.now() + 1000 * 60 * 60 * 24) / 1000),
  capabilities: {
    "api:app/xxx": {
      "book/view": [{}],
      "book/edit": [{}],
      "user/is": [{ user_id: "111" }]
    }
  },
  facts: {
    app_id: "xxx",
    user_id: "111"
  }
})

// Alice can delegate some of her rights to Bob,
// without any interaction to the server.

const bobToken = await invokeUcan({
  issuer: aliceDidDocument.verificationMethod[0],
  audience: bobDid,
  // 6 hours
  expiration: Math.ceil((Date.now() + 1000 * 60 * 60 * 6) / 1000),
  capabilities: {
    "api:app/xxx": {
      "book/view": [{}],
      "user/is": [{ user_id: "111" }]
    }
  },
  proofs: [aliceToken]
})

// Bob uses his token to acess some Alice's resource on the server.

const token = await invokeUcan({
  issuer: bobDidDocument.verificationMethod[0],
  audience: serverDid,
  expiration: Math.ceil((Date.now() + 1000 * 60 * 60 * 6) / 1000),
  capabilities: {
    "api:app/xxx": {
      "book/view": [{}],
      "user/is": [{ user_id: "111" }]
    }
  },
  proofs: [bobToken]
})

// Server verifies rights through the all proof chain.

const verification = await verifyUcan(token, {
  rootIssuer: serverDid,
  audience: serverDid,
  requiredCapabilities: {
    "api:app/{app_id}": {
      "book/view": [{}],
      "user/is": [{ user_id: "{user_id}" }]
    }
  },
  requiredFacts: {
    app_id: "*",
    user_id: "*"
  }
})
// Here the server can continue verification, checking token revocations by CID
// and performing custom caveat checks.

console.log("Access granted!")
console.log(JSON.stringify(verification, null, 2))
/*
{
  "capabilities": {
    "api:app/xxx": {
      "book/view": [
        {}
      ],
      "user/is": [
        {
          "user_id": "111"
        }
      ]
    }
  },
  "facts": {
    "app_id": "xxx",
    "user_id": "111"
  },
  "cids": [
    "bafkr4icmf3o2omytuqxkhzm76hd5ifdfnhhmharru622ca264rznwegdvy",
    "bafkr4ieyr2wnbxe6ziuuougwngnfhnfeuw2e2anwcxyy36lsmcbh5zcguu",
    "bafkr4ib24o6fsjpnmtzwvjrbptq244waiwwsdu67e27och6quzdd5azo4q"
  ]
}
*/

Decentralized identifiers

Creating an identity

To be able to invoke an UCAN, you must have a DID (Decentralized Identifier). Example of DID: "did:key:z6MkmftLP2owSZPneufrtWF7t9j2HL7sXYVgoroM59sjD7yx".

createDid(keyType?: String, useJoseFormat?: Boolean): DIDDocument

Only "did:key" DIDs are supported.

Supported key types:

  • "Ed25519" | "Ed25519VerificationKey2018" | "JsonWebKey2020"
  • "P256" | "P-256"

Additional types for DID, but you cannot sign UCANs with them:

  • "X25519" | "X25519KeyAgreementKey2019"
  • "Bls12381" | "Bls12381G2Key2020" | "BLS12381_G2"
  • "Secp256k1" | "EcdsaSecp256k1VerificationKey2019" | "secp256k1"

Example:

import { createDid } from "@myjoypin/node-ucan"

const secretDidDocument = createDid()

console.log(JSON.stringify(secretDidDocument, null, 2))

"secretDidDocument" is a secret DID document (in JSON-LD format), containing your ID and private key. Please keep it in secret.

Optionally, you can specify a key type of the DID on creation (default is "Ed25519") and the flag to return document in JOSE format:

import { createDid } from "@myjoypin/node-ucan"

const secretDidDocument = createDid("P256", true)

console.log(JSON.stringify(secretDidDocument, null, 2))

Resolving a DID document

A DID can be resolved to a DID document. The DID document (in JSON format) contains public information about the DID.

resolveDid(did: String, useJoseFormat?: Boolean): DIDDocument

Example:

import { resolveDid } from "@myjoypin/node-ucan"

const didDocument = resolveDid(
  "did:key:z6MkmftLP2owSZPneufrtWF7t9j2HL7sXYVgoroM59sjD7yx"
)

console.log(JSON.stringify(didDocument, null, 2))

Restoring a DID

A DID can be restored from a private key (specified in "verificationMethod" field in the secret DID document).

restoreDid(privateKey: Object, useJoseFormat?: Boolean): DIDDocument

Example:

import { restoreDid } from "@myjoypin/node-ucan"

const privateKey = {
  "id": "did:key:z6MkeaSMPgvhHqvNu4yem96usVDaMxSA6A5M6CrVBTXUY2Au#z6MkeaSMPgvhHqvNu4yem96usVDaMxSA6A5M6CrVBTXUY2Au",
  "type": "Ed25519VerificationKey2018",
  "controller": "did:key:z6MkeaSMPgvhHqvNu4yem96usVDaMxSA6A5M6CrVBTXUY2Au",
  "publicKeyBase58": "8BJoSgFxJRuna8x5a952PfaYPAJgGpzQBwZMBZTcoPX",
  "privateKeyBase58": "CjePJc2FYbQKTDaKeFyFRgKnzKF6DreCaX95b9x4z4Lu"
}

const secretDidDocument = restoreDid(privateKey)

console.log(JSON.stringify(secretDidDocument, null, 2))

Digital sign

The DID can be used to sign data and verify signatures.

simpleSign(privateKey: Object, message: String): String
simpleVerify(did: String, message: String, signature: String): Boolean

Example of signing a message:

import { simpleSign } from "@myjoypin/node-ucan"

const privateKey = {
  "id": "did:key:z6MkeaSMPgvhHqvNu4yem96usVDaMxSA6A5M6CrVBTXUY2Au#z6MkeaSMPgvhHqvNu4yem96usVDaMxSA6A5M6CrVBTXUY2Au",
  "type": "Ed25519VerificationKey2018",
  "controller": "did:key:z6MkeaSMPgvhHqvNu4yem96usVDaMxSA6A5M6CrVBTXUY2Au",
  "publicKeyBase58": "8BJoSgFxJRuna8x5a952PfaYPAJgGpzQBwZMBZTcoPX",
  "privateKeyBase58": "CjePJc2FYbQKTDaKeFyFRgKnzKF6DreCaX95b9x4z4Lu"
}

const signature = simpleSign(privateKey, "message to be signed")

console.log(signature)
// 0f5591xbxJ7kx8WL4wNDAU5jWcodQ91kllAcm11HF69af8mJL4WNj858yphxUVmETD9L9F44paJu2r0eTs7dDA

Example of a signature verification:

import { simpleVerify } from "@myjoypin/node-ucan"

const isValid = simpleVerify(
  "did:key:z6MkeaSMPgvhHqvNu4yem96usVDaMxSA6A5M6CrVBTXUY2Au",
  "message to be signed",
  "0f5591xbxJ7kx8WL4wNDAU5jWcodQ91kllAcm11HF69af8mJL4WNj858yphxUVmETD9L9F44paJu2r0eTs7dDA")

console.log(isValid)

UCANs

User-Controlled Authorization Network (UCAN) is a trustless, secure, local-first, user-originated authorization and revocation scheme.

We also call UCAN tokens UCANs.

Invocation of UCAN

invokeUcan({
  // the private key
  issuer: Object,
  // audience DID
  audience: String,
  // Unix time in seconds when the token becomes expired.
  expiration: number,
  // Unix time in seconds after which token becomes enabled. Optional.
  notBefore?: number,
  // Capabilities object.
  // Example:
  // {
  //   "mailto:[email protected]": {
  //     "msg/receive": [{}],
  //     "msg/send": [{ draft: true }, { publish: true, topic: ["foo"] }]
  //   }
  // }
  capabilities: Capabilities,
  // Facts object. Can contain any data linked with a token. Optional.
  facts?: Object,
  // Array of delegation proof tokens. Optional.
  proofs?: Array<String>,
  // Add a random nonce to this token. Optional, default false.
  addNonce?: Boolean,
  // Whenever to embed proof tokens to the "prf" field of facts. Doing so,
  // UCAN is complete for self-verification. Optional, default true.
  addProofFacts?: Boolean
}): Promise<String>

Example:

import { invokeUcan } from "@myjoypin/node-ucan"

const privateKey = {
  "id": "did:key:z6MkeaSMPgvhHqvNu4yem96usVDaMxSA6A5M6CrVBTXUY2Au#z6MkeaSMPgvhHqvNu4yem96usVDaMxSA6A5M6CrVBTXUY2Au",
  "type": "Ed25519VerificationKey2018",
  "controller": "did:key:z6MkeaSMPgvhHqvNu4yem96usVDaMxSA6A5M6CrVBTXUY2Au",
  "publicKeyBase58": "8BJoSgFxJRuna8x5a952PfaYPAJgGpzQBwZMBZTcoPX",
  "privateKeyBase58": "CjePJc2FYbQKTDaKeFyFRgKnzKF6DreCaX95b9x4z4Lu"
}

const token = await invokeUcan({
  issuer: privateKey,
  audience: "did:key:z6Mkmup4Wyv9kXKrmy1DB2bLhaviKSgPGSqjC1gCEtWoHjAY",
  expiration: Math.ceil((Date.now() + 1000 * 60 * 60) / 1000),
  capabilities: {
    "mailto:[email protected]": {
      "msg/receive": [{}],
      "msg/send": [{ draft: true }, { publish: true, topic: ["foo"] }]
    }
  },
  facts: {
    a: "b"
  }
})

console.log(token)

Rights delegation

The DID which is the audience of a token can delegate own rights to other DID. Tranferred rights can be the same or lower level.

To be described...

Reading UCAN without verification

decodeUcan(token: String): Ucan

Example:

import { decodeUcan } from "@myjoypin/node-ucan"

// const token = ...

let ucan = decodeUcan(token)

console.log(JSON.stringify(ucan, null, 2))

Verification of UCAN

To verify a token, you should provide required capabilities in the following format:

{
  "resource": {
    "ability": [...caveat],
  }
}

To be described...

verifyUcan(
  // the token
  token,
  {
    // The root rights issuer DID. An important field in the verification of
    // rights delegation.
    rootIssuer: String,
    // Audience DID
    audience: String,
    // Required capabilities. You can use template variables from facts with
    // "{var}" syntax. For example: "user/{user_id}".
    // Example:
    // {
    //   "mailto:[email protected]/x": {
    //     "msg/receive": [{}],
    //     "msg/send": [{ draft: true }, { publish: true, topic: ["foo"] }]
    //   }
    // }
    requiredCapabilities: Capabilities,
    // Required facts. If some facts are required. Optional.
    // To check for a field presence (with any value), use "*".
    // Also, fact fields can be used for replacement in "requiredCapabilities",
    // with "{var}" syntax. For example: "user/{user_id}".
    // Example:
    // {
    //   "user_id": "*"
    // }
    requiredFacts?: Object,
    // If UCAN to be verified doesn't contain some proofs embedded, need to 
    // provide them. Optional.
    knownTokens?: Array<String>
  }
): Promise<{
  // Capabilities allowed.
  capabilities: Capabilities,
  // Facts, if any. 
  facts?: Object,
  // CIDs array. After successfull verification, use this list to check for
  // revoked tokens.
  cids: Array<String>,
}>

Example:

import { verifyUcan } from "@myjoypin/node-ucan"

// const rootIssuer = ...
// const audience = ...

// will throw if doesn't pass checks
await verifyUcan(token, {
  rootIssuer,
  audience,
  requiredCapabilities: {
    "mailto:[email protected]": {
      "msg/receive": [{}],
      "msg/send": [{ draft: true }, { publish: true, topic: ["foo"] }]
    }
  },
  requiredFacts: {
    a: "b"
  }
})

console.log('Access granted')

Verification semantics

  1. Resource: ":"

    The resource is an URL-like path to resource (it can be a real URL). "scheme" is any application specific scheme, like "api", "docs". "path" is an URL-like path, like "user/1", "user/1/post/2". The path includes access to all its sub-paths, for example "user/1" includes any of "user/1/post/1", "user/1/post/2", etc. Verification is performed by first by comparing schemes, then comparing each part of paths between "/" sequentally. The special path "*" means all in a capability, and "some" in the requirement resource, for example the requirement "user/ *" allows "user/1", compared to the requirement "user" which doesn't allow it (as in this context "user" means all users, but "user/ *" means some user, but not all users).

    Examples:

    | Capability resource | Required resource | Includes | | ------------------- | ----------------- | ---------------------------- | | user | user/1 | Yes | | user/1 | user | No (required is higher) | | user/1 | user/1 | Yes (are equal) | | user/1 | user/1/doc/1 | Yes (required is included) | | user/1 | user/2 | No (are not equal) | | user/1 | doc/1 | No (are not equal) | | * | user/1 | Yes (requred is included) | | user/1 | * | No (reqired is higher level) | | user/1 | user/ * | Yes | | user/ * | user/1 | Yes | | user/1/post/1 | user/ * /post/2 | No |

  2. Ability: "/ability[/sub-ability]"

    The ability is an action allowed for the resource. Its format is "namespace/ability[/sub-ability]". The special ability "*" always means "all" (in difference to its meaning for resource). It can be used at the end to include all sub-actions ("user/post/ *"), but not in the middle.

    Examples:

    | Capability ability | Required ability | Enables | | ------------------ | ---------------- | ------- | | user/post | user/post | Yes | | user/post | user/post/draft | Yes | | user/post/draft | user/post | No | | * | user/post | Yes | | user/post | * | No | | user/ * | user/post | Yes | | user/post | user/ * | No |

  3. Caveats: "[{: }[, {}, ...]]"

To be described...

Examples

See the examples folder.

License

This project is licensed under the MIT license.