SDK: Code Examples
Practical examples demonstrating common use cases with the Grizzly SDK.
Setup
All examples assume you've created and initialized a KeyClient:
import { KeyClient } from '@grizzlycbg/grizzly-sdk'
import fs from 'fs'
const client = await KeyClient.create({
host: 'https://your-tenant.grizzlycbg.io',
apikey: 'apikey-123456-1234567890'
})
File Encryption
Encrypt and Decrypt Files
async function encryptFile(inputPath, outputPath, keyringName) {
const ring = await client.ring.get(keyringName)
// Create read stream from input file
const readStream = fs.createReadStream(inputPath)
// Encrypt the stream
const encryptedStream = await ring.encrypt(readStream)
// Write encrypted stream to output file
return new Promise((resolve, reject) => {
const writeStream = fs.createWriteStream(outputPath)
encryptedStream.pipe(writeStream)
writeStream.on('finish', () => {
console.log(`File encrypted: ${outputPath}`)
resolve()
})
writeStream.on('error', reject)
})
}
async function decryptFile(inputPath, outputPath) {
// Client-level decrypt automatically determines the KeyRing
const encryptedStream = fs.createReadStream(inputPath)
const decryptedStream = await client.decrypt(encryptedStream)
return new Promise((resolve, reject) => {
const writeStream = fs.createWriteStream(outputPath)
decryptedStream.pipe(writeStream)
writeStream.on('finish', () => {
console.log(`File decrypted: ${outputPath}`)
resolve()
})
writeStream.on('error', reject)
})
}
// Usage
await encryptFile('./document.pdf', './document.encrypted', 'Documents')
await decryptFile('./document.encrypted', './document-decrypted.pdf')
In-Memory Data Encryption
Encrypt Application Data
async function encryptUserData(userData, keyringName) {
const ring = await client.ring.get(keyringName)
// Serialize object to JSON
const jsonData = JSON.stringify(userData)
// Encrypt
const encrypted = await ring.encrypt(jsonData)
return encrypted
}
async function decryptUserData(encryptedData) {
// Decrypt
const decrypted = await client.decrypt(encryptedData)
// Parse JSON back to object
return JSON.parse(decrypted)
}
// Usage
const user = {
id: '12345',
email: 'user@example.com',
ssn: '123-45-6789'
}
const encrypted = await encryptUserData(user, 'UserData')
console.log('Encrypted:', encrypted)
const decrypted = await decryptUserData(encrypted)
console.log('Decrypted:', decrypted)
Asset Tagging
Working with Assets
Assets are first-class entities in Grizzly that allow you to attach cryptographically signed metadata to encrypted data. The metadata is stored in the encrypted data's header and tracked throughout the platform, including the Activity feed.async function encryptWithAsset(data, assetMetadata, keyringName) {
const ring = await client.ring.get(keyringName)
// Assets can be simple strings
const encrypted1 = await ring.encrypt(data, {
asset: 'user-12345'
})
// Or complex objects with metadata
const encrypted2 = await ring.encrypt(data, {
asset: {
id: 'doc-789',
name: 'contract.pdf',
owner: 'user-456',
classification: 'confidential',
metadata: {
department: 'legal',
retentionYears: 7
}
}
})
return { encrypted1, encrypted2 }
}
async function encryptPatientRecord(patientId, recordData) {
const ring = await client.ring.get('Healthcare')
// Asset with compliance metadata
const encrypted = await ring.encrypt(recordData, {
asset: {
id: `patient-${patientId}`,
type: 'medical-record',
policy: {
hipaa: true,
allowedPurposes: ['clinical_trial', 'treatment']
},
metadata: {
department: 'cardiology',
recordDate: new Date().toISOString()
}
}
})
return encrypted
}
// Usage
const patientData = JSON.stringify({
name: 'John Doe',
diagnosis: 'Condition XYZ'
})
const encrypted = await encryptPatientRecord('12345', patientData)
// The asset metadata is cryptographically signed and embedded in the header
// Any modification to the metadata will invalidate the encrypted data
// Assets appear in Activity feed and can be queried across the platform
Key Rotation
Manual Key Rotation
async function rotateKeyRing(keyringName) {
const ring = await client.ring.get(keyringName)
console.log(`Current active key: ${ring.activeKey.id}`)
// Rotate to new key
await ring.rotate()
// Refresh KeyRing to get updated active key
const updatedRing = await client.ring.get(keyringName)
console.log(`New active key: ${updatedRing.activeKey.id}`)
return updatedRing
}
// Demonstrate that old encrypted data still decrypts after rotation
async function testRotation() {
const ring = await client.ring.get('Finance')
// Encrypt with current key
const data1 = await ring.encrypt('Message 1')
console.log('Encrypted with key:', ring.activeKey.id)
// Rotate key
await ring.rotate()
// Encrypt with new key
const ring2 = await client.ring.get('Finance')
const data2 = await ring2.encrypt('Message 2')
console.log('Encrypted with key:', ring2.activeKey.id)
// Both decrypt successfully
const decrypted1 = await ring2.decrypt(data1)
const decrypted2 = await ring2.decrypt(data2)
console.log('Decrypted 1:', decrypted1) // 'Message 1'
console.log('Decrypted 2:', decrypted2) // 'Message 2'
}
await testRotation()
Customer-Managed Keys
Bring Your Own Keys
import fs from 'fs'
async function createKeyRingWithCustomKeys() {
// Read your RSA key pair from files
const publicKey = fs.readFileSync('./keys/public.pem', 'utf-8')
const privateKey = fs.readFileSync('./keys/private.pem', 'utf-8')
// Create KeyRing with your keys
const ring = await client.ring.create('SecureFinance', {
publicKey,
privateKey,
config: {
props: {
owner: 'finance-team',
compliance: 'PCI-DSS'
},
algos: {
aes256: {
maxEncryptCount: 50000
}
}
}
})
console.log(`Created KeyRing: ${ring.name}`)
return ring
}
async function encryptWithCustomKeys() {
const ring = await client.ring.get('SecureFinance')
const encrypted = await ring.encrypt('Highly sensitive data')
// Decrypt requires the private key
const privateKey = fs.readFileSync('./keys/private.pem', 'utf-8')
const decrypted = await client.decrypt(encrypted, privateKey)
console.log('Decrypted:', decrypted)
}
await createKeyRingWithCustomKeys()
await encryptWithCustomKeys()
KeyRing Configuration Management
Update KeyRing Settings
async function updateKeyRingConfig(keyringName) {
const ring = await client.ring.get(keyringName)
// Get current config
const config = await ring.getConfig()
console.log('Current max encrypt count:', config.algos.aes256.maxEncryptCount)
// Update configuration
await ring.updateConfig((cfg) => {
// Change encryption count before rotation
cfg.algos.aes256.maxEncryptCount = 100000
// Add custom metadata
cfg.props.updatedAt = new Date().toISOString()
cfg.props.updatedBy = 'admin'
// Add key-level metadata (applied to new keys)
cfg.key.props = {
environment: 'production',
version: '2.0'
}
})
// Verify update
const updatedConfig = await ring.getConfig()
console.log('New max encrypt count:', updatedConfig.algos.aes256.maxEncryptCount)
}
await updateKeyRingConfig('Finance')
Batch Operations
Encrypt Multiple Items
async function encryptUserBatch(users, keyringName) {
const ring = await client.ring.get(keyringName)
const encryptedUsers = await Promise.all(
users.map(async (user) => {
const userData = JSON.stringify(user)
const encrypted = await ring.encrypt(userData, {
asset: {
id: `user-${user.id}`,
type: 'user-record',
email: user.email,
encryptedAt: new Date().toISOString()
}
})
return {
id: user.id,
encrypted
}
})
)
return encryptedUsers
}
async function decryptUserBatch(encryptedUsers) {
const decryptedUsers = await Promise.all(
encryptedUsers.map(async (item) => {
const decrypted = await client.decrypt(item.encrypted)
return {
id: item.id,
data: JSON.parse(decrypted)
}
})
)
return decryptedUsers
}
// Usage
const users = [
{ id: '1', name: 'Alice', email: 'alice@example.com' },
{ id: '2', name: 'Bob', email: 'bob@example.com' },
{ id: '3', name: 'Carol', email: 'carol@example.com' }
]
const encrypted = await encryptUserBatch(users, 'UserData')
console.log('Encrypted:', encrypted.length, 'users')
const decrypted = await decryptUserBatch(encrypted)
console.log('Decrypted:', decrypted)
Error Handling
Handle Common Errors
async function encryptWithErrorHandling(data, keyringName) {
try {
// Try to get existing KeyRing
let ring = await client.ring.get(keyringName)
// Create if doesn't exist
if (!ring) {
console.log(`KeyRing '${keyringName}' not found, creating...`)
ring = await client.ring.create(keyringName)
}
// Encrypt data
const encrypted = await ring.encrypt(data)
return { success: true, encrypted }
} catch (error) {
console.error('Encryption failed:', error.message)
// Handle specific error types
if (error.message.includes('authentication')) {
return { success: false, error: 'Invalid API key' }
}
if (error.message.includes('network')) {
return { success: false, error: 'Connection failed' }
}
return { success: false, error: error.message }
}
}
async function decryptWithErrorHandling(encrypted) {
try {
const decrypted = await client.decrypt(encrypted)
return { success: true, decrypted }
} catch (error) {
console.error('Decryption failed:', error.message)
if (error.message.includes('invalid')) {
return { success: false, error: 'Invalid encrypted data' }
}
if (error.message.includes('key not found')) {
return { success: false, error: 'Decryption key unavailable' }
}
return { success: false, error: error.message }
}
}
// Usage
const result = await encryptWithErrorHandling('Sensitive data', 'Finance')
if (result.success) {
console.log('Encrypted:', result.encrypted)
} else {
console.error('Error:', result.error)
}
Caching and Performance
Optimize with Key Caching
// Create client with key caching
const client = await KeyClient.create({
host: 'https://your-tenant.grizzlycbg.io',
apikey: 'apikey-123456-1234567890',
keycache: {
max: 100 // Cache up to 100 keys
}
})
async function encryptHighVolume(items, keyringName) {
const ring = await client.ring.get(keyringName)
// First encryption fetches key from server
// Subsequent encryptions use cached key (much faster)
const startTime = Date.now()
for (const item of items) {
await ring.encrypt(item.data)
}
const duration = Date.now() - startTime
console.log(`Encrypted ${items.length} items in ${duration}ms`)
console.log(`Average: ${duration / items.length}ms per item`)
}
// Simulate high-volume encryption
const items = Array.from({ length: 1000 }, (_, i) => ({
id: i,
data: `Data item ${i}`
}))
await encryptHighVolume(items, 'HighVolume')
Complete Application Example
Secure Document Storage Service
import { KeyClient } from '@grizzlycbg/grizzly-sdk'
import fs from 'fs'
import path from 'path'
class SecureDocumentService {
constructor(client) {
this.client = client
this.keyringName = 'Documents'
}
async initialize() {
// Ensure KeyRing exists
let ring = await this.client.ring.get(this.keyringName)
if (!ring) {
ring = await this.client.ring.create(this.keyringName, {
config: {
props: {
service: 'document-storage',
version: '1.0'
},
algos: {
aes256: {
maxEncryptCount: 10000
}
}
}
})
console.log(`Created KeyRing: ${this.keyringName}`)
}
return ring
}
async uploadDocument(userId, documentId, filePath) {
try {
const ring = await this.client.ring.get(this.keyringName)
// Read file
const content = fs.readFileSync(filePath)
const filename = path.basename(filePath)
// Encrypt with Asset metadata
const encrypted = await ring.encrypt(content, {
asset: {
id: documentId,
type: 'document',
filename: filename,
owner: userId,
uploadedAt: new Date().toISOString(),
metadata: {
size: content.length,
mimeType: this.getMimeType(filename)
}
}
})
// Store encrypted data
const storagePath = `./encrypted/${documentId}.enc`
fs.writeFileSync(storagePath, encrypted)
console.log(`Document uploaded: ${documentId}`)
return {
success: true,
documentId,
storagePath
}
} catch (error) {
console.error('Upload failed:', error.message)
return { success: false, error: error.message }
}
}
getMimeType(filename) {
const ext = path.extname(filename).toLowerCase()
const mimeTypes = {
'.pdf': 'application/pdf',
'.doc': 'application/msword',
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'.txt': 'text/plain'
}
return mimeTypes[ext] || 'application/octet-stream'
}
async downloadDocument(documentId, outputPath) {
try {
// Read encrypted file
const storagePath = `./encrypted/${documentId}.enc`
const encrypted = fs.readFileSync(storagePath)
// Decrypt
const decrypted = await this.client.decrypt(encrypted)
// Write decrypted file
fs.writeFileSync(outputPath, decrypted)
console.log(`Document downloaded: ${outputPath}`)
return { success: true, outputPath }
} catch (error) {
console.error('Download failed:', error.message)
return { success: false, error: error.message }
}
}
async rotateKeys() {
const ring = await this.client.ring.get(this.keyringName)
await ring.rotate()
console.log('Keys rotated successfully')
}
}
// Usage
const client = await KeyClient.create({
host: 'https://your-tenant.grizzlycbg.io',
apikey: 'apikey-123456-1234567890'
})
const service = new SecureDocumentService(client)
await service.initialize()
// Upload document
await service.uploadDocument('user-123', 'doc-456', './contract.pdf')
// Download document
await service.downloadDocument('doc-456', './contract-decrypted.pdf')
// Periodic key rotation
await service.rotateKeys()
Next Steps
- SDK API Reference - Complete API documentation
- Error Handling & Troubleshooting - Common issues and solutions
- Security Best Practices - Secure implementation patterns
- Integration Examples - Framework-specific integration guides