| 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 <jeremy@intentsolutions.io> |
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
export enum DataCategory {
PUBLIC = 'public',
CONTACT = 'contact',
SENSITIVE = 'sensitive',
DERIVED = 'derived'
}
export const fieldClassification: Record<string, DataCategory> = {
name: DataCategory.PUBLIC,
title: DataCategory.PUBLIC,
company: DataCategory.PUBLIC,
location: DataCategory.PUBLIC,
linkedin_url: DataCategory.PUBLIC,
email: DataCategory.CONTACT,
phone: DataCategory.CONTACT,
personal_email: DataCategory.CONTACT,
salary: DataCategory.SENSITIVE,
compensation: DataCategory.SENSITIVE,
: .,
: .
};
(): <, []> {
: <, []> = {
[.]: [],
[.]: [],
[.]: [],
[.]: []
};
( field .(data)) {
category = fieldClassification[field] || .;
classified[category].(field);
}
classified;
}
Step 2: PII Handling
import crypto from 'crypto';
export class PIIHandler {
maskForLogging(profile: Profile): Record<string, any> {
return {
...profile,
email: this.maskEmail(profile.email),
phone: this.maskPhone(profile.phone),
personal_email: undefined
};
}
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;
;
}
(: , : ): {
iv = crypto.();
cipher = crypto.(, key, iv);
encrypted = cipher.(data, , );
encrypted += cipher.();
tag = cipher.();
;
}
(: , : ): {
[ivB64, tagB64, data] = encrypted.();
iv = .(ivB64, );
tag = .(tagB64, );
decipher = crypto.(, key, iv);
decipher.(tag);
decrypted = decipher.(data, , );
decrypted += decipher.();
decrypted;
}
}
Step 3: Retention Policies
export class RetentionPolicy {
private policies: Record<DataCategory, number> = {
[DataCategory.PUBLIC]: 365,
[DataCategory.CONTACT]: 90,
[DataCategory.SENSITIVE]: 30,
[DataCategory.DERIVED]: -1
};
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)) {
(days < ) ;
cutoff = ();
cutoff.(cutoff.() - days);
result = .(category , cutoff);
report. += result.;
report. += result.;
}
report;
}
(
: ,
:
): <{ : ; : }> {
db.$transaction( (tx) => {
expired = tx..({
: {
category,
: { : cutoff }
}
});
tx..({
: {
: { : expired.( e.) }
}
});
{
: expired.,
: expired.
};
});
}
}
cron.(, () => {
policy = ();
report = policy.();
logger.(, report);
});
Step 4: Data Subject Rights
export class DataRightsService {
async handleAccessRequest(subjectEmail: string): Promise<DataExport> {
const profiles = await db.profiles.findMany({
where: { email: subjectEmail }
});
const accessLogs = await db.accessLogs.findMany({
where: { profileEmail: subjectEmail }
});
return {
profiles,
accessLogs,
exportedAt: new Date(),
format: 'json'
};
}
async handleDeletionRequest(
subjectEmail: string,
requestId: string
): Promise<DeletionReport> {
const report: DeletionReport = {
requestId,
subjectEmail,
deletedRecords: 0,
status: 'completed',
completedAt: ()
};
db.$transaction( (tx) => {
deleted = tx..({
: { : subjectEmail }
});
report. += deleted.;
cache.();
tx..({
: {
requestId,
subjectEmail,
: report.,
: ()
}
});
});
.(subjectEmail, requestId);
report;
}
(
: ,
: <, >
): <> {
db..({
: { : subjectEmail },
: corrections
});
db..({
: {
: ,
subjectEmail,
: corrections
}
});
}
}
Step 5: Access Logging
export function logDataAccess(req: Request, res: Response, next: NextFunction) {
const originalJson = res.json.bind(res);
res.json = (data: any) => {
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(.);
}
(data);
};
();
}
Compliance Checklist
## 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
Next Steps
After data handling, see juicebox-enterprise-rbac for access controls.