FILE NUOVO │ ▼ WATCHER │ ▼ photo_ingest_jobs │ ▼ PHOTO DAEMON [I] │ ▼ runOneJob() │ ▼ scanPhotoSingle() │ ┌─────────┼─────────┐ │ │ │ ▼ ▼ ▼ photos thumbnails geocode_jobs │ │ │ ▼ │ GEOCODE DAEMON [G] │ │ └─ SQLite trigger │ ▼ face_scan_jobs │ ▼ FACE DAEMON [F] SCAN │ ▼ scanner foto │ ▼ scanFile.js │ ┌──────┴───────┐ │ │ ▼ ▼ processFile() foto esistente? │ ▼ thumbnails │ ▼ photos DB │ ├──────────────► trigger SQLite │ │ │ ▼ │ face_scan_jobs │ │ │ ▼ │ FACE DAEMON │ ▼ photo_changes added / updated PHOTO DAEMON │ │ foto salvata + thumbnail pronte │ job = done ▼ enqueueServerEvent() │ ▼ server_event_outbox │ │ consumer ogni ~250 ms ▼ SERVER [S] │ ▼ broadcastPhotoEvent(user, { type: "added", id: photoId }) │ ▼ WebSocket :4002 │ ▼ Browser │ ▼ sync.js │ ▼ enqueueAdded(id) │ ▼ GALLERIA AGGIORNATA