Generalize the photos-originals rclone-mount pattern to a video-projects prefix so the video studio (and imajin ETL, per storage-portability-plan §2.3) can read/write multi-GB project sources/renders as local files while only hot data stays resident on plum (bounded VFS LRU cache). Lets a small-disk laptop work with large footage without filling APFS. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
5.5 KiB
Dev setup
How to get @mac-sync running on a development machine.
Prerequisites
- macOS 13+ (matches
Package.swift:7platform requirement) - Xcode command-line tools (
swift --version>= 5.9, perPackage.swift:1) - Bun — server and web runtime (
src/server/package.json:7-10,web/package.json:7-10) - PostgreSQL 14+ with
gen_random_uuid()available (used inSendQueueRepo.ts:152) - For the Linux server target: systemd (
app.manifest.yaml:42)
Clone and resolve packages
cd ~/Code/@applications
git clone <repo> @mac-sync
cd @mac-sync
swift package resolve # pulls GRDB, Alamofire, SwiftyJSON, Swifter
Local SwiftPM dependencies (Package.swift:9-15) live in
~/Code/@packages/@swift/:
agent-core,menu-bar,sync-framework,logging,settings@tray/tray-resources
If swift package resolve complains about a missing local package, clone the
corresponding repo into ~/Code/@packages/@swift/<scope>/<name>/.
Build the Mac client
make build # Makefile:59-61 -> swift build --product MacSyncApp
make run # Makefile:78-79 -> .build/debug/MacSyncApp
make build produces .build/debug/MacSyncApp. The app is a menu-bar
executable; it stays in the dock-less tray once running. Look for the
MacSyncShared/Storage/ActivityLog entries in stderr to confirm modules are
ticking.
Tests:
make test # Makefile:71-73 -> swift test (all test targets in Package.swift:113-157)
Boot the server
cd src/server
cp .env.example .env # if present; otherwise set vars manually
bun install
bun run dev # src/server/package.json:7
Required environment (read by src/server/src/app/config.ts):
QUINN_MACSYNC_DB_URL=postgres://…— Postgres connection stringSERVICE_TOKEN— bearer for/my/*and/admin/*(src/server/src/app/server.ts:136,140)SSO_VALIDATE_URL— endpoint for the alternativessoRequiredgate on/my/*MODEL_BOSS_EMBED_URL— embedding inference endpoint (required, no default — fails fast rather than dialing a baked LAN host)
Object storage (photo/attachment blobs), selected by STORAGE_BACKEND:
STORAGE_BACKEND=local(dev default) → blobs underSTORAGE_LOCAL_PATH(./data/blobs). No other vars needed.STORAGE_BACKEND=s3→ any S3-compatible store (MinIO / DO Spaces / AWS / R2), always signed (SigV4). SetS3_ENDPOINT,S3_ACCESS_KEY,S3_SECRET_KEY,S3_BUCKET,S3_REGION(defaultus-east-1),S3_FORCE_PATH_STYLE(truefor MinIO and thelilith-quinn-mediaSpaces bucket),S3_PRESIGN_TTL_SECONDS(default900). Dev MinIO is provided bydocker-compose.dev.yml.
The server listens on the port from app.manifest.yaml:44 (3201 in prod).
First boot runs all migrations in order:
src/server/src/app/server.ts:36-52.
Health checks:
curl http://localhost:3201/health # server.ts:56
curl http://localhost:3201/health/deep # server.ts:57-66 — also pings the DB
Boot the web SPA
cd web
bun install
bun run dev # vite --port 5200 (web/package.json:7)
The dev server hot-reloads against the API on localhost:3201. For production
the SPA is built (bun run build) into web/dist/ and that directory is
shipped inside the Mac app bundle (webapp/ subresource) or served by the
server.
Wire the Mac to the server
LocalWebServer (@packages/shared/Sources/MacSyncShared/WebServer/LocalWebServer.swift:42-110)
exposes the dev SPA on http://localhost:8765 and an editable settings
endpoint:
curl http://localhost:8765/api/settings
# {"serverURL":"http://localhost:3201","webServerPort":8765, …}
curl -X PUT http://localhost:8765/api/settings \
-H 'content-type: application/json' \
-d '{"serverURL":"http://localhost:3201"}'
For local end-to-end work, set serverURL to http://localhost:3201 and
restart MacSyncApp so DeviceRegistration re-registers against the local
backend.
Permissions to grant on first run
Each module needs an OS permission. Approve when prompted:
- iMessage: Full Disk Access (to read
~/Library/Messages/chat.db). Module surfaces.fullDiskAccessRequiredif missing (@packages/imessage/Sources/IMessageSync/SyncManager.swift:36). - iPhoto: Photos library access (
PHPhotoLibrary). Modal handled byIPhotoSync.requestAuthorization(). - iMail: Automation > Mail.
- iCal: Calendars (
EKEventStore.requestFullAccessToEvents). - iReminders: Reminders (
EKEventStore.requestFullAccessToReminders). - iNotes: Automation > Notes
(
@packages/inotes/Sources/INoteSync/SyncManager.swift:96-100).
If you decline, the module sets its error state (e.g.
.calendarAccessRequired) and skips cycles until you re-grant. Click the
menu-bar status row to deep-link into System Settings.
Running a single test target
swift test --filter MacSyncSharedTests
swift test --filter IMessageSyncTests
swift test --filter ICalSyncTests
All test targets are declared in Package.swift:113-157.
Common dev problems
make buildfails with "no such package": the local SwiftPM siblings under~/Code/@packages/@swift/are missing. Clone them, thenswift package resolveagain.bun run dev(server) fails ongen_random_uuid(): enable thepgcryptoextension on the Postgres database (or use Postgres 13+).- The Mac app starts but the menu icon never appears: check stderr for
LocalWebServer: webapp directory not found(LocalWebServer.swift:151). In dev, the resolver expects<repo>/web/dist/; runcd web && bun run buildonce.