@enactprotocol/security
v1.2.13
Published
Backend security library for signing enact documents
Maintainers
Readme
Security Configuration Usage Guide
This document provides practical examples for using the security configuration features in the Enact Protocol security library.
Quick Start
import { SigningService, CryptoUtils, DEFAULT_SECURITY_CONFIG } from '@enactprotocol/security';
import type { SecurityConfig } from '@enactprotocol/security';
// Create a document to sign
const tool = {
name: "my-org/hello-world",
description: "A simple greeting tool",
command: "echo 'Hello ${name}!'",
enact: "1.0.0"
};
// Generate keys
const keyPair = CryptoUtils.generateKeyPair();
// Sign the document
const signature = SigningService.signDocument(tool, keyPair.privateKey, {
useEnactDefaults: true
});
// Verify with default config (minimumSignatures: 1, allowLocalUnsigned: true)
const isValid = SigningService.verifyDocument(tool, signature, {
useEnactDefaults: true
});
console.log('Valid:', isValid); // trueSecurity Configuration Options
Basic Configuration
interface SecurityConfig {
allowLocalUnsigned?: boolean; // Allow documents without signatures
minimumSignatures?: number; // Minimum number of signatures required
}
// Default configuration
const DEFAULT_SECURITY_CONFIG: SecurityConfig = {
allowLocalUnsigned: true,
minimumSignatures: 1
};Common Usage Patterns
1. Permissive Mode (Default)
Use case: Development, local tools, single-developer workflows
const permissiveConfig: SecurityConfig = {
allowLocalUnsigned: true,
minimumSignatures: 1
};
// This will pass even with no signatures
const documentWithoutSignatures = {
name: "local-tool",
command: "echo 'local'",
signatures: [] // Empty signatures array
};
const isValid = SigningService.verifyDocument(
documentWithoutSignatures,
{} as any, // Dummy signature
{ useEnactDefaults: true },
permissiveConfig
);
console.log('Valid:', isValid); // true2. Single Signature Required
Use case: Personal tools, simple validation
const singleSignatureConfig: SecurityConfig = {
allowLocalUnsigned: false,
minimumSignatures: 1
};
// Must have at least one valid signature
const signature = SigningService.signDocument(tool, keyPair.privateKey, {
useEnactDefaults: true
});
const isValid = SigningService.verifyDocument(
tool,
signature,
{ useEnactDefaults: true },
singleSignatureConfig
);
console.log('Valid:', isValid); // true3. Multi-Party Approval
Use case: Enterprise environments, critical tools, compliance requirements
const enterpriseConfig: SecurityConfig = {
allowLocalUnsigned: false,
minimumSignatures: 2
};
// Create multiple signatures from different parties
const developerKeys = CryptoUtils.generateKeyPair();
const reviewerKeys = CryptoUtils.generateKeyPair();
const devSignature = SigningService.signDocument(tool, developerKeys.privateKey, {
useEnactDefaults: true
});
const reviewSignature = SigningService.signDocument(tool, reviewerKeys.privateKey, {
useEnactDefaults: true
});
// Add signatures to document
const toolWithMultipleSignatures = {
...tool,
signatures: [devSignature, reviewSignature]
};
const isValid = SigningService.verifyDocument(
toolWithMultipleSignatures,
devSignature, // This gets ignored since document has signatures array
{ useEnactDefaults: true },
enterpriseConfig
);
console.log('Valid:', isValid); // true4. Strict Security Mode
Use case: Production environments, sensitive operations
const strictConfig: SecurityConfig = {
allowLocalUnsigned: false,
minimumSignatures: 3
};
// Requires 3 signatures: developer + reviewer + security team
const devSignature = SigningService.signDocument(tool, developerKeys.privateKey, {
useEnactDefaults: true
});
const reviewSignature = SigningService.signDocument(tool, reviewerKeys.privateKey, {
useEnactDefaults: true
});
const securityKeys = CryptoUtils.generateKeyPair();
const secSignature = SigningService.signDocument(tool, securityKeys.privateKey, {
useEnactDefaults: true
});
const secureDocument = {
...tool,
signatures: [devSignature, reviewSignature, secSignature]
};
const isValid = SigningService.verifyDocument(
secureDocument,
devSignature,
{ useEnactDefaults: true },
strictConfig
);
console.log('Valid:', isValid); // trueConfiguration Scenarios
Development Environment
const devConfig: SecurityConfig = {
allowLocalUnsigned: true,
minimumSignatures: 1
};
// ✅ Allows unsigned local tools
// ✅ Single signature sufficient for signed toolsTesting Environment
const testConfig: SecurityConfig = {
allowLocalUnsigned: false,
minimumSignatures: 1
};
// ❌ Requires all tools to be signed
// ✅ Single signature sufficientProduction Environment
const prodConfig: SecurityConfig = {
allowLocalUnsigned: false,
minimumSignatures: 2
};
// ❌ No unsigned tools allowed
// ❌ Requires multiple signatures for approvalError Handling
Insufficient Signatures
const config: SecurityConfig = {
allowLocalUnsigned: false,
minimumSignatures: 2
};
const toolWithOneSignature = {
...tool,
signatures: [signature] // Only 1 signature, need 2
};
const isValid = SigningService.verifyDocument(
toolWithOneSignature,
signature,
{ useEnactDefaults: true },
config
);
console.log('Valid:', isValid); // false - insufficient signaturesInvalid Signatures
const invalidSignature = {
signature: 'invalid_signature_data',
publicKey: 'invalid_key',
algorithm: 'secp256k1',
created: Date.now()
};
const toolWithInvalidSignature = {
...tool,
signatures: [invalidSignature]
};
const isValid = SigningService.verifyDocument(
toolWithInvalidSignature,
invalidSignature,
{ useEnactDefaults: true }
);
console.log('Valid:', isValid); // false - cryptographically invalidBest Practices
1. Environment-Based Configuration
const getSecurityConfig = (environment: string): SecurityConfig => {
switch (environment) {
case 'development':
return {
allowLocalUnsigned: true,
minimumSignatures: 1
};
case 'staging':
return {
allowLocalUnsigned: false,
minimumSignatures: 1
};
case 'production':
return {
allowLocalUnsigned: false,
minimumSignatures: 2
};
default:
return DEFAULT_SECURITY_CONFIG;
}
};
const config = getSecurityConfig(process.env.NODE_ENV || 'development');2. Gradual Security Increase
// Start permissive, increase security over time
const phases = {
phase1: { allowLocalUnsigned: true, minimumSignatures: 1 }, // Launch
phase2: { allowLocalUnsigned: false, minimumSignatures: 1 }, // Require signing
phase3: { allowLocalUnsigned: false, minimumSignatures: 2 } // Require approval
};3. Role-Based Workflows
// Different signature requirements for different types of tools
const getConfigForTool = (toolType: string): SecurityConfig => {
switch (toolType) {
case 'utility':
return { allowLocalUnsigned: true, minimumSignatures: 1 };
case 'deployment':
return { allowLocalUnsigned: false, minimumSignatures: 2 };
case 'security':
return { allowLocalUnsigned: false, minimumSignatures: 3 };
default:
return DEFAULT_SECURITY_CONFIG;
}
};Integration Examples
CLI Tool Integration
import { SigningService } from '@enactprotocol/security';
class ToolVerifier {
constructor(private securityConfig: SecurityConfig) {}
async verifyTool(tool: EnactDocument): Promise<boolean> {
// Tool must have at least one signature to verify
if (!tool.signatures?.length && !this.securityConfig.allowLocalUnsigned) {
return false;
}
// If no signatures and local unsigned allowed
if (!tool.signatures?.length && this.securityConfig.allowLocalUnsigned) {
return true;
}
// Verify signatures using first signature as reference
return SigningService.verifyDocument(
tool,
tool.signatures[0],
{ useEnactDefaults: true },
this.securityConfig
);
}
}
// Usage
const verifier = new ToolVerifier({
allowLocalUnsigned: false,
minimumSignatures: 2
});
const isToolValid = await verifier.verifyTool(someTool);Config File Integration
// ~/.enact/security/config.json
{
"minimumSignatures": 1,
"allowLocalUnsigned": true
}
// Load configuration
import { readFileSync } from 'fs';
import { join } from 'path';
import { homedir } from 'os';
const loadSecurityConfig = (): SecurityConfig => {
try {
const configPath = join(homedir(), '.enact', 'security', 'config.json');
const config = JSON.parse(readFileSync(configPath, 'utf8'));
return {
allowLocalUnsigned: config.allowLocalUnsigned ?? true,
minimumSignatures: config.minimumSignatures ?? 1
};
} catch {
return DEFAULT_SECURITY_CONFIG;
}
};
const config = loadSecurityConfig();Migration Guide
From No Security Config
// Before
const isValid = SigningService.verifyDocument(tool, signature, options);
// After
const isValid = SigningService.verifyDocument(
tool,
signature,
options,
DEFAULT_SECURITY_CONFIG // Uses minimumSignatures: 1, allowLocalUnsigned: true
);Upgrading Security Gradually
// Week 1: Start with permissive defaults
let config = DEFAULT_SECURITY_CONFIG;
// Week 2: Require signing for new tools
config = { ...config, allowLocalUnsigned: false };
// Week 4: Require multiple signatures for critical tools
if (tool.annotations?.critical) {
config = { ...config, minimumSignatures: 2 };
}Summary
The security configuration system provides flexible verification policies that can adapt to different environments and security requirements:
- Development: Permissive, allows unsigned tools
- Testing: Requires signatures but single signature sufficient
- Production: Strict multi-party approval required
Use the configuration to gradually increase security maturity while maintaining usability for developers.
