@25xcodes/llmfeed-signer
Ed25519 key generation and cryptographic feed signing for LLMFeed JSON.
Format Support
| Format | Status |
|---|---|
| LLMFeed JSON | ✅ Fully Supported |
| llm.txt | ❌ Not Applicable |
Why No llm.txt Signing?
The llm.txt markdown format is designed for human readability and doesn't have a structured format suitable for cryptographic signing. Use LLMFeed JSON for feeds that require signature verification.
Features
- 🔑 Key Generation - Generate Ed25519 key pairs
- ✍️ Feed Signing - Sign feeds with cryptographic signatures
- 📜 PEM Support - Work with PEM-formatted keys
- 🔄 Key Loading - Load existing keys from files
- ✅ Signature Verification - Verify signed feeds
Quick Start
bash
npm install @25xcodes/llmfeed-signertypescript
import { generateKeyPair, signFeed } from '@25xcodes/llmfeed-signer'
// Generate keys
const { privateKey, publicKey } = await generateKeyPair()
// Sign a LLMFeed JSON
const feed = {
feed_type: 'llmfeed',
metadata: {
title: 'My Service',
origin: 'https://example.com',
description: 'A helpful service'
},
items: [{ title: 'Doc', url: 'https://example.com' }]
}
const signedFeed = await signFeed(feed, privateKey)Core Functions
generateKeyPair()
Generate a new Ed25519 key pair:
typescript
import { generateKeyPair } from '@25xcodes/llmfeed-signer'
const keyPair = await generateKeyPair()
console.log(keyPair.privateKey) // Base64 PKCS#8
console.log(keyPair.publicKey) // Base64 raw (32 bytes)
console.log(keyPair.privateKeyPem) // PEM format
console.log(keyPair.publicKeyPem) // PEM format
console.log(keyPair.createdAt) // ISO timestampsignFeed(feed, privateKey, options?)
Sign a feed with your private key:
typescript
import { signFeed } from '@25xcodes/llmfeed-signer'
const signedFeed = await signFeed(feed, privateKey)
// Specify which blocks to sign
const signedFeed = await signFeed(feed, privateKey, {
signedBlocks: ['title', 'capabilities']
})The private key can be in multiple formats:
- PEM format (with
-----BEGIN PRIVATE KEY-----headers) - Base64-encoded PKCS#8 (48 bytes decoded)
- Base64-encoded raw seed (32 bytes decoded)
verifyFeed(feed, publicKeyBase64)
Verify a signed feed's signature:
typescript
import { verifyFeed, pemToPublicKey, uint8ArrayToBase64 } from '@25xcodes/llmfeed-signer'
import fs from 'fs'
// Load public key
const publicKeyPem = fs.readFileSync('./keys/public.pem', 'utf-8')
const publicKeyRaw = pemToPublicKey(publicKeyPem)
const publicKeyBase64 = uint8ArrayToBase64(publicKeyRaw)
// Verify the feed
const result = await verifyFeed(signedFeed, publicKeyBase64)
console.log(result.valid) // boolean
console.log(result.signedBlocks) // string[]
console.log(result.payloadHash) // SHA-256 hash
console.log(result.error) // Error message if invalidloadKeyPair(privateKey)
Load an existing private key:
typescript
import { loadKeyPair } from '@25xcodes/llmfeed-signer'
import fs from 'fs'
const privateKeyPem = fs.readFileSync('./private.pem', 'utf-8')
const keyPair = await loadKeyPair(privateKeyPem)Key Formats
PEM Format
typescript
const pem = `-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIOF0PrJOZV9pPi4E7E9QPWV3Q9o/K7d2mZs8L5Q+Abc1
-----END PRIVATE KEY-----`
const signedFeed = await signFeed(feed, pem)Base64 PKCS#8
typescript
const base64Pkcs8 = 'MC4CAQAwBQYDK2VwBCIEIOF0PrJOZV9pPi4E7E9QPWV3Q9o/...'
const signedFeed = await signFeed(feed, base64Pkcs8)PEM Utilities
typescript
import { formatPem, parsePem } from '@25xcodes/llmfeed-signer'
// Create PEM from base64
const pem = formatPem(base64Key, 'PRIVATE KEY')
// Extract base64 from PEM
const base64 = parsePem(pem)CLI Usage
bash
# Generate a new key pair
npx llmfeed-sign keygen --output ./keys
# Sign a LLMFeed JSON file
npx llmfeed-sign sign ./llmfeed.json --key ./keys/private.pem --output ./signed.json
# Sign specific blocks only
npx llmfeed-sign sign ./llmfeed.json --key ./keys/private.pem --blocks feed_type,metadata,capabilities
# Verify a signed feed
npx llmfeed-sign verify ./signed.jsonUtility Functions
typescript
import {
sha256,
deepSortObject,
uint8ArrayToBase64,
base64ToUint8Array,
formatPem,
parsePem,
pemToPublicKey
} from '@25xcodes/llmfeed-signer'
// Hash content
const hash = await sha256(JSON.stringify(feed))
// Canonical JSON
const canonical = JSON.stringify(deepSortObject(obj))
// Base64 conversion
const base64 = uint8ArrayToBase64(bytes)
const bytes = base64ToUint8Array(base64)Trust Block Output
The signed feed includes separate trust and signature blocks:
json
{
"feed_type": "mcp",
"metadata": {
"title": "My Service",
"origin": "https://example.com",
"description": "A helpful service"
},
"capabilities": [...],
"trust": {
"signed_blocks": ["feed_type", "metadata", "capabilities"],
"algorithm": "Ed25519",
"public_key_hint": "https://example.com/.well-known/public.pem",
"trust_level": "self-signed"
},
"signature": {
"value": "base64-encoded-signature...",
"created_at": "2025-12-01T14:30:00.000Z"
}
}Security Best Practices
Never expose private keys
- Don't commit private keys to version control
- Use environment variables in CI/CD
- Use a secrets manager for production
typescript
// ✅ Good: Load from environment
const privateKey = process.env.LLMFEED_PRIVATE_KEY
// ❌ Bad: Hardcoded key
const privateKey = 'MC4CAQAwBQYDK2VwBCIEI...'Next Steps
- Installation - Detailed installation guide
- Key Management - Managing your keys securely
- Signing Feeds - Complete signing guide
- API Reference - Full API documentation