A lightweight JavaScript SDK for parsing, validating, and signing DP-1 playlists in both Node.js and browser environments.
dp1-js provides the foundation for DP-1 tooling and FF1 apps to verify playlist structure and optionally sign playlists with Ed25519 private keys. It creates detached signatures compatible with DP-1's signatures[] evolution, enabling secure and verifiable digital display protocols.
- Parse & Validate - Parse JSON and validate DP-1 playlist structure with detailed error reporting
- Sign & Verify - Create and verify Ed25519 signatures using RFC 8785 JSON canonicalization
- Universal - Works in both Node.js (22+) and modern browsers
- Type-Safe - Full TypeScript support with comprehensive type definitions
- Standards Compliant - Implements DP-1 specification with RFC 8785 canonicalization
npm install dp1-jsOr use the library directly in the browser via CDN:
Using ES Modules (Modern Browsers):
<script type="module">
  import {
    parseDP1Playlist,
    signDP1Playlist,
    verifyPlaylistSignature,
  } from 'https://cdn.jsdelivr.net/npm/dp1-js/dist/index.js';
  // Use the functions
  const result = parseDP1Playlist(jsonData);
</script>import { parseDP1Playlist } from 'dp1-js';
// Parse and validate playlist JSON
const result = parseDP1Playlist(jsonData);
if (result.error) {
  console.error('Validation failed:', result.error.message);
  // Access detailed error information
  result.error.details?.forEach(detail => {
    console.error(`  ${detail.path}: ${detail.message}`);
  });
} else {
  console.log('Valid playlist:', result.playlist);
}import { signDP1Playlist } from 'dp1-js';
const playlist = {
  dpVersion: '1.0.0',
  id: 'playlist-123',
  slug: 'my-playlist',
  title: 'My Playlist',
  items: [
    {
      id: 'item-1',
      title: 'Artwork 1',
      source: 'https://example.com/artwork1.html',
      duration: 30,
      license: 'open',
      created: '2025-01-01T00:00:00Z',
    },
  ],
};
// Sign with Ed25519 private key (as hex string or Uint8Array)
const signature = await signDP1Playlist(
  playlist,
  privateKeyHex // or privateKeyBytes as Uint8Array
);
console.log('Signature:', signature);
// Output: "ed25519:0x<hex_signature>"
// Add signature to playlist
const signedPlaylist = {
  ...playlist,
  signature,
};import { verifyPlaylistSignature } from 'dp1-js';
// Playlist with signature
const signedPlaylist = {
  dpVersion: '1.0.0',
  id: 'playlist-123',
  slug: 'my-playlist',
  title: 'My Playlist',
  items: [
    {
      id: 'item-1',
      title: 'Artwork 1',
      source: 'https://example.com/artwork1.html',
      duration: 30,
      license: 'open',
      created: '2025-01-01T00:00:00Z',
    },
  ],
  signature: 'ed25519:0x...',
};
// Verify with Ed25519 public key (Uint8Array)
const isValid = await verifyPlaylistSignature(signedPlaylist, publicKeyBytes);
if (isValid) {
  console.log('✓ Signature is valid');
} else {
  console.log('✗ Signature verification failed');
}Parses and validates playlist data from unknown JSON input.
Parameters:
- json- Unknown JSON data to parse and validate
Returns: DP1PlaylistParseResult object containing either:
- playlist- The validated- Playlistobject (if successful)
- error- Detailed error information (if validation failed)- type:- "invalid_json"|- "validation_error"
- message: Human-readable error message
- details: Array of specific validation errors with paths
 
Example:
const result = parseDP1Playlist(data);
if (result.playlist) {
  // Use validated playlist
  console.log(result.playlist.title);
}signDP1Playlist(playlist: Omit<Playlist, "signature">, privateKey: Uint8Array | string): Promise<string>
Signs a playlist using Ed25519 as per DP-1 specification.
Parameters:
- playlist- Playlist object without signature field
- privateKey- Ed25519 private key as hex string or Uint8Array
Returns: Promise resolving to signature string in format "ed25519:0x<hex>"
Example:
const sig = await signDP1Playlist(playlist, '0x1234...');Verifies a playlist's Ed25519 signature using the provided public key.
Parameters:
- playlist- Playlist object with signature field
- publicKey- Ed25519 public key as Uint8Array (32 bytes)
Returns: Promise resolving to true if signature is valid, false otherwise
Example:
const isValid = await verifyPlaylistSignature(signedPlaylist, publicKeyBytes);
if (isValid) {
  console.log('Signature verified successfully');
}Note: The function returns false if:
- The playlist has no signature
- The signature format is invalid
- The signature doesn't match the playlist content
- The public key is invalid or doesn't match the private key used for signing
The library exports comprehensive TypeScript types for DP-1 playlists:
// Functions
import { parseDP1Playlist, signDP1Playlist, verifyPlaylistSignature } from 'dp1-js';
// Types
import type {
  Playlist,
  PlaylistItem,
  DisplayPrefs,
  Provenance,
  Repro,
  DP1PlaylistParseResult,
} from 'dp1-js';- Playlist- Complete playlist structure with metadata and items
- PlaylistItem- Individual item in a playlist
- DisplayPrefs- Display preferences for artwork rendering
- Provenance- On-chain or off-chain provenance information
- Repro- Reproduction and verification metadata
- DP1PlaylistParseResult- Result type from parsing operation
See types.ts for complete type definitions.
This package exposes small validation helpers that do not require consumers to import our internal schemas.
Available validators:
- validateDpVersion(version: string)→ ValidationResult
- validateDisplayPrefs(input: unknown)→ ValidationResult
- validateRepro(input: unknown)→ ValidationResult
- validateProvenance(input: unknown)→ ValidationResult
- validatePlaylistItem(input: unknown)→ ValidationResult
Usage examples:
import { validateProvenance } from 'dp1-js';
const provenance = {
  type: 'onChain',
  contract: { chain: 'evm', address: '0xabc', tokenId: '42' },
};
const res = validateProvenance(provenance);
if (!res.success) {
  console.error(res.error.message);
  res.error.issues.forEach(i => console.error(`${i.path}: ${i.message}`));
}ValidationResult shape:
type ValidationIssue = { path: string; message: string };
type ValidationResult =
  | { success: true }
  | { success: false; error: { message: string; issues: ValidationIssue[] } };You can also integrate these validators into your own schema library (e.g., Zod) via .refine or .superRefine to attach issues to your app's error format.
A valid DP-1 playlist includes:
{
  dpVersion: "1.0.0",           // DP-1 protocol version
  id: "unique-id",              // Unique playlist identifier
  slug: "url-friendly-slug",    // URL-friendly identifier
  title: "Playlist Title",      // Human-readable title
  created?: "ISO-8601-date",    // Optional creation timestamp
  defaults?: {                  // Optional default settings
    display?: {...},
    license?: "open" | "token" | "subscription",
    duration?: 30
  },
  items: [...],                 // Array of playlist items
  signature?: "ed25519:0x..."   // Optional Ed25519 signature
}npm installnpm run buildBuilds ESM, CJS, and TypeScript declaration files to dist/.
npm testnpm run lint- Node.js: 22+ (uses native node:cryptowith Ed25519 support)
- Browsers: Modern browsers with Web Crypto API support
- Parsing & Validation: Uses Zod schemas to validate playlist structure against DP-1 specification
- Canonicalization: Implements RFC 8785 JSON canonicalization for deterministic signing and verification
- Signing: Uses Ed25519 signatures via Web Crypto API (available in Node 22+ and modern browsers)
- SHA-256 Hashing: Creates hash of canonical JSON before signing
- Verification: Validates signatures by comparing Ed25519 signature against playlist canonical form using public key
Contributions are welcome! Please ensure:
- All tests pass (npm test)
- Code follows the existing style (npm run lint)
- TypeScript types are properly defined
- DP-1 Specification - Official DP-1 protocol specification
- Feral File - Digital art platform using DP-1
For issues and questions:
- Open an issue on GitHub
- Check the DP-1 specification for protocol details