@usewayn/server
TypeScript icon, indicating that this package has built-in type declarations

0.0.2 • Public • Published

@usewayn/server

A proof-of-work challenge system for rate limiting and bot protection. This SDK provides a server-side implementation for creating cryptographic challenges that clients must solve to prove computational work.

Installation

npm install @usewayn/server # NodeJS
bun install @usewayn/server # Bun

Quick Start

import Wayn from '@usewayn/server';

// Create a new Wayn instance
const wayn = new Wayn({
  stateless: true, // Optional: enables JWT authentication (default: true)
  jwtSecret: 'ReallySecretKey29!', // Optional: defaults to random bytes
});

// Create a challenge for the client
const challenge = wayn.createChallenge({
  challengeCount: 5,       // Number of challenges
  challengeSize: 32,       // Size of each challenge
  challengeDifficulty: 5,  // Difficulty level
  expiresMs: 300000,       // Expiration time (5 minutes)
});

// Validate a solution from the client
const result = await wayn.redeemChallenge({
  token: challenge.token,
  solutions: clientSolutions, // Array of [salt, target, nonce] tuples
});

if (result.success) {
  // Challenge solved successfully
  const validationResult = await wayn.validateToken(result.token);
  console.log('Token is valid:', validationResult.success);
}

API Reference

Constructor

new Wayn(config?: Partial<WaynConfig>)

Creates a new Wayn instance with optional configuration.

Parameters:

  • config.jwtSecret (string): Secret key for JWT token signing. Defaults to random bytes.
  • config.stateless (boolean): Whether to use JWT tokens (true) or in-memory tokens (false). Default: true.
  • config.state (ChallengeState): Initial state for challenges and tokens. Only used when stateless is false.

Methods

createChallenge(config?: ChallengeConfig)

Creates a new proof-of-work challenge for clients to solve.

Parameters:

  • config.challengeCount (number): Number of individual challenges. Default: 50
  • config.challengeSize (number): Size of each challenge string in characters. Default: 32
  • config.challengeDifficulty (number): Number of leading zeros required in hash. Default: 4
  • config.expiresMs (number): Challenge expiration time in milliseconds. Default: 600000 (10 minutes)
  • config.store (boolean): Whether to store the challenge server-side. Default: true

Returns:

{
  challenge: Array<[string, string]>, // Array of [salt, target] tuples
  token?: string,                     // Challenge token (if stored)
  expires: number                     // Expiration timestamp
}

Example:

const challenge = wayn.createChallenge({
  challengeCount: 5,
  challengeDifficulty: 3,
  expiresMs: 300000, // 5 minutes
});

// Send challenge.challenge to client
// Store challenge.token for validation

redeemChallenge(solution: Solution)

Validates a client's solution to a previously created challenge.

Parameters:

{
  token: string,                           // Challenge token
  solutions: Array<[string, string, number]> // Array of [salt, target, nonce] tuples
}

Returns:

{
  success: boolean,
  message?: string,  // Error message if success is false
  token?: string,    // Verification token if success is true
  expires?: number   // Token expiration timestamp
}

Example:

const result = await wayn.redeemChallenge({
  token: challengeToken,
  solutions: [
    ['salt1', 'target1', 12345],
    ['salt2', 'target2', 67890],
    // ... more solutions
  ]
});

if (result.success) {
  // Store result.token for future validations
  console.log('Challenge solved! Token expires at:', new Date(result.expires));
} else {
  console.error('Challenge failed:', result.message);
}

validateToken(token: string, config?: TokenConfig)

Validates a previously issued verification token.

Parameters:

  • token (string): The verification token to validate
  • config.keepToken (boolean): Whether to keep the token valid after validation (only for non-stateless mode). Default: false

Returns:

{
  success: boolean,
  payload?: JWTPayload  // JWT payload (only in stateless mode)
}

Example:

const validation = await wayn.validateToken(userToken);

if (validation.success) {
  if (validation.payload) {
    // Stateless mode - JWT token
    console.log('User ID:', validation.payload.id);
    console.log('Challenge solved at:', new Date(validation.payload.solvedAt * 1000));
    console.log('Average solution time:', validation.payload.avgSolutionTime, 'ms');
  }
  // Allow user to proceed
} else {
  // Token is invalid or expired
}

Configuration Options

Challenge Configuration

Option Type Default Description
challengeCount number 50 Number of proof-of-work challenges to generate
challengeSize number 32 Length of each challenge salt in characters
challengeDifficulty number 4 Number of leading zeros required in SHA-256 hash
expiresMs number 600000 Challenge expiration time in milliseconds (10 min)
store boolean true Whether to store challenge server-side for validation

Token Configuration

Option Type Default Description
keepToken boolean false Keep token valid after validation (non-stateless only)

Usage Patterns

Rate Limiting

const wayn = new Wayn();

// Create challenge with higher difficulty for rate limiting
const challenge = wayn.createChallenge({
  challengeCount: 20,
  challengeDifficulty: 5, // Requires more computation
  expiresMs: 300000,      // 5 minutes
});

// Client must solve challenge before making API calls

Bot Protection

const wayn = new Wayn();

// Lighter challenge for bot protection
const challenge = wayn.createChallenge({
  challengeCount: 3,
  challengeDifficulty: 3,
  expiresMs: 120000, // 2 minutes
});

// Validate before allowing form submissions

Stateless vs Stateful Mode

Stateless Mode (Recommended):

  • Uses JWT tokens
  • No server-side storage required
  • Scales horizontally
  • Includes metadata about solution performance
const wayn = new Wayn({ stateless: true });

Stateful Mode:

  • Stores tokens in memory
  • Requires server-side state management
  • Better for single-instance deployments
const wayn = new Wayn({ stateless: false });

Security Considerations

  1. JWT Secret: Use a strong, randomly generated secret for JWT signing in production
  2. Challenge Difficulty: Higher difficulty provides better protection but increases client computation time
  3. Expiration Times: Set appropriate expiration times based on your use case
  4. Token Validation: Always validate tokens before allowing protected operations

TypeScript Support

The SDK is written in TypeScript and includes full type definitions:

import Wayn, { 
  WaynConfig, 
  ChallengeConfig, 
  Solution, 
  JWTPayload 
} from '@usewayn/server';

This project was created using bun init in bun v1.2.0. Bun is a fast all-in-one JavaScript runtime.

/@usewayn/server/

    Package Sidebar

    Install

    npm i @usewayn/server

    Weekly Downloads

    0

    Version

    0.0.2

    License

    Apache-2.0

    Unpacked Size

    16.1 kB

    Total Files

    4

    Last publish

    Collaborators

    • islemci