Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ Delta requires macOS 26 or later.
1. Download the notarized DMG from the [latest release](https://github.com/dbuskariol/delta/releases/latest).
2. Open the DMG and drag Delta to Applications.
3. Open Delta from Applications and add a destination. Choose Delta encrypted backup for a restic profile, or Time Machine for a macOS-managed backup disk.
4. For Time Machine, keep the app at `/Applications/Delta.app`, approve File System Extensions and Background Items when macOS asks, connect the disk in Delta, then configure and inspect backups in macOS Time Machine. Delta refuses to register production system components from a renamed, temporary, or build-output copy.
4. For Time Machine, keep the app at `/Applications/Delta.app`. In System Settings, open **General → Login Items & Extensions**. Under Extensions, choose **By Category**. Click the info button beside **File System Extensions**, then turn on **Delta Time Machine Storage**. Approve Background Items when macOS asks, connect the disk in Delta, and configure and inspect backups in macOS Time Machine. Delta refuses to register production system components from a renamed, temporary, or build-output copy.
5. Run a first backup, inspect its authoritative history, and perform a test restore before considering the setup complete.

Public releases also include a signed ZIP for Sparkle updates, `SHA256SUMS`, external release notes, and a machine-readable provenance manifest.
Expand Down Expand Up @@ -184,7 +184,7 @@ Use **Settings → Updates** or **Updates** in the menu-bar panel. Delta checks
- **A scheduled backup did not run:** update to Delta 0.3.2 or later, confirm Scheduled Backups is enabled and approved in Login Items, then review pause, power, missed-run, source, destination, and saved-password status. Delta 0.3.2 repairs the stale missing-service registration that an earlier version could leave after an update; it does not require deleting profiles or backup data.
- **Saved Passwords needs repair:** use Settings → Permissions to review access and rewrite the Keychain access list for the currently signed Delta app.
- **A destination is unavailable or locked:** reconnect it, verify credentials, and ensure another restic client is not operating on the same repository.
- **A Time Machine disk will not connect:** confirm the app is named `Delta.app` directly in `/Applications`, then open Settings → Permissions, review File System Extensions and Time Machine System Support, approve any pending macOS Background Items request, and retry. Delta cannot grant these approvals itself.
- **A Time Machine disk will not connect:** confirm the app is named `Delta.app` directly in `/Applications`, then open Settings → Permissions and review File System Extensions and Time Machine System Support. In System Settings, open **General → Login Items & Extensions**. Under Extensions, choose **By Category**. Click the info button beside **File System Extensions**, then turn on **Delta Time Machine Storage**. Approve any pending macOS Background Items request and retry. Delta cannot grant these approvals itself.
- **A connected Time Machine disk reports a remote-storage error:** restore provider connectivity first, then use the destination's offered recovery action. Disconnect the disk before editing, checking, or removing its remote configuration; Delta deliberately keeps an uncertain or failed mount visible instead of claiming it was ejected.
- **An update is unavailable:** use Check Now, confirm network access to GitHub Releases, or install the notarized DMG manually.
- **Support needs evidence:** copy or export the sanitized diagnostic report from Settings. Known secrets, credential-bearing URLs, and personal home-directory names are redacted.
Expand Down
2 changes: 1 addition & 1 deletion Scripts/manual-acceptance-items.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ mounted_network_drive Mounted SMB or NFS destination Test at least one SMB or NF
sftp_destination SFTP destination Test a real SFTP destination with a non-root absolute path and non-interactive SSH authentication through a configured key file or ssh-agent; confirm wrong credential/key failure, corrected credential success, restore point refresh, and restore.
s3_destination S3-compatible destination Test at least one S3-compatible provider with endpoint, bucket, optional region, missing credential failure, corrected credential success, backup, check, and restore.
remote_backend_matrix Additional restic remote backends Test REST server, Backblaze B2, Azure Blob, Google Cloud Storage, OpenStack Swift, rclone, and custom restic URL destinations with provider-specific credentials/configuration; confirm backup, check, restore point refresh, selected restore, cleanup, and post-cleanup check.
time_machine_format Time Machine format Using the exact stable-signed installed candidate and an isolated disposable remote store, approve File System Extensions and Background Items, prove enabled system support is not accepted until the exact installed helper returns its authenticated readiness identity, create and connect a disk whose cache is smaller than its capacity, and confirm macOS identifies the case-sensitive APFS Backup volume and exact Time Machine destination. Complete first, no-change, and changed backups; browse and restore full and selected data; verify restored bytes; disconnect, relaunch, reconnect, and recover after removing local configuration. Measure cache and memory bounds, prove no complete sparsebundle or band payload is retained locally, exercise provider loss, upload and manifest faults, lease contention, cancellation, forced unmount, update re-registration, helper launch failure, partial-mount cleanup, and safe removal, and confirm every uncertain outcome fails without false connection, backup, or eject success. Repeat the remote lifecycle against the genuine provider families required for release evidence and exercise the native UI with keyboard and VoiceOver.
time_machine_format Time Machine format Using the exact stable-signed installed candidate and an isolated disposable remote store, open System Settings → General → Login Items & Extensions. Under Extensions, choose **By Category**. Click the info button beside **File System Extensions**, enable **Delta Time Machine Storage**, and confirm Delta's Permissions page updates to Allowed. Approve Background Items, prove enabled system support is not accepted until the exact installed helper returns its authenticated readiness identity, create and connect a disk whose cache is smaller than its capacity, and confirm macOS identifies the case-sensitive APFS Backup volume and exact Time Machine destination. Complete first, no-change, and changed backups; browse and restore full and selected data; verify restored bytes; disconnect, relaunch, reconnect, and recover after removing local configuration. Measure cache and memory bounds, prove no complete sparsebundle or band payload is retained locally, exercise provider loss, upload and manifest faults, lease contention, cancellation, forced unmount, update re-registration, helper launch failure, partial-mount cleanup, and safe removal, and confirm every uncertain outcome fails without false connection, backup, or eject success. Repeat the remote lifecycle against the genuine provider families required for release evidence and exercise the native UI with keyboard and VoiceOver.
remote_first_backup_preparation Remote first-backup preparation Add a new unprepared remote destination, start a backup without pressing Prepare first, and confirm Delta probes, prepares when missing, then runs the backup. Repeat with an existing remote destination and confirm Delta reuses it without reinitializing.
restore_wizard Restore wizard Test full restore, selected folder restore from the browser, selected file restore, dry-run preview, chosen-folder restore, original-path preview, original-path confirmation, and every overwrite policy.
restore_defaults Restore defaults Change Settings > Restore Defaults, reopen Restore, and confirm preview, verification, and overwrite policy defaults apply while remaining editable per restore.
Expand Down
18 changes: 4 additions & 14 deletions Sources/Delta/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3763,7 +3763,7 @@ struct SettingsView: View {
@ViewBuilder
private var timeMachineFileSystemPermissionActions: some View {
if hasTimeMachineDestinations, model.timeMachineFileSystemStatus != .enabled {
Button("System Settings") {
Button("Open Login Items & Extensions") {
model.openFileSystemExtensionsSettings()
}
.buttonStyle(.borderedProminent)
Expand Down Expand Up @@ -3846,19 +3846,9 @@ struct SettingsView: View {
}

private var timeMachineFileSystemPermissionDescription: String {
guard hasTimeMachineDestinations else {
return "Only required when a destination uses Time Machine format."
}
switch model.timeMachineFileSystemStatus {
case .enabled:
return "Delta's File System Extension is enabled and can present remote sparsebundle files to macOS."
case .disabled:
return "Open Login Items & Extensions, select File System Extensions, and turn on Delta Time Machine Storage."
case .notInstalled:
return "The File System Extension is missing from this Delta installation. Reinstall the signed app."
case let .unavailable(message):
return "macOS could not report File System Extension status: \(message)"
}
model.timeMachineFileSystemStatus.permissionDescription(
hasTimeMachineDestinations: hasTimeMachineDestinations
)
}

private var timeMachineSystemSupportDescription: String {
Expand Down
4 changes: 4 additions & 0 deletions Sources/DeltaCore/SettingsSurfaceContract.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ public enum SettingsSurfaceContract {
"System Settings",
"Show Delta",
"Open Login Items",
"Open Login Items & Extensions",
"Refresh Status",
"Send Test Alert",
"Restore Recommended",
Expand Down Expand Up @@ -139,6 +140,7 @@ public enum SettingsSurfaceContract {
"Run Due Now scheduled-backup action",
"Start at Login separate from Scheduled Backups",
"One Login Items recovery action per settings context",
"Category-specific File System Extensions approval guidance",
"Sparkle automatic check and download controls",
"Idle-sleep protection",
"Expandable Scheduled Backups explanation",
Expand Down Expand Up @@ -198,12 +200,14 @@ public enum SettingsSurfaceContract {
require(actionTitles, contains: "Check Now", in: "actions", failures: &failures)
require(actionTitles, contains: "Send Test Alert", in: "actions", failures: &failures)
require(actionTitles, contains: "Copy Report", in: "actions", failures: &failures)
require(actionTitles, contains: "Open Login Items & Extensions", in: "actions", failures: &failures)
require(requiredManualAcceptanceCoverage, contains: "Source access warning visibility through dashboard health", in: "manual coverage", failures: &failures)
require(requiredManualAcceptanceCoverage, contains: "Plain-language scheduled backup explanation", in: "manual coverage", failures: &failures)
require(requiredManualAcceptanceCoverage, contains: "No raw system service terminology", in: "manual coverage", failures: &failures)
require(requiredManualAcceptanceCoverage, contains: "Scheduled Backups activity shortcut", in: "manual coverage", failures: &failures)
require(requiredManualAcceptanceCoverage, contains: "Password access repair", in: "manual coverage", failures: &failures)
require(requiredManualAcceptanceCoverage, contains: "One Login Items recovery action per settings context", in: "manual coverage", failures: &failures)
require(requiredManualAcceptanceCoverage, contains: "Category-specific File System Extensions approval guidance", in: "manual coverage", failures: &failures)
require(requiredManualAcceptanceCoverage, contains: "Destination free-space warning control", in: "manual coverage", failures: &failures)
require(requiredManualAcceptanceCoverage, contains: "Configurable new-profile schedule defaults", in: "manual coverage", failures: &failures)
require(requiredManualAcceptanceCoverage, contains: "Expandable Scheduled Backups explanation", in: "manual coverage", failures: &failures)
Expand Down
18 changes: 18 additions & 0 deletions Sources/DeltaCore/TimeMachineSystemAccess.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,24 @@ public enum TimeMachineFileSystemExtensionStatus: Equatable, Sendable {
public var isReady: Bool {
self == .enabled
}

public func permissionDescription(
hasTimeMachineDestinations: Bool
) -> String {
guard hasTimeMachineDestinations else {
return "Only required when a destination uses Time Machine format."
}
switch self {
case .enabled:
return "Delta's File System Extension is enabled and can present remote sparsebundle files to macOS."
case .disabled:
return "In System Settings, open General → Login Items & Extensions. Under Extensions, choose By Category. Click the info button beside File System Extensions, then turn on Delta Time Machine Storage."
case .notInstalled:
return "The File System Extension is missing from this Delta installation. Reinstall the signed app."
case let .unavailable(message):
return "macOS could not report File System Extension status: \(message)"
}
}
}

public struct TimeMachineFileSystemModuleObservation: Equatable, Sendable {
Expand Down
2 changes: 2 additions & 0 deletions Tests/DeltaCoreTests/SettingsSurfaceContractTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ final class SettingsSurfaceContractTests: XCTestCase {
XCTAssertTrue(SettingsSurfaceContract.actionTitles.contains("Check Now"))
XCTAssertTrue(SettingsSurfaceContract.actionTitles.contains("Send Test Alert"))
XCTAssertTrue(SettingsSurfaceContract.actionTitles.contains("Copy Report"))
XCTAssertTrue(SettingsSurfaceContract.actionTitles.contains("Open Login Items & Extensions"))
XCTAssertTrue(SettingsSurfaceContract.capabilityTitles.contains("Approved by macOS"))
}

Expand All @@ -90,6 +91,7 @@ final class SettingsSurfaceContractTests: XCTestCase {
XCTAssertTrue(SettingsSurfaceContract.requiredManualAcceptanceCoverage.contains("Compact status summary"))
XCTAssertTrue(SettingsSurfaceContract.requiredManualAcceptanceCoverage.contains("Run Due Now scheduled-backup action"))
XCTAssertTrue(SettingsSurfaceContract.requiredManualAcceptanceCoverage.contains("One Login Items recovery action per settings context"))
XCTAssertTrue(SettingsSurfaceContract.requiredManualAcceptanceCoverage.contains("Category-specific File System Extensions approval guidance"))
XCTAssertTrue(SettingsSurfaceContract.requiredManualAcceptanceCoverage.contains("Expandable Scheduled Backups explanation"))
XCTAssertTrue(SettingsSurfaceContract.requiredManualAcceptanceCoverage.contains("Sparkle automatic check and download controls"))
XCTAssertTrue(SettingsSurfaceContract.requiredManualAcceptanceCoverage.contains("Idle-sleep protection"))
Expand Down
16 changes: 16 additions & 0 deletions Tests/DeltaCoreTests/TimeMachineSystemControllerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ import XCTest
@testable import DeltaCore

final class TimeMachineSystemControllerTests: XCTestCase {
func testFileSystemApprovalGuidanceUsesWorkingCategoryRoute() {
XCTAssertEqual(
TimeMachineFileSystemExtensionStatus.disabled
.permissionDescription(hasTimeMachineDestinations: true),
"In System Settings, open General → Login Items & Extensions. Under Extensions, choose By Category. Click the info button beside File System Extensions, then turn on Delta Time Machine Storage."
)
}

func testFileSystemApprovalGuidanceExplainsWhenItIsNotNeeded() {
XCTAssertEqual(
TimeMachineFileSystemExtensionStatus.disabled
.permissionDescription(hasTimeMachineDestinations: false),
"Only required when a destination uses Time Machine format."
)
}

func testSetupDeadlinesUseOneMonotonicBudgetAcrossCommands() {
let deadline = TimeMachineSetupDeadline(
duration: 10.25,
Expand Down
2 changes: 1 addition & 1 deletion docs/PRODUCTION_READINESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ Use the local acceptance probe report as supporting evidence while filling this
| SFTP destination | Test a real SFTP destination with a non-root absolute path and non-interactive SSH authentication through a configured key file or ssh-agent; confirm wrong credential/key failure, corrected credential success, restore point refresh, and restore. |
| S3-compatible destination | Test at least one S3-compatible provider with endpoint, bucket, optional region, missing credential failure, corrected credential success, backup, check, and restore. |
| Additional restic remote backends | Test REST server, Backblaze B2, Azure Blob, Google Cloud Storage, OpenStack Swift, rclone, and custom restic URL destinations with provider-specific credentials/configuration; confirm backup, check, restore point refresh, selected restore, cleanup, and post-cleanup check. |
| Time Machine format | Using the exact stable-signed installed candidate and an isolated disposable remote store, approve File System Extensions and Background Items, prove enabled system support is not accepted until the exact installed helper returns its authenticated readiness identity, create and connect a disk whose cache is smaller than its capacity, and confirm macOS identifies the case-sensitive APFS Backup volume and exact Time Machine destination. Complete first, no-change, and changed backups; browse and restore full and selected data; verify restored bytes; disconnect, relaunch, reconnect, and recover after removing local configuration. Measure cache and memory bounds, prove no complete sparsebundle or band payload is retained locally, exercise provider loss, upload and manifest faults, lease contention, cancellation, forced unmount, update re-registration, helper launch failure, partial-mount cleanup, and safe removal, and confirm every uncertain outcome fails without false connection, backup, or eject success. Repeat the remote lifecycle against the genuine provider families required for release evidence and exercise the native UI with keyboard and VoiceOver. |
| Time Machine format | Using the exact stable-signed installed candidate and an isolated disposable remote store, open System Settings → General → Login Items & Extensions. Under Extensions, choose **By Category**. Click the info button beside **File System Extensions**, enable **Delta Time Machine Storage**, and confirm Delta's Permissions page updates to Allowed. Approve Background Items, prove enabled system support is not accepted until the exact installed helper returns its authenticated readiness identity, create and connect a disk whose cache is smaller than its capacity, and confirm macOS identifies the case-sensitive APFS Backup volume and exact Time Machine destination. Complete first, no-change, and changed backups; browse and restore full and selected data; verify restored bytes; disconnect, relaunch, reconnect, and recover after removing local configuration. Measure cache and memory bounds, prove no complete sparsebundle or band payload is retained locally, exercise provider loss, upload and manifest faults, lease contention, cancellation, forced unmount, update re-registration, helper launch failure, partial-mount cleanup, and safe removal, and confirm every uncertain outcome fails without false connection, backup, or eject success. Repeat the remote lifecycle against the genuine provider families required for release evidence and exercise the native UI with keyboard and VoiceOver. |
| Remote first-backup preparation | Add a new unprepared remote destination, start a backup without pressing Prepare first, and confirm Delta probes, prepares when missing, then runs the backup. Repeat with an existing remote destination and confirm Delta reuses it without reinitializing. |
| Restore wizard | Test full restore, selected folder restore from the browser, selected file restore, dry-run preview, chosen-folder restore, original-path preview, original-path confirmation, and every overwrite policy. |
| Restore defaults | Change Settings > Restore Defaults, reopen Restore, and confirm preview, verification, and overwrite policy defaults apply while remaining editable per restore. |
Expand Down