Skip to content

Privacy & Security Features

Last Updated: 2026-02-03 | Reading Time: 20 minutes

Comprehensive guide to PasteShelf’s privacy and security capabilities.



PasteShelf is built on the principle of privacy by design:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Privacy Principles β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β”‚
β”‚ 1. LOCAL FIRST β”‚
β”‚ All data stored locally by default β”‚
β”‚ No server-side processing β”‚
β”‚ No telemetry without explicit consent β”‚
β”‚ β”‚
β”‚ 2. MINIMAL DATA COLLECTION β”‚
β”‚ Only collect what's necessary β”‚
β”‚ No unnecessary metadata β”‚
β”‚ Clear data retention policies β”‚
β”‚ β”‚
β”‚ 3. USER CONTROL β”‚
β”‚ Full control over what's captured β”‚
β”‚ Easy data export and deletion β”‚
β”‚ Transparent about data handling β”‚
β”‚ β”‚
β”‚ 4. SECURITY BY DEFAULT β”‚
β”‚ Encryption enabled by default β”‚
β”‚ Sensitive data detection β”‚
β”‚ Secure app exclusion β”‚
β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

All clipboard data is protected at rest:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Data at Rest Protection β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Clipboard Item Storage β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚
β”‚ β”‚ β”‚ CoreData DB β”‚ β”‚ External Files β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ ──────────── β”‚ β”‚ ────────────── β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ SQLite with β”‚ β”‚ Images & binaries β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ encryption β”‚ β”‚ in App Support dir β”‚ β”‚ β”‚
β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
β”‚ β”‚ β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
β”‚ β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚
β”‚ β”‚ β”‚ FileVault β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ (System) β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ Full disk β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ encryption β”‚ β”‚ β”‚
β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Protection Layers:

LayerTechnologyProtection
ApplicationCoreData encryptionApp-level protection
File SystemFileVaultDisk encryption
KeychainSecure EnclaveSecrets protection

For Pro/Enterprise sync:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Data in Transit Protection β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β”‚
β”‚ Device A CloudKit Device B β”‚
β”‚ ──────── ──────── ──────── β”‚
β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚Plaintext β”‚ β”‚ Encrypted β”‚ β”‚Plaintext β”‚ β”‚
β”‚ β”‚ Data β”‚ β”‚ Data β”‚ β”‚ Data β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚ β–² β”‚ β”‚
β”‚ β–Ό β”‚ β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ AES-256 β”‚β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ AES-256 β”‚ β”‚
β”‚ β”‚ GCM β”‚ β”‚ GCM β”‚ β”‚
β”‚ β”‚ Encrypt β”‚ β”‚ Decrypt β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚ β–² β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ └──────────────── TLS 1.3 β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚
β”‚ Encryption: β”‚
β”‚ β€’ E2E: AES-256-GCM (before leaving device) β”‚
β”‚ β€’ Transport: TLS 1.3 (Apple infrastructure) β”‚
β”‚ β€’ Key exchange: ECDH on Curve25519 β”‚
β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Complete data removal:

// Delete single item
func deleteItem(_ item: ClipboardItem) async throws {
// Remove from CoreData
viewContext.delete(item)
try viewContext.save()
// Remove external files
if let contentPath = item.content?.externalPath {
try FileManager.default.removeItem(at: contentPath)
}
// Remove from search index
await searchIndex.remove(itemId: item.id)
// If synced, mark for deletion in CloudKit
if syncEnabled {
await syncEngine.markForDeletion(item.id)
}
}
// Clear all history
func clearAllHistory() async throws {
// Batch delete all items
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = ClipboardItem.fetchRequest()
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
try viewContext.execute(deleteRequest)
// Clear external storage
let supportDir = FileManager.default.urls(
for: .applicationSupportDirectory,
in: .userDomainMask
).first!.appendingPathComponent("PasteShelf/Content")
try FileManager.default.removeItem(at: supportDir)
// Rebuild search index
await searchIndex.rebuild()
}

PasteShelf automatically detects sensitive content:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Sensitive Data Detection β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β”‚
β”‚ Detection Categories: β”‚
β”‚ ───────────────────── β”‚
β”‚ β”‚
β”‚ πŸ”‘ CREDENTIALS β”‚
β”‚ β€’ Passwords (password managers, forms) β”‚
β”‚ β€’ API keys (AWS, GCP, Stripe, etc.) β”‚
β”‚ β€’ SSH private keys β”‚
β”‚ β€’ OAuth tokens β”‚
β”‚ β”‚
β”‚ πŸ’³ FINANCIAL β”‚
β”‚ β€’ Credit card numbers (Luhn validation) β”‚
β”‚ β€’ Bank account numbers β”‚
β”‚ β€’ Social Security Numbers β”‚
β”‚ β”‚
β”‚ πŸ₯ HEALTH β”‚
β”‚ β€’ Medical record numbers β”‚
β”‚ β€’ Health insurance IDs β”‚
β”‚ β”‚
β”‚ πŸ“§ PERSONAL β”‚
β”‚ β€’ Email addresses β”‚
β”‚ β€’ Phone numbers β”‚
β”‚ β€’ Physical addresses β”‚
β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
class SensitiveDataDetector {
private let patterns: [(name: String, regex: NSRegularExpression, severity: Severity)] = [
// API Keys
("AWS Access Key", try! NSRegularExpression(pattern: "AKIA[0-9A-Z]{16}"), .high),
("GitHub Token", try! NSRegularExpression(pattern: "ghp_[a-zA-Z0-9]{36}"), .high),
("Stripe Key", try! NSRegularExpression(pattern: "sk_live_[a-zA-Z0-9]{24}"), .high),
// Passwords
("Password Field", try! NSRegularExpression(pattern: "password[\"']?\\s*[:=]\\s*[\"'][^\"']+[\"']", options: .caseInsensitive), .high),
// Financial
("Credit Card", try! NSRegularExpression(pattern: "\\b(?:\\d[ -]*?){13,16}\\b"), .high),
("SSN", try! NSRegularExpression(pattern: "\\b\\d{3}-\\d{2}-\\d{4}\\b"), .high),
// Personal
("Email", try! NSRegularExpression(pattern: "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}"), .low),
("Phone", try! NSRegularExpression(pattern: "\\+?\\d{1,3}[-.\\s]?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}"), .low)
]
func analyze(_ content: String) -> SensitiveDataResult {
var detections: [Detection] = []
for pattern in patterns {
let matches = pattern.regex.matches(
in: content,
range: NSRange(content.startIndex..., in: content)
)
for match in matches {
detections.append(Detection(
type: pattern.name,
severity: pattern.severity,
range: match.range
))
}
}
// Additional validation (e.g., Luhn for credit cards)
detections = detections.filter { validateDetection($0, in: content) }
return SensitiveDataResult(
isSensitive: !detections.isEmpty,
detections: detections,
highestSeverity: detections.map(\.severity).max() ?? .none
)
}
}
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Sensitive Data Handling Options β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β”‚
β”‚ Preferences β†’ Privacy β†’ Sensitive Data: β”‚
β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ When sensitive data is detected: β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β—‹ Store normally β”‚ β”‚
β”‚ β”‚ ● Require authentication to view [Recommended] β”‚ β”‚
β”‚ β”‚ β—‹ Redact in history (show as β€’β€’β€’β€’β€’β€’) β”‚ β”‚
β”‚ β”‚ β—‹ Don't store at all β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ ───────────────────────────────────────────────── β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ Auto-delete sensitive items: β”‚ β”‚
β”‚ β”‚ β˜‘ Delete after 1 hour β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ ───────────────────────────────────────────────── β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β˜‘ Exclude from sync β”‚ β”‚
β”‚ β”‚ β˜‘ Exclude from search β”‚ β”‚
β”‚ β”‚ β˜‘ Exclude from export β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Biometric Authentication β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β”‚
β”‚ Supported Methods: β”‚
β”‚ ───────────────── β”‚
β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Touch ID β”‚ β”‚ Apple Watchβ”‚ β”‚ Password β”‚ β”‚
β”‚ β”‚ ───────── β”‚ β”‚ ───────────│ β”‚ ──────── β”‚ β”‚
β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ Fingerprintβ”‚ β”‚ Proximity β”‚ β”‚ Fallback when β”‚ β”‚
β”‚ β”‚ sensor on β”‚ β”‚ unlock via β”‚ β”‚ biometrics β”‚ β”‚
β”‚ β”‚ MacBook/ β”‚ β”‚ paired β”‚ β”‚ unavailable β”‚ β”‚
β”‚ β”‚ keyboard β”‚ β”‚ watch β”‚ β”‚ β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚
β”‚ Authentication Required For: β”‚
β”‚ ──────────────────────────── β”‚
β”‚ β”‚
β”‚ β˜‘ Viewing sensitive items β”‚
β”‚ β˜‘ Exporting clipboard history β”‚
β”‚ β˜‘ Changing security settings β”‚
β”‚ ☐ Opening PasteShelf (optional) β”‚
β”‚ ☐ Every paste action (optional) β”‚
β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
import LocalAuthentication
class BiometricAuth {
private let context = LAContext()
var biometryType: LABiometryType {
var error: NSError?
guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
return .none
}
return context.biometryType
}
func authenticate(reason: String) async throws -> Bool {
let context = LAContext()
context.localizedFallbackTitle = "Use Password"
// Check if biometrics available
var error: NSError?
guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
// Fall back to device password
return try await authenticateWithPassword(reason: reason)
}
return try await context.evaluatePolicy(
.deviceOwnerAuthenticationWithBiometrics,
localizedReason: reason
)
}
private func authenticateWithPassword(reason: String) async throws -> Bool {
let context = LAContext()
return try await context.evaluatePolicy(
.deviceOwnerAuthentication,
localizedReason: reason
)
}
}
class AutoLockManager {
@AppStorage("autoLockTimeout") var timeout: TimeInterval = 300 // 5 minutes
@Published var isLocked = false
private var lastActivity: Date = Date()
private var timer: Timer?
func startMonitoring() {
timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { [weak self] _ in
self?.checkLock()
}
}
func recordActivity() {
lastActivity = Date()
isLocked = false
}
private func checkLock() {
if Date().timeIntervalSince(lastActivity) > timeout {
isLocked = true
}
}
}

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Encryption Architecture β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β”‚
β”‚ Key Hierarchy: β”‚
β”‚ ───────────── β”‚
β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Master Key β”‚ β”‚
β”‚ β”‚ (Derived from user's Keychain) β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ β”‚ β”‚ β”‚
β”‚ β–Ό β–Ό β–Ό β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Database β”‚ β”‚ Content β”‚ β”‚ Sync β”‚ β”‚
β”‚ β”‚ Key β”‚ β”‚ Key β”‚ β”‚ Key ⭐ β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚ β”‚ β”‚ β”‚
β”‚ β–Ό β–Ό β–Ό β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ CoreData β”‚ β”‚ Binary β”‚ β”‚ CloudKit β”‚ β”‚
β”‚ β”‚ Store β”‚ β”‚ Files β”‚ β”‚ Records β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚
β”‚ Algorithms: β”‚
β”‚ β€’ Key derivation: PBKDF2 with 100,000 iterations β”‚
β”‚ β€’ Symmetric encryption: AES-256-GCM β”‚
β”‚ β€’ Key storage: Secure Enclave (when available) β”‚
β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
import CryptoKit
class EncryptionManager {
private let keychain = KeychainManager()
// Generate or retrieve master key
private func getMasterKey() throws -> SymmetricKey {
if let keyData = try? keychain.get("masterKey") {
return SymmetricKey(data: keyData)
}
// Generate new key
let key = SymmetricKey(size: .bits256)
try keychain.save("masterKey", data: key.dataRepresentation)
return key
}
// Encrypt data
func encrypt(_ data: Data) throws -> Data {
let key = try getMasterKey()
let sealedBox = try AES.GCM.seal(data, using: key)
return sealedBox.combined!
}
// Decrypt data
func decrypt(_ encryptedData: Data) throws -> Data {
let key = try getMasterKey()
let sealedBox = try AES.GCM.SealedBox(combined: encryptedData)
return try AES.GCM.open(sealedBox, using: key)
}
// Encrypt for sync (with per-device key exchange)
func encryptForSync(_ data: Data, recipientPublicKey: P256.KeyAgreement.PublicKey) throws -> Data {
let privateKey = try getSyncPrivateKey()
let sharedSecret = try privateKey.sharedSecretFromKeyAgreement(with: recipientPublicKey)
let symmetricKey = sharedSecret.hkdfDerivedSymmetricKey(
using: SHA256.self,
salt: Data(),
sharedInfo: Data("PasteShelf-Sync".utf8),
outputByteCount: 32
)
let sealedBox = try AES.GCM.seal(data, using: symmetricKey)
return sealedBox.combined!
}
}

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ App Exclusion β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β”‚
β”‚ Preferences β†’ Privacy β†’ Excluded Apps: β”‚
β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Never capture from these apps: β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚
β”‚ β”‚ β”‚ πŸ” 1Password [Built-in] β”‚ β”‚
β”‚ β”‚ β”‚ πŸ” Bitwarden [Built-in] β”‚ β”‚
β”‚ β”‚ β”‚ πŸ” LastPass [Built-in] β”‚ β”‚
β”‚ β”‚ β”‚ πŸ” Dashlane [Built-in] β”‚ β”‚
β”‚ β”‚ β”‚ πŸ” Keychain Access [Built-in] β”‚ β”‚
β”‚ β”‚ β”‚ πŸ’¬ Messages (private conversations) [Custom] β”‚ β”‚
β”‚ β”‚ β”‚ πŸ’Ό Slack (when in #private channel) [Custom] β”‚ β”‚
β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ [+ Add App] β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ ───────────────────────────────────────────────── β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β˜‘ Auto-detect password manager fields β”‚ β”‚
β”‚ β”‚ β˜‘ Exclude private browsing windows β”‚ β”‚
β”‚ β”‚ β˜‘ Exclude secure text fields β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
struct CaptureRule {
let id: UUID
let name: String
let condition: CaptureCondition
let action: CaptureAction
enum CaptureCondition {
case app(bundleId: String)
case contentType(uti: String)
case contentMatches(regex: String)
case windowTitle(contains: String)
case isSecureTextField
case isPrivateBrowsing
}
enum CaptureAction {
case capture
case skip
case captureButMarkSensitive
case captureButEncrypt
}
}
// Default rules
let defaultRules: [CaptureRule] = [
CaptureRule(
name: "Skip password managers",
condition: .app(bundleId: "com.1password.*"),
action: .skip
),
CaptureRule(
name: "Skip secure text fields",
condition: .isSecureTextField,
action: .skip
),
CaptureRule(
name: "Mark API keys as sensitive",
condition: .contentMatches(regex: "(api[_-]?key|secret)[\"']?\\s*[:=]"),
action: .captureButMarkSensitive
)
]
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Data Retention Settings β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β”‚
β”‚ Preferences β†’ Privacy β†’ Data Retention: β”‚
β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Keep clipboard items for: β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β—‹ Forever β”‚ β”‚
β”‚ β”‚ β—‹ 1 year β”‚ β”‚
β”‚ β”‚ ● 90 days [Recommended] β”‚ β”‚
β”‚ β”‚ β—‹ 30 days β”‚ β”‚
β”‚ β”‚ β—‹ 7 days β”‚ β”‚
β”‚ β”‚ β—‹ 1 day β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ ───────────────────────────────────────────────── β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ Maximum items: [1000 ] β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ ───────────────────────────────────────────────── β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β˜‘ Automatically delete duplicates β”‚ β”‚
β”‚ β”‚ β˜‘ Clear history on quit β”‚ β”‚
β”‚ β”‚ β˜‘ Delete sensitive items after 1 hour β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ [Clear All History Now] β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Supported providers:

  • Okta
  • Azure AD
  • Google Workspace
  • OneLogin
  • Custom SAML 2.0 / OIDC
// SSO Configuration
struct SSOConfiguration {
let provider: SSOProvider
let clientId: String
let issuer: URL
let redirectURI: URL
let scopes: [String]
// SAML specific
var samlMetadataURL: URL?
var samlEntityId: String?
// OIDC specific
var oidcDiscoveryURL: URL?
}
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Audit Logging β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β”‚
β”‚ Events logged: β”‚
β”‚ ───────────── β”‚
β”‚ β”‚
β”‚ β€’ User authentication (success/failure) β”‚
β”‚ β€’ Clipboard item capture β”‚
β”‚ β€’ Clipboard item access (view/paste) β”‚
β”‚ β€’ Item deletion β”‚
β”‚ β€’ Export operations β”‚
β”‚ β€’ Settings changes β”‚
β”‚ β€’ Sync events β”‚
β”‚ β€’ Policy violations β”‚
β”‚ β”‚
β”‚ Log format (JSON): β”‚
β”‚ ───────────────── β”‚
β”‚ β”‚
β”‚ { β”‚
β”‚ "timestamp": "2026-02-03T12:00:00Z", β”‚
β”‚ "eventType": "item.accessed", β”‚
β”‚ "userId": "[email protected]", β”‚
β”‚ "deviceId": "device_abc123", β”‚
β”‚ "itemId": "550e8400-e29b-...", β”‚
β”‚ "action": "paste", β”‚
β”‚ "metadata": { β”‚
β”‚ "targetApp": "com.apple.mail", β”‚
β”‚ "contentType": "public.plain-text" β”‚
β”‚ } β”‚
β”‚ } β”‚
β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
// DLP Policy
struct DLPPolicy {
let id: UUID
let name: String
let rules: [DLPRule]
let action: DLPAction
let notification: DLPNotification
struct DLPRule {
let type: RuleType
let pattern: String
enum RuleType {
case regex
case keyword
case contentType
case fileExtension
}
}
enum DLPAction {
case allow
case block
case encrypt
case notify
case quarantine
}
}
// Example: Block credit card numbers from leaving secure apps
let creditCardPolicy = DLPPolicy(
name: "Credit Card Protection",
rules: [
DLPRule(type: .regex, pattern: "\\b(?:\\d[ -]*?){13,16}\\b")
],
action: .block,
notification: .alertAdmin
)

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Security Self-Assessment β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β”‚
β”‚ Access Control β”‚
β”‚ β˜‘ Biometric authentication enabled β”‚
β”‚ β˜‘ Auto-lock configured β”‚
β”‚ β˜‘ No unauthorized users β”‚
β”‚ β”‚
β”‚ Data Protection β”‚
β”‚ β˜‘ FileVault enabled on this Mac β”‚
β”‚ β˜‘ Encryption keys in Keychain β”‚
β”‚ β˜‘ Sensitive data detection on β”‚
β”‚ β”‚
β”‚ Privacy Settings β”‚
β”‚ β˜‘ Password managers excluded β”‚
β”‚ β˜‘ Private browsing excluded β”‚
β”‚ β˜‘ Data retention configured β”‚
β”‚ β”‚
β”‚ Sync Security (Pro) β”‚
β”‚ β˜‘ E2E encryption enabled β”‚
β”‚ β˜‘ Only trusted devices synced β”‚
β”‚ ☐ Sync disabled (highest security) β”‚
β”‚ β”‚
β”‚ Overall Score: 9/10 βœ“ Excellent β”‚
β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

DocumentDescription
SecuritySecurity architecture
Legal & ComplianceGDPR, HIPAA
Enterprise AdminAdmin setup

Last updated: 2026-02-03