Deprecated, this package was moved into enact.
Cryptographic signing library for Enact Protocol documents with configurable field selection
A comprehensive TypeScript security library providing cryptographic signing and verification for Enact Protocol tool definitions. Features cross-platform compatibility, field-specific signing, and full Enact Protocol compliance.
✨ Perfect for: Enact Protocol tools, multi-party signatures, sensitive data protection, cross-platform applications
| Package | Description | Environment |
|---|---|---|
@enactprotocol/security |
Backend/Node.js signing library | Server-side |
@enactprotocol/security-web |
Frontend/Browser signing library | Client-side |
# Install the package
npm install @enactprotocol/security
# Create and run this file
echo 'import { SigningService, CryptoUtils } from "@enactprotocol/security";
const tool = {
name: "my-org/hello-world",
description: "My first tool",
command: "echo \"Hello \${name}!\""
};
const keys = CryptoUtils.generateKeyPair();
const signature = SigningService.signDocument(tool, keys.privateKey, { useEnactDefaults: true });
const isValid = SigningService.verifyDocument(tool, signature, { useEnactDefaults: true });
console.log("✅ Signature valid:", isValid);' > test.mjs
node test.mjsnpm install @enactprotocol/security
# or
yarn add @enactprotocol/security
# or
bun add @enactprotocol/securitynpm install @enactprotocol/security-web
# or
yarn add @enactprotocol/security-web
# or
bun add @enactprotocol/security-webWe've created working examples you can try immediately:
📱 Web Demo: Interactive browser application
git clone https://github.com/enactprotocol/security
cd security/example-web-app
npm install && npm run dev
# Open http://localhost:5173🖥️ Backend Demo: Command-line demonstration
cd security/example-backend-app
npm install && node demo.jsimport {
SigningService,
CryptoUtils,
EnactFieldSelector,
type EnactDocument
} from '@enactprotocol/security';
// Generate a key pair
const keyPair = CryptoUtils.generateKeyPair();
console.log('Private key:', keyPair.privateKey);
console.log('Public key:', keyPair.publicKey);
// Define an Enact tool
const tool: EnactDocument = {
name: "acme-corp/formatting/prettier",
description: "Auto-formats JavaScript and TypeScript code",
command: "npx prettier@3.3.3 --write '${file}'",
enact: "1.0.0",
version: "1.2.0",
from: "node:18-alpine",
timeout: "30s",
annotations: {
destructiveHint: true,
title: "Code Formatter"
},
inputSchema: {
type: "object",
properties: {
file: { type: "string", description: "File to format" }
},
required: ["file"]
}
};
// Sign with Enact Protocol defaults (security-critical fields only)
const signature = SigningService.signDocument(tool, keyPair.privateKey, {
useEnactDefaults: true
});
console.log('Signature:', signature.signature);
console.log('Public key:', signature.publicKey);
// Verify the signature
const isValid = SigningService.verifyDocument(tool, signature, {
useEnactDefaults: true
});
console.log('Signature valid:', isValid); // trueimport {
SigningService,
CryptoUtils,
type EnactDocument
} from '@enactprotocol/security-web';
// Same API as backend - works identically in browsers
const keyPair = CryptoUtils.generateKeyPair();
const tool: EnactDocument = {
name: "my-org/web-tool",
description: "A browser-based tool",
command: "echo 'Hello from browser'",
enact: "1.0.0"
};
// Sign and verify - identical to backend usage
const signature = SigningService.signDocument(tool, keyPair.privateKey, {
useEnactDefaults: true
});
const isValid = SigningService.verifyDocument(tool, signature, {
useEnactDefaults: true
});Perfect for most Enact Protocol tools:
const signature = SigningService.signDocument(tool, privateKey, {
useEnactDefaults: true // Signs all security-critical fields
});Different teams sign different aspects:
// Developer signs core functionality
const devSignature = SigningService.signDocument(tool, devPrivateKey, {
includeFields: ["name", "command", "description", "inputSchema"]
});
// Security team signs security aspects
const secSignature = SigningService.signDocument(tool, secPrivateKey, {
includeFields: ["annotations", "env", "from", "timeout"]
});Exclude confidential information from signatures:
const signature = SigningService.signDocument(tool, privateKey, {
useEnactDefaults: true,
excludeFields: ["env", "metadata", "secrets"] // Keep sensitive data private
});Sign only essential fields for performance:
const signature = SigningService.signDocument(tool, privateKey, {
includeFields: ["name", "command", "version"] // Minimal signature
});Frontend creates, backend verifies (or vice versa):
// Frontend (browser)
import { SigningService } from '@enactprotocol/security-web';
const signature = SigningService.signDocument(tool, privateKey, { useEnactDefaults: true });
// Backend (Node.js) - verifies frontend signature perfectly!
import { SigningService } from '@enactprotocol/security';
const isValid = SigningService.verifyDocument(tool, signature, { useEnactDefaults: true });Signs only the security-critical fields defined by the Enact Protocol:
const signature = SigningService.signDocument(tool, privateKey, {
useEnactDefaults: true // Signs: annotations, command, description, enact, env, from, inputSchema, name, timeout, version
});
// See which fields are signed
const signedFields = SigningService.getSignedFields({ useEnactDefaults: true });
console.log('Signed fields:', signedFields);
// Output: ['annotations', 'command', 'description', 'enact', 'env', 'from', 'inputSchema', 'name', 'timeout', 'version']Sign only specific fields you care about:
const signature = SigningService.signDocument(tool, privateKey, {
includeFields: ["name", "command", "version", "license"]
});
// View the canonical document that gets signed
const canonical = SigningService.getCanonicalDocument(tool, {
includeFields: ["name", "command", "version", "license"]
});
console.log('Canonical document:', canonical);Extend Enact defaults with extra fields:
const signature = SigningService.signDocument(tool, privateKey, {
useEnactDefaults: true,
additionalCriticalFields: ["license", "tags", "authors"]
});Remove sensitive fields from signatures:
const signature = SigningService.signDocument(tool, privateKey, {
useEnactDefaults: true,
excludeFields: ["env", "metadata"] // Exclude potentially sensitive data
});For non-Enact documents, use generic defaults:
const document = {
id: "contract-123",
content: "Terms and conditions...",
timestamp: Date.now(),
metadata: { type: "contract" }
};
const signature = SigningService.signDocument(document, privateKey, {
useEnactDefaults: false // Uses generic defaults: id, content, timestamp
});Signatures created on the backend can be verified on the frontend and vice versa:
// Backend creates signature
const backendSignature = BackendSigningService.signDocument(tool, privateKey, {
useEnactDefaults: true
});
// Frontend verifies signature - works perfectly!
const isValid = FrontendSigningService.verifyDocument(tool, backendSignature, {
useEnactDefaults: true
});
console.log('Cross-platform verification:', isValid); // trueimport { CryptoUtils } from '@enactprotocol/security';
// Generate a new key pair
const keyPair = CryptoUtils.generateKeyPair();
// Derive public key from existing private key
const publicKey = CryptoUtils.getPublicKeyFromPrivate(existingPrivateKey);The backend package includes a key management utility:
import { KeyManager } from '@enactprotocol/security';
// Generate and store a key pair
const keyPair = KeyManager.generateAndStoreKey('my-signing-key');
// Retrieve stored key
const storedKey = KeyManager.getKey('my-signing-key');
// Import existing key
KeyManager.importKey('imported-key', existingPrivateKey);
// List all stored keys
const keyIds = KeyManager.listKeys();Signs a document with configurable field selection.
Parameters:
document: EnactDocument- Document to signprivateKey: string- Hex-encoded private keyoptions?: SigningOptions- Signing configuration
Options:
interface SigningOptions {
algorithm?: 'secp256k1'; // Signature algorithm (default: 'secp256k1')
useEnactDefaults?: boolean; // Use Enact protocol critical fields (default: false)
includeFields?: string[]; // Specific fields to include
excludeFields?: string[]; // Fields to exclude from signing
additionalCriticalFields?: string[]; // Extra fields to add to Enact defaults
}Returns:
interface Signature {
signature: string; // Hex-encoded signature
publicKey: string; // Hex-encoded public key
algorithm: string; // Signature algorithm used
timestamp: number; // Signature creation timestamp
}Verifies a document signature.
Parameters:
document: EnactDocument- Document to verifysignature: Signature- Signature object to verifyoptions?: SigningOptions- Must match signing options
Returns: boolean - True if signature is valid
Returns the canonical document that gets signed.
Returns: Record<string, any> - Canonical document object
Returns the list of fields that will be signed with given options.
Returns: string[] - Array of field names
Generates a new secp256k1 key pair.
Returns:
{
privateKey: string; // Hex-encoded private key
publicKey: string; // Hex-encoded public key
}Derives public key from private key.
Creates SHA-256 hash of data.
Signs a message hash.
Verifies a signature.
Pre-configured selector for Enact Protocol security-critical fields:
annotations,command,description,enact,env,from,inputSchema,name,timeout,version
Pre-configured selector for generic documents:
id,content,timestamp
Create custom field configurations:
import { FieldSelector, type FieldConfig } from '@enactprotocol/security';
const customConfig: FieldConfig[] = [
{ name: 'title', required: true, securityCritical: true },
{ name: 'content', required: true, securityCritical: true },
{ name: 'metadata', required: false, securityCritical: false }
];
const customSelector = new FieldSelector(customConfig);Run the comprehensive test suite:
# Backend tests
cd packages/security && bun test
# Frontend tests
cd packages/security-web && bun test
# Cross-platform compatibility test
bun run cross-platform-test.ts
# Field-specific signing tests
bun run field-specific-test.ts- 🔐 secp256k1 ECDSA Signatures - Industry-standard elliptic curve cryptography
- 📋 Enact Protocol Compliance - Follows official Enact specification for field selection
- 🎯 Configurable Security - Choose exactly which fields to include in signatures
- 🚫 Empty Field Exclusion - Automatically excludes null, empty strings, and empty objects
- 🔄 Deterministic Signing - Produces identical signatures for identical inputs
- 🌐 Cross-Platform Verified - Backend and frontend signatures are fully compatible
// Party 1 signs core fields
const authorSignature = SigningService.signDocument(tool, authorPrivateKey, {
includeFields: ["name", "command", "description"]
});
// Party 2 signs security-critical fields
const securitySignature = SigningService.signDocument(tool, securityPrivateKey, {
useEnactDefaults: true
});
// Verify both signatures
const authorValid = SigningService.verifyDocument(tool, authorSignature, {
includeFields: ["name", "command", "description"]
});
const securityValid = SigningService.verifyDocument(tool, securitySignature, {
useEnactDefaults: true
});// Sign everything except sensitive environment variables
const signature = SigningService.signDocument(tool, privateKey, {
useEnactDefaults: true,
excludeFields: ["env", "secrets", "apiKeys"]
});// Corporate security policy: sign metadata + Enact defaults
const corporateSignature = SigningService.signDocument(tool, privateKey, {
useEnactDefaults: true,
additionalCriticalFields: [
"license",
"authors",
"approval",
"security-review"
]
});# Clone the repository
git clone <repository-url>
cd enact-security
# Install dependencies
bun install
# Build both packages
bun run build
# Run all tests
bun test
# Run examples
bun run examples/enact-signing-examples.tsMake sure you're using the correct import:
// ✅ Correct - use default import
import { SigningService } from '@enactprotocol/security';
// ❌ Incorrect
import SigningService from '@enactprotocol/security';Ensure you use the same field selection for signing and verification:
const options = { useEnactDefaults: true };
const signature = SigningService.signDocument(tool, privateKey, options);
const isValid = SigningService.verifyDocument(tool, signature, options); // Same options!This is expected! Signatures include timestamps, so they'll be different each time. Verification should still work:
// Different signatures (different timestamps) ✅
// But verification works across platforms ✅The web package requires modern browsers with Web Crypto API support:
- Chrome 37+, Firefox 34+, Safari 7+, Edge 12+
- Use HTTPS in production (required for Web Crypto API)
Make sure you have the latest TypeScript definitions:
npm update @types/node # For backend- 📖 Check the examples for working code
- 🐛 Open an issue
- 💬 Start a discussion
MIT License - see LICENSE for details.
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
Ready to secure your Enact Protocol tools? Install the packages and start signing! 🚀