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

@alexalves87/opentimestamps-client

v1.0.5

Published

Official client SDK for OpenTimestamps calendars with resilience patterns

Readme

OpenTimestamps Client SDK

Official TypeScript/JavaScript client for OpenTimestamps with enterprise-grade resilience patterns

npm version License: MIT TypeScript Node.js

Features

( Complete OpenTimestamps Operations

  • stamp() - Create timestamp proofs by submitting to calendar servers
  • upgrade() - Query calendars for Bitcoin confirmations
  • verify() - Verify proofs against the Bitcoin blockchain

=� Enterprise-Grade Resilience

  • Circuit Breaker - Isolate failures per calendar (prevents cascading failures)
  • Exponential Backoff - Configurable retry strategies with jitter
  • Timeout Management - Per-attempt and total operation timeouts
  • Threshold-based Submissions - Require minimum successful submissions (default: 2/4 calendars)

=' Developer Experience

  • TypeScript First - Full type safety with strict mode enabled
  • Multi-Runtime - Works in Node.js 18+, browsers, and edge runtimes
  • Tree-Shakeable - Dual ESM/CJS build with zero dependencies (except core OTS library)
  • Abort Support - Native AbortController integration for all operations
  • Observable - Optional logger interface for monitoring and debugging

Installation

npm install @alexalves87/opentimestamps-client

Or with other package managers:

yarn add @alexalves87/opentimestamps-client
pnpm add @alexalves87/opentimestamps-client

Quick Start

Create a Timestamp

import { OpenTimestampsClient } from '@alexalves87/opentimestamps-client'
import { createHash } from 'crypto'
import { readFileSync } from 'fs'

// Initialize client (uses default OpenTimestamps calendars)
const client = new OpenTimestampsClient()

// Hash your file
const fileContent = readFileSync('document.pdf')
const hash = createHash('sha256').update(fileContent).digest()

// Create timestamp proof
const otsProof = await client.stamp(hash)

// Save the .ots file
writeFileSync('document.pdf.ots', otsProof)

Upgrade to Bitcoin Confirmation

// Later, query calendars for Bitcoin confirmation
const upgradedProof = await client.upgrade(otsProof)

if (upgradedProof !== otsProof) {
  console.log('Proof upgraded with Bitcoin confirmation!')
  writeFileSync('document.pdf.ots', upgradedProof)
}

Verify a Timestamp

// Verify the timestamp proof
const result = await client.verify(upgradedProof, hash)

if (result.valid) {
  console.log(` Timestamp verified!`)
  console.log(`   Block: ${result.blockHeight}`)
  console.log(`   Time:  ${result.timestamp}`)
} else {
  console.error(`L Verification failed: ${result.error}`)
}

Advanced Usage

Custom Calendars

const client = new OpenTimestampsClient({
  calendars: [
    'https://alice.btc.calendar.opentimestamps.org',
    'https://bob.btc.calendar.opentimestamps.org',
    'https://finney.calendar.eternitywall.com',
  ],
  minimumSuccessfulSubmissions: 2, // Require 2/3 calendars to succeed
})

Resilience Configuration

const client = new OpenTimestampsClient({
  resilience: {
    // Timeout settings
    timeout: {
      totalMs: 30000,      // 30s total operation timeout
      perAttemptMs: 5000,  // 5s per attempt timeout
    },

    // Retry configuration
    retries: {
      enabled: true,
      maxAttempts: 3,
      backoff: {
        strategy: 'exponential', // 'exponential' | 'linear' | 'constant'
        initialDelayMs: 1000,
        maxDelayMs: 10000,
        jitter: 'full',  // 'full' | 'equal' | 'none'
      },
    },

    // Circuit breaker settings
    circuitBreaker: {
      enabled: true,
      failureThreshold: 5,        // Open circuit after 5 failures
      recoveryTimeoutMs: 15000,   // Try recovery after 15s
      halfOpenMaxAttempts: 1,     // Test with 1 request when half-open
    },
  },
})

Cancellation Support

const controller = new AbortController()

// Cancel operation after 5 seconds
setTimeout(() => controller.abort(), 5000)

try {
  const proof = await client.stamp(hash, { signal: controller.signal })
} catch (error) {
  if (error.name === 'AbortError') {
    console.log('Operation cancelled')
  }
}

Monitoring with Logger

const logger = {
  debug: (msg) => console.log(`[DEBUG] ${msg}`),
  info: (msg) => console.log(`[INFO] ${msg}`),
  warn: (msg) => console.warn(`[WARN] ${msg}`),
  error: (msg) => console.error(`[ERROR] ${msg}`),
}

const client = new OpenTimestampsClient({ logger })

// Monitor circuit breaker state
const state = client.getCircuitState('https://alice.btc.calendar.opentimestamps.org')
console.log(`Circuit state: ${state}`) // 'CLOSED' | 'OPEN' | 'HALF_OPEN'

// Manually reset circuit if needed
client.resetCircuit('https://alice.btc.calendar.opentimestamps.org')

API Reference

OpenTimestampsClient

Constructor Options

interface ClientOptions {
  // Calendar servers to use (defaults to official OpenTimestamps calendars)
  calendars?: string[]

  // Minimum number of successful submissions required for stamp()
  minimumSuccessfulSubmissions?: number // default: 2

  // Resilience configuration (timeout, retry, circuit breaker)
  resilience?: ResilienceOptions

  // Optional logger for observability
  logger?: Logger

  // Global AbortSignal for all operations
  signal?: AbortSignal
}

Methods

stamp(hash, options?)

Create a timestamp by submitting to calendar servers.

async stamp(
  hash: Buffer | string,  // SHA-256 hash (32 bytes or 64 hex chars)
  options?: OperationOptions
): Promise<Buffer>  // .ots proof with pending attestations

Throws:

  • ValidationError - Invalid hash format
  • StampError - Insufficient successful submissions
  • NetworkError - Network/timeout errors
upgrade(proof, options?)

Query calendars for Bitcoin confirmation.

async upgrade(
  proof: Buffer,  // Existing .ots proof
  options?: OperationOptions
): Promise<Buffer>  // Upgraded .ots proof (or original if no upgrade available)

Throws:

  • ValidationError - Invalid proof format
  • UpgradeError - Upgrade operation failed
verify(proof, hash, options?)

Verify timestamp proof against Bitcoin blockchain.

async verify(
  proof: Buffer,    // .ots proof file
  hash: Buffer | string,  // Original file hash
  options?: OperationOptions
): Promise<VerificationResult>

interface VerificationResult {
  valid: boolean
  blockHeight?: number
  timestamp?: Date
  error?: string
}
getCircuitState(calendar)

Get current circuit breaker state for a calendar.

getCircuitState(calendar: string): 'CLOSED' | 'OPEN' | 'HALF_OPEN' | undefined
resetCircuit(calendar)

Manually reset circuit breaker for a calendar.

resetCircuit(calendar: string): void

Architecture

The SDK implements a layered resilience architecture:

�������������������������������������
   Public API (stamp/upgrade/verify) 
����������������,��������������������
                 
�������������������������������������
   Orchestration Layer                
   - Validation                       
   - Parallel/Sequential coordination 
����������������,��������������������
                 
�������������������������������������
   Resilient Network Layer            
   - Timeout (total + per-attempt)    
   - Retry (exponential backoff)      
   - Circuit Breaker (per-calendar)   
����������������,��������������������
                 
�������������������������������������
   Fetch Adapter (multi-runtime)     
�������������������������������������

Key Design Decisions

  • Per-Calendar Circuit Breakers: Each calendar has independent failure tracking, so one failing calendar doesn't affect others
  • Threshold-based Submissions: stamp() requires N/M calendars to succeed (default: 2/4), providing resilience through redundancy
  • Sequential Upgrade Queries: upgrade() stops at the first confirmed calendar, minimizing unnecessary API calls
  • 4xx = Fail Fast, 5xx = Retry: HTTP semantics are respected for efficient error handling

Error Handling

The SDK provides detailed error context:

try {
  await client.stamp(hash)
} catch (error) {
  if (error instanceof StampError) {
    console.log(`${error.successfulSubmissions.length} calendars succeeded`)
    console.log(`${error.failedSubmissions.length} calendars failed`)

    error.failedSubmissions.forEach(({ calendar, error }) => {
      console.error(`${calendar}: ${error.message}`)
    })
  }
}

Browser Usage

The SDK works in browsers with native fetch support:

<script type="module">
  import { OpenTimestampsClient } from '@alexalves87/opentimestamps-client'

  const client = new OpenTimestampsClient()

  // Hash from file input
  const file = document.querySelector('input[type="file"]').files[0]
  const buffer = await file.arrayBuffer()
  const hashBuffer = await crypto.subtle.digest('SHA-256', buffer)
  const hash = Array.from(new Uint8Array(hashBuffer))
    .map(b => b.toString(16).padStart(2, '0'))
    .join('')

  // Create timestamp
  const proof = await client.stamp(hash)
  console.log('Timestamp created!', proof)
</script>

Testing

The SDK has comprehensive test coverage:

# Run tests
npm test

# Run tests with coverage
npm test -- --coverage

# Run tests in watch mode
npm run test:watch

Test Stats:

  • 83+ tests (unit + integration)
  • 80%+ code coverage
  • MSW-based integration tests with realistic scenarios
  • Property-based testing with fast-check

Development

# Install dependencies
npm install

# Build (ESM + CJS)
npm run build

# Development watch mode
npm run dev

# Lint
npm run lint

# Format
npm run format

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

Security

For security issues, please see SECURITY.md.

License

MIT � alexalves87


FAQ

How does this differ from the core opentimestamps library?

The core @alexalves87/opentimestamps library handles OTS file format serialization/deserialization and cryptographic operations. This client SDK adds:

  • High-level API for calendar operations
  • Enterprise resilience patterns (circuit breaker, retry, timeout)
  • Multi-calendar coordination
  • TypeScript-first developer experience

Why require 2/4 calendars by default?

Requiring multiple successful submissions provides resilience:

  • Protects against single calendar outages
  • Ensures timestamp validity even if some calendars are compromised
  • Default threshold (2/4) balances reliability vs. speed

Can I use my own calendar servers?

Yes! Just pass custom calendars to the constructor:

const client = new OpenTimestampsClient({
  calendars: ['https://my-calendar.example.com'],
  minimumSuccessfulSubmissions: 1,
})

How do I handle offline scenarios?

Use timeout configuration and catch network errors:

const client = new OpenTimestampsClient({
  resilience: {
    timeout: { totalMs: 5000 },  // Fail fast
    retries: { enabled: false },  // Don't retry
  },
})

try {
  await client.stamp(hash)
} catch (error) {
  // Store hash locally for retry later
  await queueForLater(hash)
}

Is this production-ready?

Yes! The SDK is designed for production use with:

  • Comprehensive test coverage (83+ tests)
  • Battle-tested resilience patterns
  • Semantic versioning
  • Clear error messages and debugging support

Links

Acknowledgments

Built with the OpenTimestamps protocol created by Peter Todd.