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
24 changes: 12 additions & 12 deletions Delta.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -992,11 +992,11 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = Packaging/Delta.entitlements;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 15;
CURRENT_PROJECT_VERSION = 16;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = Packaging/Delta.app.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
MARKETING_VERSION = 0.4.0;
MARKETING_VERSION = 0.4.1;
PRODUCT_BUNDLE_IDENTIFIER = com.delta.backup;
PRODUCT_NAME = Delta;
SDKROOT = macosx;
Expand All @@ -1010,11 +1010,11 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = Packaging/Delta.entitlements;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 15;
CURRENT_PROJECT_VERSION = 16;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = Packaging/Delta.app.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
MARKETING_VERSION = 0.4.0;
MARKETING_VERSION = 0.4.1;
PRODUCT_BUNDLE_IDENTIFIER = com.delta.backup;
PRODUCT_NAME = Delta;
SDKROOT = macosx;
Expand Down Expand Up @@ -1122,13 +1122,13 @@
APPLICATION_EXTENSION_API_ONLY = YES;
CODE_SIGN_ENTITLEMENTS = "$(DELTA_FSKIT_CODE_SIGN_ENTITLEMENTS)";
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 15;
CURRENT_PROJECT_VERSION = 16;
DELTA_FSKIT_CODE_SIGN_ENTITLEMENTS = Packaging/DeltaTimeMachineFS.entitlements;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = Packaging/DeltaTimeMachineFS.Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Delta Time Machine Storage";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks";
MARKETING_VERSION = 0.4.0;
MARKETING_VERSION = 0.4.1;
PRODUCT_BUNDLE_IDENTIFIER = "com.delta.backup.timemachine-filesystem";
PRODUCT_NAME = DeltaTimeMachineFS;
SDKROOT = macosx;
Expand Down Expand Up @@ -1238,14 +1238,14 @@
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
CREATE_INFOPLIST_SECTION_IN_BINARY = YES;
CURRENT_PROJECT_VERSION = 15;
CURRENT_PROJECT_VERSION = 16;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = Packaging/DeltaTimeMachineHelper.Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 0.4.0;
MARKETING_VERSION = 0.4.1;
PRODUCT_BUNDLE_IDENTIFIER = "com.delta.backup.timemachine-helper";
PRODUCT_NAME = DeltaTimeMachineHelper;
SDKROOT = macosx;
Expand Down Expand Up @@ -1274,13 +1274,13 @@
APPLICATION_EXTENSION_API_ONLY = YES;
CODE_SIGN_ENTITLEMENTS = "$(DELTA_FSKIT_CODE_SIGN_ENTITLEMENTS)";
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 15;
CURRENT_PROJECT_VERSION = 16;
DELTA_FSKIT_CODE_SIGN_ENTITLEMENTS = Packaging/DeltaTimeMachineFS.entitlements;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = Packaging/DeltaTimeMachineFS.Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Delta Time Machine Storage";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks";
MARKETING_VERSION = 0.4.0;
MARKETING_VERSION = 0.4.1;
PRODUCT_BUNDLE_IDENTIFIER = "com.delta.backup.timemachine-filesystem";
PRODUCT_NAME = DeltaTimeMachineFS;
SDKROOT = macosx;
Expand Down Expand Up @@ -1309,14 +1309,14 @@
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
CREATE_INFOPLIST_SECTION_IN_BINARY = YES;
CURRENT_PROJECT_VERSION = 15;
CURRENT_PROJECT_VERSION = 16;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = Packaging/DeltaTimeMachineHelper.Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 0.4.0;
MARKETING_VERSION = 0.4.1;
PRODUCT_BUNDLE_IDENTIFIER = "com.delta.backup.timemachine-helper";
PRODUCT_NAME = DeltaTimeMachineHelper;
SDKROOT = macosx;
Expand Down
43 changes: 6 additions & 37 deletions Documentation/RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,41 +1,10 @@
# Delta 0.4.0
# Delta 0.4.1

Delta 0.4.0 adds native Time Machine-format destinations:
Delta 0.4.1 corrects the setup guidance for Time Machine-format destinations on macOS 26:

- Presents a Delta-managed encrypted APFS sparsebundle to macOS Time Machine without first staging the complete disk locally.
- Stores changed 8 MiB bands as immutable remote objects behind a strictly bounded cache, and acknowledges synchronization only after remote verification and authenticated generation publication.
- Treats the cache as a performance window rather than a backup-size limit: verified dirty bands spill to invisible content-addressed remote objects under pressure, so a user-selected cache size cannot make a larger valid backup fail with local `ENOSPC`.
- Keeps repeated DiskImages reads fast with a fixed two-band authenticated memory window, in-place band-buffer reuse across local and rclone reads, and per-request temporary-object lifetime, without retaining an attachment's complete read stream in memory.
- Supports local or mounted paths, SFTP, S3-compatible storage, Backblaze B2, Azure Blob, Google Cloud Storage, OpenStack Swift, and configured rclone remotes. Restic REST and custom restic URLs remain Delta-format only.
- Adds native destination creation, connection, disconnection, backup triggering, remote verification, recovery-key export, existing-disk reconnection, permission guidance, dashboard status, menu-bar status, diagnostics, and update-safety controls.
- Makes ordinary disconnect behave like unplugging a physical Time Machine disk: the APFS disk detaches while its exact macOS destination identity remains available for native reconnect. Explicit removal requires the verified disk to be connected before deregistration and local cleanup.
- Routes Time Machine add/remove prerequisites through Delta's existing Permissions surface. Missing Full Disk Access stops before connection writes begin, opens the exact recovery page, and never exposes tmutil's Terminal-specific diagnostic as product guidance.
- Uses a user-approved FSKit extension, same-user storage service, and narrowly scoped on-demand setup helper with signed-peer validation, bounded operations, conservative rollback, remote writer leases, and fail-closed recovery.
- Enforces an owner-only sparsebundle source directory before every attach and accepts identical authenticated band rewrites without manufacturing a redundant remote payload or invalid generation.
- Persists an authenticated generation-and-digest rollback witness, verifies an unbroken retained manifest chain before mounting or maintenance, and binds cleanup to the exact verified head so a provider rollback cannot silently become authoritative.
- Presents missing or corrupt remote Time Machine data as a verification failure with one `Check Again` recovery action, while Activity retains the exact redacted object evidence for diagnosis and Dashboard reuses the same typed, nontechnical guidance.
- Distinguishes a detached drive, unavailable mounted path, rejected remote command, or provider timeout from damaged history; existing disks are never recreated on a fallback volume and receive one `Storage Unavailable` recovery action.
- Labels destination evidence as `Last Verified`, so a preserved successful timestamp cannot be mistaken for the time of a newer failed check.
- Verifies that an app-managed disk recovery key is retained under the immutable remote disk identity before removing any local configuration or bounded cache, including safe migration from early repository-scoped development keys.
- Keeps the bundled rclone backend working when Delta is installed in a folder or app name containing spaces, while still pinning execution to the verified sibling tool.
- Keeps Time Machine and restic repository semantics separate: macOS remains authoritative for Time Machine history and restores, while restic continues to own Delta-format backup, restore, retention, prune, and check behavior.
- Attempts automatic Time Machine system-support replacement once per exact installed component fingerprint, preventing repeated Background Items churn while preserving one authoritative recovery action.
- Keeps an automatic system-support check from monopolizing the Set Up action, detects a stale Service Management path after the installed app moves, lets an explicit Set Up repair a stale helper or user-service registration, and preserves any component newly registered by that same request instead of forcing a second privileged approval.
- Keeps release rehearsals and evidence collection from launching or registering Service Management components from a transient build path. Identity-sensitive launch, Login Items, and helper acceptance now require the exact app installed under `/Applications`.
- Refuses production Time Machine registration and privileged mutations unless the running app is exactly `/Applications/Delta.app`, preventing renamed acceptance apps, DerivedData, `dist`, and worktree builds from replacing the installed Background Items state while retaining safe disconnect cleanup.
- Makes helper and user-service replacement resilient to macOS briefly retaining the retired Background Items record after asynchronous unregistration, using a bounded retry only while the public status remains unregistered.
- Re-registers changed Time Machine helper and storage-service bytes automatically after an idle app update through Apple's public Service Management lifecycle, avoiding a stale-helper connection timeout and preserving one authoritative Permissions recovery state if macOS requires approval.
- Requires a short authenticated readiness response from the exact embedded privileged helper before recording system support as current or mounting a disk, so an enabled-but-unlaunchable macOS background item fails before remote writes and routes to native Set Up recovery instead of consuming the twelve-minute mutation deadline.
- Keeps a partial FSKit/APFS mount in an explicit cleanup-only state: storage telemetry can no longer erase a system-connection failure, and no UI or command surface offers Back Up Now until macOS returns the exact Time Machine destination identity.
- Places the privileged setup daemon in Apple's current `SMAppService` executable layout and rejects the obsolete `SMJobBless` helper location, preventing update-time launch constraints from being bound to a legacy bundle structure.
- Adds a fail-closed clean-install release gate for the exact notarized app: after native administrator approval, Delta must launch and authenticate the embedded privileged helper by Security code hash, and candidate-bound evidence must match the app, helper, source commit, immutable build, and canonical `/Applications/Delta.app` path before publishing.
- Shows one authoritative Review Login Items action in Permissions when Time Machine system support and scheduled backups need the same macOS recovery surface.

This release also restores live Activity output following:

- Keeps the Output view scrolled to the newest line as backup logs arrive.
- Continues refreshing long runs after the bounded live-log window reaches 200 entries by tracking the newest persisted log rather than the unchanged row count.
- Retains bounded, lazy log rendering and stable loading of earlier output.
- Gives every timestamped output row one complete VoiceOver announcement containing its level and saved message.
- Directs users to **System Settings → General → Login Items & Extensions → By Category → File System Extensions** and the **Delta Time Machine Storage** switch—the system surface that reliably enables Delta's FSKit module.
- Removes the ambiguous **FSKit Modules** wording from Delta's instructions.
- Keeps the same concise instructions across Delta's Permissions page, setup guidance, README, and release acceptance matrix.
- Renames the recovery action to **Open Login Items & Extensions** so its destination is explicit.

Requires macOS 26 or later. Install from the notarized DMG for drag-to-Applications setup; the signed/notarized ZIP is provided for Sparkle updates and manual installation.
2 changes: 2 additions & 0 deletions Documentation/RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ A release must prove all of the following:

The Developer ID private key, Sparkle private key, notarization credentials, dSYMs, `.xcarchive`, and Apple evidence stay private. Only the DMG, ZIP, appcast, release notes, checksums, and release manifest are public assets.

The release-history audit accepts only the repository account's GitHub noreply identity (using either its handle or public display name) and GitHub's service committer identity. This keeps GitHub-created merge commits inside the exact allowlist without permitting unrelated author or committer identities. Identity, forbidden-name, private-key-path, Gitleaks, tag, ref, and Git-object integrity checks cover the complete reachable history. The previous public release tag is the immutable baseline for machine-specific home-path findings: each release rechecks its complete current tree and every object introduced since that baseline, permits only the finite generic user names used by redaction and path tests, and fails on any new or reintroduced machine-specific path.

The release build discovers an installed all-device FSKit distribution profile by its exact team, application identifier, and `com.apple.developer.fskit.fsmodule` grant. Xcode archives the complete graph with team-managed automatic provisioning so restricted capabilities and Swift-package resource targets retain coherent signing. The required `developer-id` export then uses Xcode's managed-profile flow to re-sign every nested component inside-out with the exact Developer ID certificate. The final validator independently requires the matching all-device profile, FSKit capability, App Sandbox, and exact Team-ID-prefixed macOS App Group on all three IPC peers, while rejecting client/server-network or temporary-exception entitlements on the extension. Team-ID-prefixed App Groups are a supported macOS form and do not require a separately provisioned group; they are not used here as a Keychain access group. Missing or mismatched provisioning stops before archive; the build never clears the shipping entitlement to work around provisioning.

## Rehearsal
Expand Down
10 changes: 6 additions & 4 deletions Documentation/VERIFICATION_REPORT.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ Date: 20 July 2026

Host: macOS 26.5.2 (25F84)

Candidate: Delta 0.4.0 (15)
Candidate: Delta 0.4.1 (16)

Status: exact-candidate verification contract

## Outcome

Delta's source, deterministic acceptance harnesses, Developer ID build, stable installer, notarization flow, Sparkle packaging, privacy controls, and production-readiness checks are covered by the release contract below. The source identifies the next candidate as `0.4.0` (15).
Delta's source, deterministic acceptance harnesses, Developer ID build, stable installer, notarization flow, Sparkle packaging, privacy controls, and production-readiness checks are covered by the release contract below. The source identifies the next candidate as `0.4.1` (16).

A release must not be merged, tagged, or published until the complete exact-commit gate, separate app and DMG notarization, stapling, Gatekeeper checks, signed-update installation, manual acceptance matrix, and required genuine external-provider evidence have all passed for this version. Generated evidence under `dist/` is authoritative for the commit, installed path, CDHash, notarization IDs, artifact hashes, command results, and acceptance status; this tracked report deliberately does not duplicate volatile identity values or claim that an unverified source commit has passed.

Expand All @@ -37,6 +37,8 @@ A release must not be merged, tagged, or published until the complete exact-comm
- Added a clean first-registration Time Machine system-support acceptance gate that uses the production Service Management and authenticated XPC path, binds evidence to the exact notarized installed candidate and helper code hashes, and prevents publishing from treating retained development registration state or a transient build location as release proof.
- Kept the Permissions surface to one authoritative Review Login Items action when Time Machine system support and scheduled backups both need attention, and bound the generated Xcode project and its source specification to the same immutable build identity.
- Refreshed the README Permissions image to show the current Time Machine File System and Time Machine System Support rows and the single shared Login Items recovery action.
- Replaced ambiguous per-app FSKit approval guidance with the working macOS By Category → File System Extensions route everywhere Delta presents or verifies setup instructions.
- Kept the release-history audit fail closed while allowing the repository account's approved GitHub noreply display name and GitHub's service committer on GitHub-created commits, and treating the previous public tag as the immutable baseline for historical machine-home-path findings while scanning the full current tree and every newly introduced object.

## Automated verification contract

Expand Down Expand Up @@ -71,7 +73,7 @@ Finalization requires separate accepted Apple submissions for the app archive an

## Required release handoff

1. Build, install, and complete all exact-commit automated, signing, Apple notarization, stapling, Gatekeeper, and evidence checks for `0.4.0` (15).
1. Build, install, and complete all exact-commit automated, signing, Apple notarization, stapling, Gatekeeper, and evidence checks for `0.4.1` (16).
2. Complete every row of the exact-candidate manual acceptance matrix, including a real signed Sparkle upgrade and system-delivered notifications.
3. Provide and exercise genuine external mounted SMB or NFS, non-local SFTP, and non-local S3-compatible fixtures, plus any additional backend families intended for this release.
4. Obtain a fully passing production-readiness result before merging, tagging `v0.4.0`, or publishing.
4. Obtain a fully passing production-readiness result before merging, tagging `v0.4.1`, or publishing.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,4 +202,4 @@ Use **Settings → Updates** or **Updates** in the menu-bar panel. Delta checks

## Status

Delta is in active development at version 0.4.0. The Time Machine architecture and recovery workflow are implemented on `main` but are not yet production-accepted or shipped. Deterministic and localhost evidence remains regression evidence, not a substitute for a complete installed Time Machine backup/restore, genuine external-provider acceptance, Intel runtime coverage, or the exact notarized release candidate. Public production readiness still depends on those runtime results plus all signing, Apple-service, notarization, stapling, Gatekeeper, Sparkle, and release-history gates.
Delta 0.4.1 is the current release. Time Machine-format destinations use macOS's native Time Machine disk format while Delta streams the sparsebundle's bands to the user's selected remote destination through a bounded local cache. Deterministic and localhost evidence remains regression evidence, not a substitute for complete installed-app, genuine external-provider, Intel runtime, signing, notarization, stapling, Gatekeeper, and Sparkle release validation.
Loading