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
73 changes: 73 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions GLOSSARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@ _Avoid_: settings dialog, preferences
The status icon in the notification area. It shows sync state and it opens the config window.
_Avoid_: system tray, notification icon

**Pause**:
The user's hold on new uploads, from the dashboard control or the tray menu. It is separate from the known-network hold, so resuming never overrides the list.
_Avoid_: stop, disable sync

**Folder status**:
The sync state of one source folder. The dashboard shows one card per source folder, and the folder detail renders the same status in full.
_Avoid_: folder state, folder progress
Expand All @@ -163,6 +167,22 @@ _Avoid_: add dialog, onboarding
The enlarged view of one source folder: its full path, live state and counts, its own upload activity, settings, timestamps, and the list of the assets it has uploaded, with links into the Failed and Review views filtered to it. Its Remove needs a confirmation, because the folder's uploaded assets stay on the server.
_Avoid_: folder modal, folder popup

**Known networks**:
The user's list of network names for the **Sync only on known networks** setting, compared against the name the OS reports for the current wireless network. A wired link always syncs, because it has no name to match. The list is off by default.
_Avoid_: allowed networks, trusted networks, whitelist

**Network check**:
The client's one read of the OS's current network, through a **network source** per platform. It decides whether uploads are held, and its answer is what the Settings page shows as the current network.
_Avoid_: SSID lookup, network probe, reachability check

**Network source**:
The platform backend that answers the network check: NetworkManager over D-Bus on Linux, the Network List Manager on Windows, and CoreWLAN with CaptiveNetwork on macOS. It also reports the OS's network-change events, which the client uses to re-check before the 30-second fallback poll.
_Avoid_: provider, adapter, detector

**Pause reason**:
Why uploads are held: the user's own **pause**, or the known-network list. Both read as the paused activity state; the dashboard and the tray tooltip phrase the reason.
_Avoid_: pause kind, pause cause, hold type

### Diagnostics

**Client version**:
Expand Down
34 changes: 32 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,14 @@ copy and its shortcuts.
Drive, or on a removable or network volume, asks for access to files and
folders. Allow it; without the grant the folder shows as unreadable, with the
reason (see [Troubleshooting](#troubleshooting)).
4. Turning **Sync only on known networks** on asks for Location permission,
because macOS reveals the Wi-Fi network name only to an app that has it. The
unsigned build cannot register for the permission the way a signed app does,
so if macOS refuses it the Settings page says the name is unknown, offers
**Open Location Settings**, and the list holds uploads; turning the list off
restores syncing on any network. The prompt only works from the installed
**Immich Sync** app: a build run out of `target/` is not an app bundle, so
macOS cannot show it and the page stays unknown.

To uninstall, first turn **Autostart at login** off if macOS refused the
unsigned login item: that removes the LaunchAgent at
Expand Down Expand Up @@ -254,8 +262,10 @@ its card instead of producing one error per file.
**Trash** for it. A rename is recognized by content and is not a deletion.
- **Settings.** Changes save immediately. General holds **Autostart at login**
(off by default); Limits holds the bandwidth limit (0 means unlimited) and
concurrent uploads; Sync holds the reconciliation scan interval and the retry
attempt cap; Tagging holds **Tag uploaded assets** (on by default), which adds
concurrent uploads; Sync holds the reconciliation scan interval, the retry
attempt cap, and **Sync only on known networks** (off by default) with the
list of network names beside it; Tagging holds **Tag uploaded assets** (on by
default), which adds
the fixed tag `immich-sync` to every asset the client uploads. With an API
key, tagging needs the `tag.create`, `tag.asset`, and `asset.update`
permissions. The same section shows the backfill's progress and **Tag existing
Expand All @@ -272,6 +282,12 @@ its card instead of producing one error per file.
newest file while the view is open; scrolling up pauses it and shows the new
line count with **Jump to latest**, and **Refresh** re-reads the tail once.
The log never contains the password, API key, or session token.
- **Known networks.** With **Sync only on known networks** on, the client
pauses unless the OS names the current Wi-Fi network in your list, and shows
the name the OS reports beside the list. Nothing fails and the queue is kept;
syncing resumes by itself when a listed network comes back. Ethernet always
syncs, and with the list off the client syncs on any network. The name is read
from the OS on this computer and is never sent anywhere.
- The theme follows the system theme.

## Where everything lives
Expand Down Expand Up @@ -369,6 +385,20 @@ identity.
After an update, the login-Keychain entry and the **Files & Folders** grant can
ask again. Allow them again and syncing continues; nothing is lost.

### Sync is paused and the reason names the network

**Sync only on known networks** is on and the client cannot prove the current
network is on your list. The Settings page shows the name the OS reports
("unknown" with the reason when it cannot name it), and the dashboard and tray
say why. Add the network to the list, connect to a listed network, or turn the
setting off. Ethernet always syncs. On Linux the name comes from NetworkManager,
so a machine using systemd-networkd or `iwd` reports no name and stays paused
while the setting is on; on macOS the name needs the Location grant, and the
Settings page has **Open Location Settings** when it is missing (the prompt
appears only in the installed app bundle, not in a `cargo run` build). Full
checks per platform are in
[docs/network-hand-checks.md](docs/network-hand-checks.md).

### Sync does not pick up a new file

Hidden files, symlinks, temporary files, and system files are skipped by
Expand Down
3 changes: 2 additions & 1 deletion SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,9 @@ Acceptance: a sync at a 1 MB/s limit produces near-limit observed throughput. Ra
- R8.5 Autostart at login is a setting. The default is off. Windows uses its Run key, Linux an XDG autostart entry, and macOS a Login Item (`SMAppService.mainApp`), falling back to a LaunchAgent plist when an unsigned bundle is refused.
- R8.6 The theme follows the system theme.
- R8.7 Pause stops new uploads. In-flight uploads finish or stop at a safe point.
- R8.8 **Sync only on known networks** is a setting, off by default, with a list of names the user types. While it is on, uploads and scans wait unless the OS names the current wireless network in the list: no new attempts, the queue is kept, nothing fails, and syncing resumes on its own when a listed network comes back. A wired connection always syncs, and the list's help text says so. It fails closed: an unknown name, a missing or empty OS answer, an OS call that errors or times out, and no network at all hold uploads, and the dashboard and tray say why ("Paused — not on a known network", "Paused — network unknown"). It checks at startup, on the OS's network-change events, on a 30-second fallback poll, and after any upload network error. The Settings page shows the name the OS reports beside the list.

Acceptance: every tray action works from a cold start. A second launch does not start a second process. Pause stops new uploads within 5 seconds.
Acceptance: every tray action works from a cold start. A second launch does not start a second process. Pause stops new uploads within 5 seconds. Leaving a listed network holds uploads within 5 seconds, the queue keeps every waiting file, and returning to a listed network resumes without a click.

### R9 — Settings and paths

Expand Down
37 changes: 37 additions & 0 deletions crates/app/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ embed-resource = "3"
# (ADR 0006 research §3.2). `crypto-rust` keeps the D-Bus transport encryption
# in Rust, so the build needs no OpenSSL.
dbus-secret-service-keyring-store = { version = "=1.0.1", features = ["crypto-rust"] }
# The known-network check (R8.8): NetworkManager's primary connection. The
# crate is already in the tree through the keyring store, so this adds no
# dependency, only a direct edge to it.
dbus = "0.9"

[target."cfg(target_os = \"macos\")".dependencies]
# SMAppService (macOS 13+), the login-item API behind the autostart setting.
Expand All @@ -162,6 +166,39 @@ smappservice-rs = "=0.1.3"
# (ADR 0008).
apple-native-keyring-store = { version = "=1.0.2", features = ["keychain"] }

# The known-network check (R8.8): the SSID through CaptiveNetwork, the Wi-Fi
# interface through CoreWLAN, the Location grant that gates the SSID, and a
# System Configuration store for change events. The objc2 family is already in
# the tree through the login-item wrapper; every crate here is a safe binding,
# so the backend's unsafe blocks are only the calls themselves.
dispatch2 = "0.3"
# `getifaddrs`, to tell "Wi-Fi off with Ethernet" from "no link at all".
libc = "0.2"
objc2 = "0.6"
objc2-core-foundation = "0.3"
objc2-core-location = "0.3"
objc2-core-wlan = "0.3"
objc2-foundation = "0.3"
objc2-system-configuration = "0.3"

[target."cfg(windows)".dependencies]
windows-native-keyring-store = "=1.1.0"
windows-registry = "0.6"
# The known-network check (R8.8): the Network List Manager for the connected
# profile's name and its change events, the COM plumbing for both, and the IP
# helper's adapter list for the media type. The module paths changed in 0.62
# from `NetworkManagement::NetworkConnector` to
# `Networking::NetworkListManager`.
# `#[implement]` expands to `windows_core::` paths, so the crate that defines
# the COM sink must name it directly, not only through `windows::core`.
windows-core = "0.62"
windows = { version = "0.62", features = [
"Win32_Foundation",
"Win32_NetworkManagement_IpHelper",
"Win32_NetworkManagement_Ndis",
"Win32_Networking_NetworkListManager",
"Win32_Networking_WinSock",
"Win32_System_Com",
"Win32_System_Ole",
"Win32_System_Variant",
] }
9 changes: 7 additions & 2 deletions crates/app/macos/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<!--
Merged over the Info.plist cargo-packager generates, with a last-writer-wins
insert per key, so it carries only the file-access usage strings macOS shows in
its privacy prompt. Every other key — CFBundleIdentifier, the version keys,
insert per key, so it carries only the usage strings macOS shows in its privacy
prompts: the file-access ones, and the Location grant the Wi-Fi network name
needs (SPEC R8.8). Every other key — CFBundleIdentifier, the version keys,
LSMinimumSystemVersion, NSHighResolutionCapable, LSUIElement — comes from
crates/app/Cargo.toml and the packager's own generator. Repeating one here
would silently override the manifest instead of being ignored.
Expand All @@ -16,6 +17,10 @@
<string>Immich Sync uploads photos and videos from the source folders you add, including folders in your Documents folder.</string>
<key>NSDownloadsFolderUsageDescription</key>
<string>Immich Sync uploads photos and videos from the source folders you add, including folders in your Downloads folder.</string>
<key>NSLocationUsageDescription</key>
<string>Immich Sync reads the current Wi-Fi network name, so it can sync only on the networks you list.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Immich Sync reads the current Wi-Fi network name, so it can sync only on the networks you list.</string>
<key>NSNetworkVolumesUsageDescription</key>
<string>Immich Sync uploads photos and videos from the source folders you add on mounted network volumes.</string>
<key>NSRemovableVolumesUsageDescription</key>
Expand Down
2 changes: 2 additions & 0 deletions crates/app/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ impl DiagnosticsModel {
server: ServerInfo::default(),
status: Status {
activity: sync_core::status::Activity::Idle,
pause: None,
network: sync_core::network::NetworkStatus::default(),
folders: Vec::new(),
counts: sync_core::store::FolderCounts::default(),
last_error: None,
Expand Down
Loading
Loading