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

sign-proof

v0.2.0

Published

Lightweight TypeScript library to sign and verify structured data (JSON, text, Buffer) with Ed25519 signatures and optional schema validation.

Readme

📦 sign-proof

A lightweight TypeScript library for cryptographically signing and verifying JSON, text, and binary data using Ed25519 signatures.

sign-proof canonicalizes JSON deterministically, hashes the normalized payload with SHA-256, and signs the resulting hash together with proof metadata. Binary payloads such as Buffer and Uint8Array are processed as raw bytes without UTF-8 conversion.

Works in both Node.js and modern browsers.

Features

  • Ed25519 digital signatures with tweetnacl
  • Deterministic canonical JSON serialization before hashing/signing
  • SHA-256 hashing before signature generation
  • Raw binary support for Buffer and Uint8Array
  • JSON Schema validation with ajv
  • Includes timestamp (signedAt) and mimeType in the signed proof metadata
  • Clean API for signing and verifying JSON, text, and binary data
  • Built-in SHA-256 hash utility
  • Compatible with both Node.js and modern browser environments

Installation

npm install sign-proof

How Signing Works

For JSON payloads, sign-proof first canonicalizes the object so that equivalent objects produce the same deterministic representation regardless of property insertion order.

The signing flow is:

payload
  ↓
canonicalize JSON (when applicable)
  ↓
SHA-256
  ↓
payload hash + signedAt + mimeType
  ↓
Ed25519 signature

This means the signature is created over a fixed-size SHA-256 digest rather than directly over the original payload.

For binary payloads (Buffer or Uint8Array), the original bytes are hashed directly without converting them to UTF-8 text.


Usage

Generate Key Pair

import { generateKeyPair } from 'sign-proof';

const { publicKey, privateKey } = generateKeyPair();

Sign JSON with Schema Validation

import {
    signData,
    verifyData,
    SignedData,
    JSONSchemaType,
} from 'sign-proof';

const schema: JSONSchemaType<{
    name: string;
    age: number;
}> = {
    type: 'object',
    properties: {
        name: { type: 'string' },
        age: { type: 'number' },
    },
    required: ['name', 'age'],
    additionalProperties: false,
};

const person = { name: 'Alice', age: 30 };
const privateKey = '...'; // base64
const publicKey = '...';  // base64

const proof = signData(person, privateKey, { schema });

const signed: SignedData = { payload: person, proof };

const isValid = verifyData(signed, publicKey);
console.log('Verified?', isValid);

Because JSON is canonicalized before hashing, equivalent objects can be verified even if their property order differs:

const original = {
    name: 'Alice',
    age: 30,
};

const proof = signData(original, privateKey);

const signed: SignedData = {
    payload: {
        age: 30,
        name: 'Alice',
    },
    proof,
};

console.log(verifyData(signed, publicKey)); // true

Sign Plain Text

import { signData, verifyData, SignedData } from 'sign-proof';

const text = 'Hello World!';

const privateKey = '...'; // base64
const publicKey = '...';  // base64

const proof = signData(text, privateKey);

const signed: SignedData = { payload: text, proof };

const isValid = verifyData(signed, publicKey);
console.log('Verified?', isValid);

Sign Binary Data with Buffer

import { signData, verifyData, SignedData } from 'sign-proof';

const buffer = Buffer.from([0x00, 0xff, 0x42, 0x10]);

const privateKey = '...'; // base64
const publicKey = '...';  // base64

const proof = signData(buffer, privateKey);

const signed: SignedData = { payload: buffer, proof };

const isValid = verifyData(signed, publicKey);
console.log('Verified?', isValid);

Binary data is hashed as raw bytes and is not converted to UTF-8 before signing or verification.


Sign Binary Data with Uint8Array

import { signData, verifyData, SignedData } from 'sign-proof';

const bytes = new Uint8Array([0x00, 0xff, 0x42, 0x10]);

const proof = signData(bytes, privateKey);

const signed: SignedData = {
    payload: bytes,
    proof,
};

console.log(verifyData(signed, publicKey)); // true

This is useful when working with browser APIs such as File, Blob, and ArrayBuffer:

const file = input.files?.[0];

if (file) {
    const bytes = new Uint8Array(await file.arrayBuffer());
    const proof = signData(bytes, privateKey);
}

Hashing Utility

import { hashData } from 'sign-proof';

const hash = hashData({ foo: 'bar' });
console.log('SHA256:', hash);

JSON objects are canonicalized before hashing, and binary inputs are hashed from their original bytes.


Deterministic JSON Signing

Object key order does not affect the resulting payload hash or signature.

For example, these payloads are treated as equivalent:

const first = {
    name: 'Alice',
    age: 30,
};

const second = {
    age: 30,
    name: 'Alice',
};

Canonicalization produces a stable JSON representation before SHA-256 hashing. Array order remains significant.


Proof Format

Each SignedData<T> consists of the original payload and its proof:

{
  payload: T;
  proof: {
    signature: string;   // base64-encoded Ed25519 signature
    signedAt: number;    // UNIX timestamp (ms)
    mimeType: string;    // inferred from payload
  };
}

The payload itself is not embedded into the signature. Instead, sign-proof derives a SHA-256 hash from the normalized payload and signs that hash together with the proof metadata.

During verification, the payload is normalized and hashed again. Verification succeeds only when the resulting digest and signed metadata reproduce a valid Ed25519 signature.


Technology Stack

  • TypeScript – Static typing and generics
  • tweetnacl – Ed25519 cryptographic signing and verification
  • ajv – JSON Schema validation
  • SHA-256 – Payload hashing before signature generation

License

MIT © 2025


Author

Made by Kyrylo Sotnykov · GitHub