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

cloudkit-sign

v0.1.0

Published

Minimal, well-typed CloudKit server-to-server request signing for Node.js.

Downloads

35

Readme

cloudkit-sign

Minimal, well-typed CloudKit server-to-server request signing for Node.js.

Apple's CloudKit Web Services docs describe the signing flow, but the details are easy to get subtly wrong: hash the body first, remove milliseconds from the date, sign the URL subpath instead of the full origin, and include query parameters. This package keeps that small ritual in one tested place.

Install

npm install cloudkit-sign

Usage

import { readFileSync } from "node:fs";
import { cloudKitPath, signCloudKitRequest } from "cloudkit-sign";

const privateKey = readFileSync("./eckey.pem", "utf8");
const path = cloudKitPath({
  container: "iCloud.com.example.app",
  environment: "development",
  database: "public",
  operation: "records/query",
});

const body = {
  query: {
    recordType: "Note",
  },
};

const signed = signCloudKitRequest({
  keyId: process.env.CLOUDKIT_KEY_ID!,
  privateKey,
  url: path,
  body,
});

const response = await fetch(`https://api.apple-cloudkit.com${path}`, {
  method: "POST",
  headers: {
    "content-type": "application/json",
    ...signed.headers,
  },
  body: signed.body,
});

if (!response.ok) {
  throw new Error(await response.text());
}

API

signCloudKitRequest(options)

Returns the CloudKit authentication headers plus helpful debug fields.

const signed = signCloudKitRequest({
  keyId: "your-cloudkit-key-id",
  privateKey: "-----BEGIN EC PRIVATE KEY-----\n...",
  url: "/database/1/iCloud.com.example.app/development/public/users/current",
  body: "",
});

The returned headers object contains:

{
  "X-Apple-CloudKit-Request-KeyID": string;
  "X-Apple-CloudKit-Request-ISO8601Date": string;
  "X-Apple-CloudKit-Request-SignatureV1": string;
}

cloudKitPath(options)

Builds a CloudKit database URL subpath:

cloudKitPath({
  container: "iCloud.com.example.app",
  environment: "production",
  database: "public",
  operation: "records/lookup",
});

Important Details

  • Generate the EC key with openssl ecparam -name prime256v1 -genkey -noout -out eckey.pem.
  • Upload the public key to CloudKit Dashboard and use the generated Key ID.
  • The signed date is valid for 10 minutes, so your server clock needs to be accurate.
  • For GET requests, pass no body or an empty string.
  • For JSON bodies, this package signs the exact JSON.stringify output it returns as signed.body. If you need a custom serializer, pass your already-serialized string or bytes.
  • Sign the CloudKit URL subpath, not https://api.apple-cloudkit.com.
  • Query parameters are part of the signature and must be included.

Common Operations

cloudkit-sign does not wrap CloudKit itself. It signs any CloudKit Web Services request, so you can use it with the operations Apple exposes today and any new ones they add later.

Pass one of these values as operation to cloudKitPath():

// Records
"records/lookup";
"records/query";
"records/modify";
"records/changes";
"records/resolve";

// Zones
"zones/list";
"zones/lookup";
"zones/modify";
"zones/changes";

// Subscriptions
"subscriptions/list";
"subscriptions/lookup";
"subscriptions/modify";

// Users
"users/current";
"users/lookup";
"users/lookup/email";
"users/lookup/phone";

You can also pass a leading slash if you prefer:

cloudKitPath({
  container: "iCloud.com.example.app",
  environment: "production",
  database: "public",
  operation: "/records/modify",
});

References

License

MIT