Privacy & Security Features
Last Updated: 2026-02-03 | Reading Time: 20 minutes
Comprehensive guide to PasteShelfβs privacy and security capabilities.
Table of Contents
Section titled βTable of Contentsβ- Privacy Philosophy
- Data Protection
- Sensitive Data Handling
- Authentication
- Encryption
- Privacy Controls
- Enterprise Security
- Security Audit
Privacy Philosophy
Section titled βPrivacy Philosophyβ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 ββ ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββData Protection
Section titled βData ProtectionβData at Rest π
Section titled βData at Rest πβ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:
| Layer | Technology | Protection |
|---|---|---|
| Application | CoreData encryption | App-level protection |
| File System | FileVault | Disk encryption |
| Keychain | Secure Enclave | Secrets protection |
Data in Transit β
Section titled βData in Transit ββ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 ββ ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββData Deletion π
Section titled βData Deletion πβComplete data removal:
// Delete single itemfunc 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 historyfunc 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()}Sensitive Data Handling
Section titled βSensitive Data HandlingβAutomatic Detection π
Section titled βAutomatic Detection πβ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 ββ ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββDetection Patterns
Section titled βDetection Patternsβ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 ) }}Handling Options
Section titled βHandling Optionsβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ 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 β ββ βββββββββββββββββββββββββββββββββββββββββββββββββββββββ ββ ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββAuthentication
Section titled βAuthenticationβBiometric Authentication π
Section titled βBiometric Authentication πβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ 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) ββ ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββImplementation
Section titled βImplementationβ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 ) }}Auto-Lock π
Section titled βAuto-Lock πβ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
Section titled βEncryptionβEncryption Architecture π
Section titled βEncryption Architecture πβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ 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) ββ ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββCryptoKit Implementation
Section titled βCryptoKit Implementationβ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! }}Privacy Controls
Section titled βPrivacy ControlsβApp Exclusion π
Section titled βApp Exclusion πβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ 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 β ββ βββββββββββββββββββββββββββββββββββββββββββββββββββββββ ββ ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββCapture Rules π
Section titled βCapture Rules πβ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 ruleslet 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 π
Section titled βData Retention πβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ 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] β ββ βββββββββββββββββββββββββββββββββββββββββββββββββββββββ ββ ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββEnterprise Security π’
Section titled βEnterprise Security π’βSSO Integration
Section titled βSSO IntegrationβSupported providers:
- Okta
- Azure AD
- Google Workspace
- OneLogin
- Custom SAML 2.0 / OIDC
// SSO Configurationstruct 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 π’
Section titled βAudit Logging π’βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ 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" ββ } ββ } ββ ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββData Loss Prevention π’
Section titled βData Loss Prevention π’β// DLP Policystruct 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 appslet creditCardPolicy = DLPPolicy( name: "Credit Card Protection", rules: [ DLPRule(type: .regex, pattern: "\\b(?:\\d[ -]*?){13,16}\\b") ], action: .block, notification: .alertAdmin)Security Audit
Section titled βSecurity AuditβSelf-Assessment Checklist
Section titled βSelf-Assessment Checklistβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ 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 ββ ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββRelated Documentation
Section titled βRelated Documentationβ| Document | Description |
|---|---|
| Security | Security architecture |
| Legal & Compliance | GDPR, HIPAA |
| Enterprise Admin | Admin setup |
Last updated: 2026-02-03