Skip to content

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