juicebox-data-handling by jeremylongshore
Implement Juicebox data privacy and handling.Use when managing personal data, implementing GDPR compliance,or handling sensitive candidate information.Trigger with phrases like "juicebox data privacy", "juicebox GDPR","juicebox PII handling", "juicebox data compliance".
Content & Writing
1.4K Stars
171 Forks
Updated Jan 18, 2026, 11:36 PM
Why Use This
This skill provides specialized capabilities for jeremylongshore's codebase.
Use Cases
- Developing new features in the jeremylongshore repository
- Refactoring existing code to follow jeremylongshore standards
- Understanding and working with jeremylongshore's codebase structure
Install Guide
2 steps- 1
Skip this step if Ananke is already installed.
- 2
Skill Snapshot
Auto scan of skill assets. Informational only.
Valid SKILL.md
Checks against SKILL.md specification
Source & Community
Repository claude-code-plugins-plus-skills
Skill Version
main
Community
1.4K 171
Updated At Jan 18, 2026, 11:36 PM
Skill Stats
SKILL.md 362 Lines
Total Files 1
Total Size 9.2 KB
License MIT
--- name: juicebox-data-handling description: | Implement Juicebox data privacy and handling. Use when managing personal data, implementing GDPR compliance, or handling sensitive candidate information. Trigger with phrases like "juicebox data privacy", "juicebox GDPR", "juicebox PII handling", "juicebox data compliance". allowed-tools: Read, Write, Edit, Bash(kubectl:*), Bash(curl:*) version: 1.0.0 license: MIT author: Jeremy Longshore <[email protected]> --- # Juicebox Data Handling ## Overview Implement compliant data handling practices for personal and candidate data from Juicebox. ## Prerequisites - Understanding of applicable privacy regulations (GDPR, CCPA) - Data classification framework - Legal/compliance team sign-off ## Data Classification | Category | Examples | Retention | Access | |----------|----------|-----------|--------| | Public | Name, title, company | 1 year | All users | | Contact | Email, phone | 90 days | Recruiters only | | Sensitive | SSN, salary | 30 days | Admins only | | Derived | Scores, notes | Permanent | Internal only | ## Instructions ### Step 1: Data Classification System ```typescript // lib/data-classifier.ts export enum DataCategory { PUBLIC = 'public', CONTACT = 'contact', SENSITIVE = 'sensitive', DERIVED = 'derived' } export const fieldClassification: Record<string, DataCategory> = { // Public data name: DataCategory.PUBLIC, title: DataCategory.PUBLIC, company: DataCategory.PUBLIC, location: DataCategory.PUBLIC, linkedin_url: DataCategory.PUBLIC, // Contact data email: DataCategory.CONTACT, phone: DataCategory.CONTACT, personal_email: DataCategory.CONTACT, // Sensitive data salary: DataCategory.SENSITIVE, compensation: DataCategory.SENSITIVE, // Derived data fit_score: DataCategory.DERIVED, recruiter_notes: DataCategory.DERIVED }; export function classifyData(data: Record<string, any>): Record<DataCategory, string[]> { const classified: Record<DataCategory, string[]> = { [DataCategory.PUBLIC]: [], [DataCategory.CONTACT]: [], [DataCategory.SENSITIVE]: [], [DataCategory.DERIVED]: [] }; for (const field of Object.keys(data)) { const category = fieldClassification[field] || DataCategory.DERIVED; classified[category].push(field); } return classified; } ``` ### Step 2: PII Handling ```typescript // lib/pii-handler.ts import crypto from 'crypto'; export class PIIHandler { // Mask sensitive fields for logging maskForLogging(profile: Profile): Record<string, any> { return { ...profile, email: this.maskEmail(profile.email), phone: this.maskPhone(profile.phone), personal_email: undefined // Remove entirely }; } private maskEmail(email?: string): string | undefined { if (!email) return undefined; const [local, domain] = email.split('@'); return `${local[0]}***@${domain}`; } private maskPhone(phone?: string): string | undefined { if (!phone) return undefined; return `***-***-${phone.slice(-4)}`; } // Encrypt sensitive data at rest encrypt(data: string, key: Buffer): string { const iv = crypto.randomBytes(16); const cipher = crypto.createCipheriv('aes-256-gcm', key, iv); let encrypted = cipher.update(data, 'utf8', 'base64'); encrypted += cipher.final('base64'); const tag = cipher.getAuthTag(); return `${iv.toString('base64')}:${tag.toString('base64')}:${encrypted}`; } // Decrypt sensitive data decrypt(encrypted: string, key: Buffer): string { const [ivB64, tagB64, data] = encrypted.split(':'); const iv = Buffer.from(ivB64, 'base64'); const tag = Buffer.from(tagB64, 'base64'); const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv); decipher.setAuthTag(tag); let decrypted = decipher.update(data, 'base64', 'utf8'); decrypted += decipher.final('utf8'); return decrypted; } } ``` ### Step 3: Retention Policies ```typescript // lib/retention-policy.ts export class RetentionPolicy { private policies: Record<DataCategory, number> = { [DataCategory.PUBLIC]: 365, // 1 year [DataCategory.CONTACT]: 90, // 90 days [DataCategory.SENSITIVE]: 30, // 30 days [DataCategory.DERIVED]: -1 // No auto-delete }; getRetentionDays(category: DataCategory): number { return this.policies[category]; } async enforceRetention(): Promise<RetentionReport> { const report: RetentionReport = { processed: 0, deleted: 0, errors: [] }; for (const [category, days] of Object.entries(this.policies)) { if (days < 0) continue; // Skip no-delete categories const cutoff = new Date(); cutoff.setDate(cutoff.getDate() - days); const result = await this.deleteExpiredData(category as DataCategory, cutoff); report.processed += result.processed; report.deleted += result.deleted; } return report; } private async deleteExpiredData( category: DataCategory, cutoff: Date ): Promise<{ processed: number; deleted: number }> { // Implementation depends on data storage return db.$transaction(async (tx) => { const expired = await tx.profileData.findMany({ where: { category, createdAt: { lt: cutoff } } }); await tx.profileData.deleteMany({ where: { id: { in: expired.map(e => e.id) } } }); return { processed: expired.length, deleted: expired.length }; }); } } // Run retention daily cron.schedule('0 2 * * *', async () => { const policy = new RetentionPolicy(); const report = await policy.enforceRetention(); logger.info('Retention policy enforced', report); }); ``` ### Step 4: Data Subject Rights ```typescript // services/data-rights.ts export class DataRightsService { // Right to access async handleAccessRequest(subjectEmail: string): Promise<DataExport> { // Find all data for subject const profiles = await db.profiles.findMany({ where: { email: subjectEmail } }); // Include access logs const accessLogs = await db.accessLogs.findMany({ where: { profileEmail: subjectEmail } }); return { profiles, accessLogs, exportedAt: new Date(), format: 'json' }; } // Right to erasure (GDPR Article 17) async handleDeletionRequest( subjectEmail: string, requestId: string ): Promise<DeletionReport> { const report: DeletionReport = { requestId, subjectEmail, deletedRecords: 0, status: 'completed', completedAt: new Date() }; await db.$transaction(async (tx) => { // Delete profile data const deleted = await tx.profiles.deleteMany({ where: { email: subjectEmail } }); report.deletedRecords += deleted.count; // Delete from cache await cache.deleteByPattern(`*${subjectEmail}*`); // Log deletion for compliance await tx.deletionLogs.create({ data: { requestId, subjectEmail, deletedCount: report.deletedRecords, completedAt: new Date() } }); }); // Notify downstream systems await this.notifyDeletion(subjectEmail, requestId); return report; } // Right to rectification async handleRectificationRequest( subjectEmail: string, corrections: Record<string, any> ): Promise<void> { await db.profiles.updateMany({ where: { email: subjectEmail }, data: corrections }); // Log for audit await db.auditLogs.create({ data: { type: 'rectification', subjectEmail, changes: corrections } }); } } ``` ### Step 5: Access Logging ```typescript // middleware/access-logging.ts export function logDataAccess(req: Request, res: Response, next: NextFunction) { const originalJson = res.json.bind(res); res.json = (data: any) => { // Log access to profile data if (data?.profiles || data?.profile) { const profiles = data.profiles || [data.profile]; const profileIds = profiles.map((p: any) => p.id); db.accessLogs.create({ data: { userId: req.user?.id, profileIds, operation: req.method, path: req.path, timestamp: new Date(), ip: req.ip, userAgent: req.get('user-agent') } }).catch(console.error); } return originalJson(data); }; next(); } ``` ## Compliance Checklist ```markdown ## Data Handling Compliance ### GDPR Requirements - [ ] Lawful basis for processing documented - [ ] Privacy policy updated - [ ] Data subject rights implemented - [ ] Data breach notification process - [ ] DPA with Juicebox executed ### CCPA Requirements - [ ] "Do Not Sell" option implemented - [ ] Consumer rights portal - [ ] Opt-out mechanisms - [ ] Annual training completed ### Security - [ ] Encryption at rest - [ ] Encryption in transit - [ ] Access logging - [ ] Regular audits ``` ## Output - Data classification system - PII handling utilities - Retention policy enforcement - Data subject rights handlers ## Resources - [Juicebox Privacy Policy](https://juicebox.ai/privacy) - [GDPR Guidelines](https://gdpr.eu) ## Next Steps After data handling, see `juicebox-enterprise-rbac` for access controls.
Name Size