Files
2026-09-08 20:06:58 +01:00

115 lines
5.0 KiB
Markdown

# 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:
```bash
./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:
```bash
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
```bash
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:
```bash
xcrun notarytool store-credentials "Rsync Studio Notary"
```
Submit a signed ZIP/DMG/PKG:
```bash
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:
```bash
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:
```json
{
"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.