Files
RSync-Studio/Docs/RELEASE.md
T
2026-09-09 08:23:43 +01:00

5.2 KiB

Rsync Studio — Direct Distribution Release

Rsync Studio is designed for direct macOS distribution rather than the Mac App Store because it intentionally launches rsync/ssh and accesses user-selected arbitrary paths.

Release security baseline

  • Developer ID Application signing.
  • Hardened Runtime enabled.
  • App Sandbox intentionally disabled for this product architecture.
  • No Hardened Runtime exception entitlements.
  • SSH host-key verification is never disabled.
  • SSH uses BatchMode, connection timeout and keepalive limits.
  • Password storage/authentication is not supported; use a private key or the macOS SSH agent.
  • Advanced rsync arguments that can replace the SSH transport, run remote commands, load passwords, read/write arbitrary option/list/log/batch files or bypass controlled delete handling are rejected.
  • Scheduled/file-watch --delete requires explicit per-job authorisation.
  • Run logs are sanitised and capped before storage and expire according to Settings.
  • PrivacyInfo.xcprivacy declares no tracking/no collected data and the UserDefaults required-reason API.

Mandatory preflight

Before every release archive, run on the release Mac:

./Scripts/release-preflight.sh

This performs the static security policy checks, validates the privacy manifest, and performs a clean unsigned Release build with the installed macOS SDK so compiler/typecheck warnings or SDK incompatibilities are caught before signing.

Archive

In Xcode select Any Mac (Apple Silicon, Intel), choose Product > Archive, then distribute using Developer ID.

Alternatively:

xcodebuild -project "Rsync Studio.xcodeproj" -scheme "Rsync Studio" -configuration Release -archivePath "build/Rsync Studio.xcarchive" archive

Choose your Developer ID Application certificate/team in Xcode signing settings before archiving.

Verify signature

codesign --verify --deep --strict --verbose=2 "/path/to/Rsync Studio.app"
codesign -d --entitlements :- "/path/to/Rsync Studio.app"

The Release build should show Hardened Runtime and should not contain com.apple.security.get-task-allow.

Notarise

Store notarisation credentials in a Keychain profile once:

xcrun notarytool store-credentials "Rsync Studio Notary"

Submit a signed ZIP/DMG/PKG:

ditto -c -k --keepParent "/path/to/Rsync Studio.app" "Rsync Studio.zip"
xcrun notarytool submit "Rsync Studio.zip" --keychain-profile "Rsync Studio Notary" --wait

Staple and validate:

xcrun stapler staple "/path/to/Rsync Studio.app"
xcrun stapler validate "/path/to/Rsync Studio.app"
spctl --assess --type execute --verbose=4 "/path/to/Rsync Studio.app"

Pre-release functional security tests

  1. Local source nested inside destination is rejected.
  2. Local destination nested inside source is rejected.
  3. SSH host beginning with - is rejected.
  4. SSH username containing spaces/control characters is rejected.
  5. A .pub key cannot be selected as the effective private key.
  6. Advanced arguments -e, --rsh, --rsync-path, --password-file, --files-from, --log-file, --write-batch, --remove-source-files, --delete* and -M are rejected, and malformed quoting is rejected.
  7. Remote destination shell metacharacters are rejected before rsync is launched.
  8. Scheduled/watch job using --delete cannot run until Allow unattended delete is explicitly enabled.
  9. SSH to a previously unknown host does not silently bypass host-key checking.
  10. Scheduled SSH job with unavailable authentication fails rather than prompting indefinitely.
  11. History expiry removes old completed runs according to Settings.
  12. A failed persistent SwiftData store opens Recovery Mode instead of immediately crashing.
  13. Scripts/security-check.sh passes before every signed build.

Signed update feed

The application contains a dependency-free signed update checker. It remains disabled until these generated Info.plist values are populated in the Release configuration:

  • RsyncStudioUpdateManifestURL: HTTPS URL to the JSON manifest.
  • RsyncStudioUpdatePublicKey: Base64 raw Ed25519/Curve25519.Signing public key.

Manifest schema:

{
  "version": "1.2.0",
  "build": 120,
  "downloadURL": "https://example.com/releases/Rsync Studio-1.2.0.dmg",
  "signature": "BASE64_ED25519_SIGNATURE"
}

The exact signed bytes are UTF-8: version + "\n" + build + "\n" + downloadURL. The checker refuses HTTP feeds, unsigned manifests, invalid signatures, and non-HTTPS download URLs. It opens the verified signed download location; it does not silently install software. Keep the private signing key outside the source repository and build machine where practical.

Automated commercial release pipeline

Stage 2 adds Scripts/release.sh, which automates archive/export, signed-app verification, drag-to-Applications DMG creation, Apple notarization, stapling, Gatekeeper verification, SHA-256 generation and release metadata. It intentionally does not publish artifacts.

See Docs/COMMERCIAL_RELEASE_STAGE2.md for first-time setup and usage.

Stage 4 licensing backend

See Docs/COMMERCIAL_RELEASE_STAGE4.md and the Licensing Server/ Laravel overlay for production activation, purchase provisioning and webhook handling.