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

@writermark/sdk

v0.6.4

Published

Writermark client SDK — human authorship verification for text editors

Readme

@writermark/sdk

Human-authorship certification for text editors. The SDK observes how users type and issues cryptographic certificates proving a human wrote the text. No text content is stored on the server.

Status: Alpha — this SDK is in early development. Breaking changes may occur before the official 1.0.0 release. Reach out at [email protected] if you need help getting started.

Install

npm install @writermark/sdk

Optional for DOCX verification support:

npm install jszip

Quick Start: React + TipTap

The fastest path. One hook, one component, ~15 lines of code.

import { useEditor, EditorContent } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import { useWritermark, CertIndicator } from '@writermark/sdk/react'

function MyEditor() {
  const editor = useEditor({ extensions: [StarterKit] })

  const { status, certificate, certifyNow } = useWritermark('doc-1', editor, {
    onCheckpoint: (checkpoint, coverage, pass, cert, map, checkpoints) => {
      localStorage.setItem('my-checkpoint', checkpoint)
      localStorage.setItem('my-checkpoints', JSON.stringify(checkpoints))
    },
    previousCheckpoint: localStorage.getItem('my-checkpoint'),
    previousCheckpoints: JSON.parse(localStorage.getItem('my-checkpoints') || 'null'),
  })

  return (
    <div>
      <EditorContent editor={editor} />
      <CertIndicator status={status} certificate={certificate} certifyNow={certifyNow} />
    </div>
  )
}

That's it. The hook handles the full certification lifecycle — collecting behavioral telemetry, sending it to the server every 30 seconds, and maintaining a signed checkpoint chain. The CertIndicator shows a colored dot with the current status and a hover panel with score details.

useWritermark options

| Prop | Type | Description | |------|------|-------------| | writermarkUrl | string? | API URL (defaults to https://api.writermark.org) | | onCheckpoint | function | Called after each certification with (checkpoint, coverage, pass, certificate, authorshipMap, checkpoints, vdfState) | | previousCheckpoint | string | null | Restore a saved checkpoint to continue certification across sessions | | previousCheckpoints | string[] | null | Rolling window of recent checkpoint JWTs (max 2). Falls back to previousCheckpoint if not provided. | | previousPass | boolean | Whether the previous checkpoint was passing | | previousAuthorshipMap | AuthorshipMap | null | Saved authorship map from a previous session | | previousVdfState | VdfState | null | VDF temporal proof state (not yet integrated) | | debug | boolean | Log certification details to console |

useWritermark return values

| Field | Type | Description | |-------|------|-------------| | status | 'idle' | 'certifying' | 'certified' | 'not-certified' | Current certification state | | coverage | number | null | Fraction of text covered by observed keystrokes (0–1) | | certificate | string | null | Signed attestation JWT when passing | | checkpoint | string | null | Latest signed checkpoint JWT | | certifyNow | () => Promise | Force an immediate certification cycle | | authorshipMap | AuthorshipMap | null | RLE array tracking provenance of each character | | isTracking | boolean | Whether the collector is attached and running |

TipTap without React

Use WritermarkSession — one constructor, full lifecycle handled for you. No manual fetch loops.

import { WritermarkSession } from '@writermark/sdk'

const session = new WritermarkSession(editor, {
  documentId: 'my-doc',
  onCheckpoint: (checkpoint, coverage, pass, cert, map, checkpoints) => {
    myDatabase.save({ checkpoint, checkpoints, authorshipMap: map })
  },
  onStatusChange: (status) => {
    console.log('certification status:', status)
  },
  previousCheckpoint: myDatabase.get('checkpoint'),
  previousCheckpoints: myDatabase.get('checkpoints'),
})

// Force immediate certification (e.g. before publish or export)
const result = await session.certifyNow()

// Clean up when done
session.destroy()

WritermarkSession handles event collection, compression, the 30-second certification loop, checkpoint chain management, clipboard enrichment on copy, and certified paste detection — all internally. Same class works for all editor types.

WritermarkSession options

| Option | Type | Description | |--------|------|-------------| | documentId | string | Persistent identifier for the document (required) | | writermarkUrl | string? | API URL (defaults to https://api.writermark.org) | | onCheckpoint | function | Called after each certification with (checkpoint, coverage, pass, certificate, authorshipMap, checkpoints, vdfState) | | onStatusChange | function | Called when status changes: 'idle' | 'certifying' | 'certified' | 'not-certified' | | onCertifyResult | function | Called with the full server response object after each certification | | previousCheckpoint | string | null | Restore a saved checkpoint to continue across sessions | | previousCheckpoints | string[] | null | Rolling window of recent checkpoint JWTs (max 2) | | previousPass | boolean | Whether the previous checkpoint was passing | | previousAuthorshipMap | AuthorshipMap | null | Saved authorship map from a previous session | | previousVdfState | VdfState | null | VDF temporal proof state (not yet integrated) | | getText | () => string | Custom text getter (auto-detected for TipTap, textarea, contenteditable) | | debug | boolean | Log certification details to console |

WritermarkSession methods

| Method | Returns | Description | |--------|---------|-------------| | certifyNow() | Promise<object | null> | Force an immediate certification cycle. Call before exporting or copying a certificate to ensure it matches the latest text. | | getStatus() | CertificationStatus | Current certification state | | getCertificate() | string | null | Latest attestation JWT | | getCheckpoint() | string | null | Latest checkpoint JWT | | getCheckpoints() | string[] | Full rolling checkpoint window | | getVdfState() | VdfState | null | Current VDF state (not yet integrated) | | getAuthorshipMap() | AuthorshipMap | null | Current authorship map | | getCoverage() | number | null | Keystroke coverage fraction (0–1) | | isActive() | boolean | Whether the session is running | | destroy() | void | Stop the session and clean up all listeners |

Generic DOM (textarea / contenteditable)

Pass any <textarea>, <input>, or contenteditable element. The session auto-detects the element type.

import { WritermarkSession } from '@writermark/sdk'

const session = new WritermarkSession(
  document.querySelector('#my-editor'),
  {
    documentId: 'my-doc',
    onCheckpoint: (checkpoint, coverage, pass) => {
      localStorage.setItem('checkpoint', checkpoint)
    },
  }
)

session.destroy()

Note: clipboard enrichment on copy (certified paste provenance) requires TipTap.

Browser Script Tag

For non-bundled environments, load the SDK as a script tag. Everything is available on window.Writermark.

<script src="https://writermark.org/sdk.js"></script>
<script>
  var session = new Writermark.WritermarkSession(
    document.querySelector('#my-editor'),
    {
      documentId: 'my-doc',
      onCheckpoint: function(checkpoint) {
        localStorage.setItem('wm-checkpoint', checkpoint)
      },
    }
  )
</script>

Verification

The SDK includes a complete verification toolkit.

Verify a file (golden path)

Hand it a File object from an <input type="file"> or drag-and-drop. Supports .wtxt, .docx, .rtf, .md, .txt, and .html.

import { verifyFile } from '@writermark/sdk'

const input = document.querySelector('input[type="file"]')
input.addEventListener('change', async () => {
  const result = await verifyFile(input.files[0])

  if (result.valid) {
    console.log('Verified!', result.score, result.confidence)
  } else {
    console.log('Failed:', result.detail)
  }
})

DOCX support requires JSZip as an optional dependency.

Verify text + token

import { verify } from '@writermark/sdk'

const result = await verify({
  token: 'eyJhbGciOi...',   // raw JWT or full certificate text
  text: 'The original document text...',
})

console.log(result.valid)         // true/false
console.log(result.signatureOnly) // false (text was checked too)
console.log(result.score)         // 0.82
console.log(result.confidence)    // 0.88

Verify token only (signature check)

const result = await verify({ token: certificateText })
// result.signatureOnly === true

Extract without verifying

import { extractFromFile } from '@writermark/sdk'

const { text, token, fileType } = await extractFromFile(file)
// fileType: 'wtxt' | 'docx' | 'rtf' | 'md' | 'txt' | 'html'

VerifyResult

| Field | Type | Description | |-------|------|-------------| | valid | boolean | Whether the certificate is valid | | pass | boolean? | Whether the document passed certification | | score | number? | Human-authorship score (0–1) | | confidence | number? | Confidence level (0–1) | | issuedAt | string? | ISO timestamp of certification | | signatureOnly | boolean | True if only the signature was verified (no text hash check) | | detail | string? | Human-readable detail on failure |

verifyFile() also returns text, token, and fileType on the result object.

Custom Editor (Manual API)

For editors that aren't TipTap or standard DOM elements, use the Collector directly. Call record* methods from your editor's event handlers.

import { Collector } from '@writermark/sdk'

const collector = new Collector()
collector.start()

collector.recordKey('KeyA', cursorPosition)
collector.recordKeyUp('KeyA')
collector.recordBackspace('Backspace', cursorPosition)
collector.recordEnter('Enter', cursorPosition)
collector.recordPaste(charCount, pastedText, 'external', cursorPosition)
collector.recordCopyOrCut(selectedText, copyStart, copyLength, isCut)
collector.recordCursorJump(distance)
collector.recordSelect(selectionLength)
collector.recordUndo()
collector.recordRedo()
collector.recordFocus()
collector.recordBlur()
collector.recordScroll(deltaY)
collector.recordMouse(xRatio, yRatio)  // 0-1 normalized
collector.recordVisibility(isVisible)
collector.recordMutation(position, deleteLength, insertLength, 'typed')

The recordMutation method is key — it tracks document changes for the authorship map. The insertSource parameter should be 'typed', 'paste-internal', 'paste-external', 'paste-certified', 'undo', or 'redo'.

API Reference

All API endpoints are served from https://api.writermark.org.

POST /certify

The main certification endpoint. Stateless — all state lives in the signed checkpoint. Rate limit: 30/min per IP.

Request body:

| Field | Type | Description | |-------|------|-------------| | documentId | string | Persistent document identifier (required on first call) | | events | EditorEvent[] | New events since last checkpoint | | checkpoints | string[] | Rolling window of recent checkpoint JWTs (max 2). Empty array on first call. | | recentEvents | EditorEvent[] | Events from the previous window (for ML context). Empty on first call. | | merkleRoot | string | null | Merkle root of document chunks | | authorshipMap | AuthorshipMap | null | RLE authorship intervals | | textHash | string | SHA-256 of normalized text | | charCount | number | Current text length | | vdfState | VdfState | null | VDF temporal proof state (not yet integrated) |

The daemon also accepts legacy fields checkpoint (singular) and contentMerkleRoot for backward compatibility.

Response:

{
  "checkpoint": "eyJhbGciOi...",
  "score": 0.72,
  "behavioralScore": 0.85,
  "coverage": 0.95,
  "pass": true,
  "certificate": "eyJhbGciOi...",
  "authorshipMap": [[0, 150, "human_evidenced"]],
  "confidence": 0.88,
  "activeWritingTimeMs": 120000,
  "revisionPercent": 8.5
}

POST /verify

Verify a certificate's signature. Body: { "token": "eyJ..." }

POST /verify-text

Verify a certificate matches specific text. Body: { "text": "...", "token": "eyJ..." }

POST /derive

Create a standalone certificate for an excerpt (e.g. copy-paste certification). Requires a valid source JWT and Merkle proofs for the excerpt's chunks.

GET /public-key

Returns the Ed25519 public key for verifying attestation JWTs.

{
  "publicKey": {
    "kty": "OKP",
    "crv": "Ed25519",
    "x": "..."
  }
}

GET /.well-known/jwks.json

Standard JWKS endpoint for public key discovery. Key ID: writermark-v1.

Components

CertIndicator

Drop-in React component showing a colored status dot, label, and hover popup with certificate details.

import { CertIndicator } from '@writermark/sdk/react'

<CertIndicator
  status={status}
  certificate={certificate}
  certifyNow={certifyNow}
/>

| Prop | Type | Description | |------|------|-------------| | status | CertificationStatus | From useWritermark | | certificate | string | null | Attestation JWT from useWritermark | | certifyNow | () => Promise? | If provided, the indicator runs an immediate certification cycle before copying or viewing. Pass certifyNow from useWritermark. | | writermarkUrl | string? | Server URL for "View certificate" link (defaults to writermark.org) | | className | string? | CSS class override for the wrapper element |

Links

License

MIT