feat: relay reliability + UI consistency pass (1.6.0) - #38
Conversation
APNs silent pushes expire after ~1h and the change-driven trigger path
dedups by marker, so a phone that was offline for the only change of the
day stayed stale until the next change or a manual app open.
The sidecar now sweeps /rest/db/completion on a slow cadence (default
6h, STALE_RETRIGGER_SECONDS, 0 disables) and re-sends a wake-up while
any unpaused peer still reports outstanding need{Items,Bytes,Deletes}.
Sweeps skip when a change-driven trigger is already debouncing, and a
fully-synced fleet never causes a push.
Adds the third standard background primitive next to BGAppRefreshTask and BGContinuedProcessingTask: a BGProcessingTask scheduled on every backgrounding (charging + network required) that runs the shared background sync cycle with a 180s budget instead of ~25s. Large catch-ups that time out in the refresh/silent-push window now complete overnight on the charger. The 'processing' UIBackgroundModes entry was already declared; only the task identifier, registration, scheduling, and handler were missing.
…ow Power Mode Silent pushes are the part of Cloud Relay iOS is allowed to drop: a force-quit blocks them entirely until the next manual launch, and Low Power Mode defers them. Users had no way to see how much was actually arriving, and no hint why it stopped. - RelayTriggerStore now keeps a rolling local history of wake-up arrivals (capped at 200, never leaves the device); Relay Diagnostics shows a 7-day received counter. - Live Low Power Mode warning in the Trigger Delivery section and as a troubleshooting hint. - Force-quit education: permanent footnote, plus a targeted 'wake-ups went quiet' hint when delivery was confirmed before but nothing arrived within the freshness window. - Localized in en/de/es/zh-Hans following existing terminology (Weck-Signal / señal de activación / 唤醒信号).
The vaultsync://sync deep link has existed for a while and the widgets are already tap-to-sync, but nothing told users they can automate the upload direction: a one-time Shortcuts personal automation (Obsidian 'Is Closed' -> Open URL vaultsync://sync) syncs every editing session the moment they leave Obsidian. New docs/instant-upload.md with setup steps and honest caveats, linked from the README doc table, the architecture asymmetry table, and the 'Background Sync Not Working' troubleshooting section.
…PNs constraint The spec promised a free self-hosted relay tier as roadmap. It cannot be built: APNs only accepts pushes for eu.vaultsync.app signed with VaultSync's own p8 key, which can never be distributed (security + Apple ToS). Advertising it anchored a free-tier expectation against the product's only revenue stream while being technically unfulfillable. Replaced the section with the real constraint, what remains open (RELAY_URL for development, full-stack self-build from source under MPL-2.0), dropped the pricing-table row and the answered open questions.
Changelog for the relay-reliability wave: sidecar stale-peer sweep, overnight BGProcessingTask catch-up, Cloud Relay delivery visibility (7-day counter, Low Power / force-quit education), the instant-upload Shortcuts guide, and the honest self-hosted relay documentation.
Cross-language:
- Unify relay diagnostics on wake-up terminology (was: trigger vs wake-up
on the same screen); renames keys 'Trigger Delivery' -> 'Wake-up Delivery'
and 'Last Trigger Received' -> 'Last Wake-up Received'
- Replace ASCII '->' with '→' and '...' with '…' in user-facing strings
- Drop literal backticks and stray 'homeserver container' wording from the
no-wake-up hint; align it with the helper naming used everywhere else
- Tighten the longest walls of text (relay setup steps 1+2, 'not active
yet' explainer, contribution blurb)
German:
- Standardize on 'Sync-…' compounds (was a mix of Hintergrundsynchronisation/
Hintergrundsync/Hintergrund-Sync, Sync-Problem vs Synchronisationsprobleme,
Synchronisations-Engine vs Sync-Engine, Synchronisierung vs Synchronisation)
- Use iOS-standard 'Mitteilungen' instead of 'Benachrichtigungen'
- Fix 'QR-Code scannen', restore full meaning of 'Add or Reconnect Device',
fix word order in the server-helper hint, passive 'Verbindet neu…'
- Unify dashes to '–' and quotes to „…“ in values; widget: 'Wartet',
'Jetzt synchronisieren'
- Disambiguate 'Learn how to fix' ('Lösung anzeigen') from 'How to fix: %@'
Spanish:
- 'compartición' instead of stray 'recurso compartido'; 'Volver a conectar'
instead of mixed 'Reconectar'; 'Tiempo agotado' for Timed out; 'Preparado'
for Ready (was colliding with Done = 'Listo'); '«…»' quotes; 'La mejor
opción' for Best value
Chinese (Simplified):
- Translate the remaining 'homeserver' mention; restore 'First' in accept-
first-share; '在 %@ 中' for folder context (was duration-like '在 %@ 内');
natural per-period price format ('每个月 ¥…'); celebratory '太好了' for Great
All four files keep full key parity and format-specifier parity; xcodebuild
build + test pass.
…component - Theme: alpha-capable vaultColor so muted fills (vaultAccentFill/Subtle, vaultSlateFill/Subtle) retire the per-view 'colorScheme == .dark ? a : b' opacity math; vaultHairline for card strokes; VaultSpacing.xxs (2pt); Font.vaultMono() replacing scattered .system(design: .monospaced) literals. - DesignSystem: StatusTag capsule badge unifying the three hand-rolled badge looks (conflict counts, 'Save N%', Ready/Needs Attention); filled variant uses systemBackground text so it stays readable on lifted dark tints. - StatusRow: optional glyphTint for the rare non-SyncStatus glyph color.
…checklist; dismissable relay upsell ContentView: - The four hand-built relay rows become one relayNavRow (StatusRow-based); the active state reuses the same StatusBadge as the Relay tab. - Sync and folder errors render as ActionCard instead of red caption stacks; the Obsidian connect block becomes an ActionCard with a real primary button. - Vault rows get the StatusRow treatment (full-size glyph, headline title, StatusTag conflict count) instead of plain text with a caption2 icon. - Empty states (vaults, devices) use ContentUnavailableView; Add Device moves from the section header to the Devices tab toolbar. - The relay upsell no longer hijacks tab selection at the aha moment: it is a dismissable dashboard card (View Cloud Relay / Not now). Setup checklist: - Items with an in-app entry point (connect Obsidian, add device, open Relay tab) carry a ChecklistAction; the view renders a real button below the remediation prose, routed through Settings back to ContentView, which presents the right sheet/tab after the dismissal transition. l10n: five new keys in en/de/es/zh-Hans (wake-up terminology per glossary).
… a11y polish - Onboarding: the seven hand-rolled dark-mode opacity sites move to the new muted fill / hairline tokens; paddings and the benefit-chip radius join the VaultSpacing/VaultRadius scale. - SyncIssuesView: .small action buttons become .regular (44pt minimum); a button that could do nothing is hidden instead of disabled unexplained. - PendingShares/SubscribePlanPicker/SetupChecklist: hand-rolled capsule badges replaced with StatusTag; spacing literals onto the scale. - DeviceDetailView: renaming now shows a transient saved checkmark (mirrors MonoField copy feedback) instead of committing invisibly. - Conflict/LineDiff/IgnorePatterns/RelayServerSetup: Font.vaultMono and VaultRadius.control replace ad-hoc monospace fonts and radius 8. - Widget: the medium widget's bare clock time gets a 'Last sync' accessibility label; QR scanner and relay views join the spacing scale.
|
Warning Review limit reached
More reviews will be available in 2 minutes and 16 seconds. Learn how PR review limits work. To continue reviewing without waiting, purchase usage credits in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThis 1.6.0 release adds backend and iOS features to improve push reliability and background sync, modernizes the iOS app UI with design tokens, introduces setup checklist action shortcuts, and applies comprehensive multilingual localization updates across four languages. ChangesBackend: Stale-Peer Sweep & Wake-Up Tracking
iOS App: Background Sync, UI Modernization, Localization
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
ios/VaultSync/Views/ContentView.swift (1)
233-245: ⚡ Quick winAvoid fixed-delay sheet routing for checklist actions.
Using
Task.sleep(.milliseconds(450))as a dismissal synchronization gate is timing-sensitive and can still race on slower/interactive transitions; route the deferred action from a dismissal completion signal (e.g., queued pending action processed aftershowSettingsbecomesfalse) instead of a hardcoded delay.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@ios/VaultSync/Views/ContentView.swift` around lines 233 - 245, The current handleChecklistAction uses a fixed Task.sleep delay which can race; remove the sleep and instead enqueue the action into a pendingChecklistAction property (e.g., Optional<SetupChecklistViewModel.ChecklistAction>) inside handleChecklistAction, then trigger dismissal (toggle showSettings as before) and perform the actual routing when the sheet has fully dismissed by observing the dismissal state (e.g., an onChange of showSettings or a didSet on showSettings) — when showSettings becomes false, read and clear pendingChecklistAction and perform the routing logic (set showObsidianPicker, showAddDevice, or selectedTab = .relay). Ensure all UI updates happen on the main actor.notify/main_test.go (1)
1049-1055: ⚡ Quick winTighten stale-sweep regression guard for local-device queries
The stub fails on paused-peer completion requests, but it still allows completion lookups against the local device ID. That can let a self-device filtering regression slip through while tests stay green.
Suggested test-hardening patch
case "/rest/db/completion": - if r.URL.Query().Get("device") == "PAUSED-PEER" { - t.Error("stale sweep queried completion for a paused device") - } + switch r.URL.Query().Get("device") { + case "PAUSED-PEER": + t.Error("stale sweep queried completion for a paused device") + case "DEVICE-STALE": + t.Error("stale sweep queried completion for the local device") + } w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{"completion":99,"needBytes":` + strconv.FormatInt(needBytes.Load(), 10) + `,"needItems":0,"needDeletes":0}`))🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@notify/main_test.go` around lines 1049 - 1055, The stub handling the "/rest/db/completion" path only rejects requests when r.URL.Query().Get("device") == "PAUSED-PEER" but still allows queries for the local device, weakening the stale-sweep guard; change the conditional in that handler to also reject when the "device" query equals the test's local device identifier (the same value the test uses for the current/self device) so both "PAUSED-PEER" and the local device ID are treated as invalid in the completion stub; update the check around r.URL.Query().Get("device") and keep the same response behavior otherwise (leave the Content-Type header and the JSON body using needBytes.Load()).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@ios/VaultSync/Views/ContentView.swift`:
- Around line 599-605: The Text and DisclosureGroup instances in ContentView are
using hardcoded English strings (e.g., the Text("In the picker, choose \"On My
iPhone\" → \"Obsidian\", then tap Open."), DisclosureGroup("Can't find the
Obsidian folder?") and its child Text) so localizations are missing; replace
these literal strings with localized keys (either use
LocalizedStringKey("your.key.here") or NSLocalizedString("your.key.here",
comment: "…") / Text(LocalizedStringKey("your.key.here"))), update the
DisclosureGroup and inner Text to use those keys (refer to the ContentView type
and the specific Text/DisclosureGroup declarations to locate them), and add
corresponding entries for each key into Localizable.strings for en, de, es, and
zh-Hans with appropriate translator comments.
In `@ios/VaultSync/Views/DesignSystem.swift`:
- Around line 59-63: The filled StatusTag currently uses
Color(.systemBackground) for its foreground which can produce low-contrast text
on bright tint colors; update the StatusTag rendering logic (the view that uses
the filled Bool and the modifiers .foregroundStyle and .background in
DesignSystem.swift) to choose a contrast-safe foreground dynamically—either use
the dynamic label color (Color(uiColor: .label) / Color.primary) for filled tags
or implement a luminance check on the tint color and pick a light or dark
foreground (e.g., .white or .black / .label) based on that result—so that filled
tags always maintain accessible contrast in both light and dark modes and with
VoiceOver/Dynamic Type.
---
Nitpick comments:
In `@ios/VaultSync/Views/ContentView.swift`:
- Around line 233-245: The current handleChecklistAction uses a fixed Task.sleep
delay which can race; remove the sleep and instead enqueue the action into a
pendingChecklistAction property (e.g.,
Optional<SetupChecklistViewModel.ChecklistAction>) inside handleChecklistAction,
then trigger dismissal (toggle showSettings as before) and perform the actual
routing when the sheet has fully dismissed by observing the dismissal state
(e.g., an onChange of showSettings or a didSet on showSettings) — when
showSettings becomes false, read and clear pendingChecklistAction and perform
the routing logic (set showObsidianPicker, showAddDevice, or selectedTab =
.relay). Ensure all UI updates happen on the main actor.
In `@notify/main_test.go`:
- Around line 1049-1055: The stub handling the "/rest/db/completion" path only
rejects requests when r.URL.Query().Get("device") == "PAUSED-PEER" but still
allows queries for the local device, weakening the stale-sweep guard; change the
conditional in that handler to also reject when the "device" query equals the
test's local device identifier (the same value the test uses for the
current/self device) so both "PAUSED-PEER" and the local device ID are treated
as invalid in the completion stub; update the check around
r.URL.Query().Get("device") and keep the same response behavior otherwise (leave
the Content-Type header and the JSON body using needBytes.Load()).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 00c1d8d7-07a2-45ef-a7bb-6c0285f401e2
📒 Files selected for processing (45)
CHANGELOG.mdREADME.mddocs/architecture.mddocs/instant-upload.mddocs/relay-spec.mddocs/troubleshooting.mdios/VaultSync/App/VaultSyncApp.swiftios/VaultSync/Models/RelayProvisionStatus.swiftios/VaultSync/Models/SyncUserError.swiftios/VaultSync/Resources/Theme.swiftios/VaultSync/Services/BackgroundSyncService.swiftios/VaultSync/Services/SubscriptionManager.swiftios/VaultSync/Services/VaultManager.swiftios/VaultSync/ViewModels/SetupChecklistViewModel.swiftios/VaultSync/Views/ConflictDiffView.swiftios/VaultSync/Views/ConflictListView.swiftios/VaultSync/Views/ContentView.swiftios/VaultSync/Views/DesignSystem.swiftios/VaultSync/Views/DeviceDetailView.swiftios/VaultSync/Views/IgnorePatternsView.swiftios/VaultSync/Views/LineDiffView.swiftios/VaultSync/Views/OnboardingView.swiftios/VaultSync/Views/PendingSharesView.swiftios/VaultSync/Views/QRScannerView.swiftios/VaultSync/Views/RelayDiagnosticsView.swiftios/VaultSync/Views/RelayHomeView.swiftios/VaultSync/Views/RelayServerSetupView.swiftios/VaultSync/Views/SettingsView.swiftios/VaultSync/Views/SetupChecklistView.swiftios/VaultSync/Views/SubscribePlanPicker.swiftios/VaultSync/Views/SyncActivityView.swiftios/VaultSync/Views/SyncIssuesView.swiftios/VaultSync/de.lproj/Localizable.stringsios/VaultSync/en.lproj/Localizable.stringsios/VaultSync/es.lproj/Localizable.stringsios/VaultSync/zh-Hans.lproj/Localizable.stringsios/VaultSyncTests/RelayTriggerStoreTests.swiftios/VaultSyncTests/TestSupport.swiftios/VaultSyncWidget/VaultSyncWidget.swiftios/VaultSyncWidget/de.lproj/Localizable.stringsios/project.ymlnotify/README.mdnotify/main.gonotify/main_test.gonotify/syncthing.go
📜 Review details
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Build & Test
🧰 Additional context used
📓 Path-based instructions (12)
ios/**/*.lproj/Localizable.strings
📄 CodeRabbit inference engine (README.md)
App must be localized in English, German, Spanish, and Simplified Chinese
Files:
ios/VaultSyncWidget/de.lproj/Localizable.stringsios/VaultSync/es.lproj/Localizable.stringsios/VaultSync/de.lproj/Localizable.stringsios/VaultSync/en.lproj/Localizable.stringsios/VaultSync/zh-Hans.lproj/Localizable.strings
**/*
⚙️ CodeRabbit configuration file
**/*: VaultSync syncs private Obsidian notes through Syncthing. Treat data loss,
privacy leaks, security regressions, and broken sync behavior as high priority.
Do not nitpick formatting unless it affects maintainability, correctness, or public API clarity.
Flag any accidental logging, telemetry, crash reporting, or network transfer of note contents,
vault paths, filenames with private context, API keys, APNs tokens, relay keys, or security-scoped bookmark data.
Files:
ios/VaultSyncWidget/de.lproj/Localizable.stringsios/VaultSync/Models/SyncUserError.swiftios/VaultSync/Views/ConflictListView.swiftios/VaultSync/Views/RelayServerSetupView.swiftios/VaultSync/App/VaultSyncApp.swiftios/VaultSync/Views/SyncActivityView.swiftios/VaultSyncWidget/VaultSyncWidget.swiftdocs/troubleshooting.mdios/project.ymlREADME.mddocs/instant-upload.mdios/VaultSync/Models/RelayProvisionStatus.swiftnotify/README.mdios/VaultSync/Views/QRScannerView.swiftCHANGELOG.mdios/VaultSync/Views/RelayHomeView.swiftdocs/architecture.mdios/VaultSyncTests/TestSupport.swiftnotify/syncthing.goios/VaultSync/Views/ConflictDiffView.swiftios/VaultSync/Views/LineDiffView.swiftios/VaultSync/Services/SubscriptionManager.swiftios/VaultSync/Views/SyncIssuesView.swiftios/VaultSync/Views/SubscribePlanPicker.swiftios/VaultSync/Services/VaultManager.swiftios/VaultSync/Views/IgnorePatternsView.swiftios/VaultSync/Views/SettingsView.swiftdocs/relay-spec.mdios/VaultSync/Views/SetupChecklistView.swiftios/VaultSync/Views/DeviceDetailView.swiftnotify/main.goios/VaultSyncTests/RelayTriggerStoreTests.swiftios/VaultSync/Views/DesignSystem.swiftios/VaultSync/Resources/Theme.swiftnotify/main_test.goios/VaultSync/ViewModels/SetupChecklistViewModel.swiftios/VaultSync/es.lproj/Localizable.stringsios/VaultSync/Services/BackgroundSyncService.swiftios/VaultSync/de.lproj/Localizable.stringsios/VaultSync/Views/RelayDiagnosticsView.swiftios/VaultSync/Views/ContentView.swiftios/VaultSync/en.lproj/Localizable.stringsios/VaultSync/zh-Hans.lproj/Localizable.stringsios/VaultSync/Views/PendingSharesView.swiftios/VaultSync/Views/OnboardingView.swift
**
⚙️ CodeRabbit configuration file
**:![]()
VaultSync
Self-hosted Obsidian vault sync for iPhone and iPad.
Your notes sync peer-to-peer over Syncthing, straight into Obsidian's iOS sandbox — no note cloud, no account, no tracking.![]()
![]()
![]()
🔭 Why VaultSync
- Peer-to-peer & private — syncs directly between your own devices over Syncthing. No note cloud, no account, no tracking.
- Lands in Obsidian — files sync into Obsidian's iOS sandbox, where the app already looks for them.
- Pair by QR, resolve conflicts — connect your server in seconds; settle Markdown conflicts with side-by-side diffs.
- Server changes wake your iPhone — optional Cloud Relay nudges the app the moment your server updates, so incoming notes land eve...
Files:
ios/VaultSyncWidget/de.lproj/Localizable.stringsios/VaultSync/Models/SyncUserError.swiftios/VaultSync/Views/ConflictListView.swiftios/VaultSync/Views/RelayServerSetupView.swiftios/VaultSync/App/VaultSyncApp.swiftios/VaultSync/Views/SyncActivityView.swiftios/VaultSyncWidget/VaultSyncWidget.swiftdocs/troubleshooting.mdios/project.ymlREADME.mddocs/instant-upload.mdios/VaultSync/Models/RelayProvisionStatus.swiftnotify/README.mdios/VaultSync/Views/QRScannerView.swiftCHANGELOG.mdios/VaultSync/Views/RelayHomeView.swiftdocs/architecture.mdios/VaultSyncTests/TestSupport.swiftnotify/syncthing.goios/VaultSync/Views/ConflictDiffView.swiftios/VaultSync/Views/LineDiffView.swiftios/VaultSync/Services/SubscriptionManager.swiftios/VaultSync/Views/SyncIssuesView.swiftios/VaultSync/Views/SubscribePlanPicker.swiftios/VaultSync/Services/VaultManager.swiftios/VaultSync/Views/IgnorePatternsView.swiftios/VaultSync/Views/SettingsView.swiftdocs/relay-spec.mdios/VaultSync/Views/SetupChecklistView.swiftios/VaultSync/Views/DeviceDetailView.swiftnotify/main.goios/VaultSyncTests/RelayTriggerStoreTests.swiftios/VaultSync/Views/DesignSystem.swiftios/VaultSync/Resources/Theme.swiftnotify/main_test.goios/VaultSync/ViewModels/SetupChecklistViewModel.swiftios/VaultSync/es.lproj/Localizable.stringsios/VaultSync/Services/BackgroundSyncService.swiftios/VaultSync/de.lproj/Localizable.stringsios/VaultSync/Views/RelayDiagnosticsView.swiftios/VaultSync/Views/ContentView.swiftios/VaultSync/en.lproj/Localizable.stringsios/VaultSync/zh-Hans.lproj/Localizable.stringsios/VaultSync/Views/PendingSharesView.swiftios/VaultSync/Views/OnboardingView.swift
**/*.swift
📄 CodeRabbit inference engine (Custom checks)
For Swift background execution changes, pass if work is bounded, cancellation-aware, handles expiration callbacks, and records errors without leaking private vault data. Fail only when background work can continue unbounded, miss cleanup, or violate iOS background execution constraints.
Files:
ios/VaultSync/Models/SyncUserError.swiftios/VaultSync/Views/ConflictListView.swiftios/VaultSync/Views/RelayServerSetupView.swiftios/VaultSync/App/VaultSyncApp.swiftios/VaultSync/Views/SyncActivityView.swiftios/VaultSyncWidget/VaultSyncWidget.swiftios/VaultSync/Models/RelayProvisionStatus.swiftios/VaultSync/Views/QRScannerView.swiftios/VaultSync/Views/RelayHomeView.swiftios/VaultSyncTests/TestSupport.swiftios/VaultSync/Views/ConflictDiffView.swiftios/VaultSync/Views/LineDiffView.swiftios/VaultSync/Services/SubscriptionManager.swiftios/VaultSync/Views/SyncIssuesView.swiftios/VaultSync/Views/SubscribePlanPicker.swiftios/VaultSync/Services/VaultManager.swiftios/VaultSync/Views/IgnorePatternsView.swiftios/VaultSync/Views/SettingsView.swiftios/VaultSync/Views/SetupChecklistView.swiftios/VaultSync/Views/DeviceDetailView.swiftios/VaultSyncTests/RelayTriggerStoreTests.swiftios/VaultSync/Views/DesignSystem.swiftios/VaultSync/Resources/Theme.swiftios/VaultSync/ViewModels/SetupChecklistViewModel.swiftios/VaultSync/Services/BackgroundSyncService.swiftios/VaultSync/Views/RelayDiagnosticsView.swiftios/VaultSync/Views/ContentView.swiftios/VaultSync/Views/PendingSharesView.swiftios/VaultSync/Views/OnboardingView.swift
**/*.{swift,go,sh,kt,java,js,ts,tsx,jsx,py,c,h,cpp,cc}
📄 CodeRabbit inference engine (README.md)
All code must be licensed under MPL-2.0 (Mozilla Public License 2.0)
Files:
ios/VaultSync/Models/SyncUserError.swiftios/VaultSync/Views/ConflictListView.swiftios/VaultSync/Views/RelayServerSetupView.swiftios/VaultSync/App/VaultSyncApp.swiftios/VaultSync/Views/SyncActivityView.swiftios/VaultSyncWidget/VaultSyncWidget.swiftios/VaultSync/Models/RelayProvisionStatus.swiftios/VaultSync/Views/QRScannerView.swiftios/VaultSync/Views/RelayHomeView.swiftios/VaultSyncTests/TestSupport.swiftnotify/syncthing.goios/VaultSync/Views/ConflictDiffView.swiftios/VaultSync/Views/LineDiffView.swiftios/VaultSync/Services/SubscriptionManager.swiftios/VaultSync/Views/SyncIssuesView.swiftios/VaultSync/Views/SubscribePlanPicker.swiftios/VaultSync/Services/VaultManager.swiftios/VaultSync/Views/IgnorePatternsView.swiftios/VaultSync/Views/SettingsView.swiftios/VaultSync/Views/SetupChecklistView.swiftios/VaultSync/Views/DeviceDetailView.swiftnotify/main.goios/VaultSyncTests/RelayTriggerStoreTests.swiftios/VaultSync/Views/DesignSystem.swiftios/VaultSync/Resources/Theme.swiftnotify/main_test.goios/VaultSync/ViewModels/SetupChecklistViewModel.swiftios/VaultSync/Services/BackgroundSyncService.swiftios/VaultSync/Views/RelayDiagnosticsView.swiftios/VaultSync/Views/ContentView.swiftios/VaultSync/Views/PendingSharesView.swiftios/VaultSync/Views/OnboardingView.swift
ios/**/*.{swift,xcconfig,pbxproj}
📄 CodeRabbit inference engine (README.md)
iOS app must target iOS 18 or later
Files:
ios/VaultSync/Models/SyncUserError.swiftios/VaultSync/Views/ConflictListView.swiftios/VaultSync/Views/RelayServerSetupView.swiftios/VaultSync/App/VaultSyncApp.swiftios/VaultSync/Views/SyncActivityView.swiftios/VaultSyncWidget/VaultSyncWidget.swiftios/VaultSync/Models/RelayProvisionStatus.swiftios/VaultSync/Views/QRScannerView.swiftios/VaultSync/Views/RelayHomeView.swiftios/VaultSyncTests/TestSupport.swiftios/VaultSync/Views/ConflictDiffView.swiftios/VaultSync/Views/LineDiffView.swiftios/VaultSync/Services/SubscriptionManager.swiftios/VaultSync/Views/SyncIssuesView.swiftios/VaultSync/Views/SubscribePlanPicker.swiftios/VaultSync/Services/VaultManager.swiftios/VaultSync/Views/IgnorePatternsView.swiftios/VaultSync/Views/SettingsView.swiftios/VaultSync/Views/SetupChecklistView.swiftios/VaultSync/Views/DeviceDetailView.swiftios/VaultSyncTests/RelayTriggerStoreTests.swiftios/VaultSync/Views/DesignSystem.swiftios/VaultSync/Resources/Theme.swiftios/VaultSync/ViewModels/SetupChecklistViewModel.swiftios/VaultSync/Services/BackgroundSyncService.swiftios/VaultSync/Views/RelayDiagnosticsView.swiftios/VaultSync/Views/ContentView.swiftios/VaultSync/Views/PendingSharesView.swiftios/VaultSync/Views/OnboardingView.swift
ios/**/*.swift
📄 CodeRabbit inference engine (README.md)
ios/**/*.swift: Swift 6 with SwiftUI must be used for iOS development
Background sync must use BGAppRefreshTask and BGContinuedProcessingTask (iOS 26+ when available)
Push notifications must use APNs silent push via Cloud Relay
Pairing must support both QR code scanning and manual Device ID paste input for Syncthing connection
App must provide an activity timeline and diagnostics showing exactly what synced
VoiceOver accessibility must be supported throughout the entire iOS app
Dynamic Type text scaling must be supported throughout the entire iOS app
Files:
ios/VaultSync/Models/SyncUserError.swiftios/VaultSync/Views/ConflictListView.swiftios/VaultSync/Views/RelayServerSetupView.swiftios/VaultSync/App/VaultSyncApp.swiftios/VaultSync/Views/SyncActivityView.swiftios/VaultSyncWidget/VaultSyncWidget.swiftios/VaultSync/Models/RelayProvisionStatus.swiftios/VaultSync/Views/QRScannerView.swiftios/VaultSync/Views/RelayHomeView.swiftios/VaultSyncTests/TestSupport.swiftios/VaultSync/Views/ConflictDiffView.swiftios/VaultSync/Views/LineDiffView.swiftios/VaultSync/Services/SubscriptionManager.swiftios/VaultSync/Views/SyncIssuesView.swiftios/VaultSync/Views/SubscribePlanPicker.swiftios/VaultSync/Services/VaultManager.swiftios/VaultSync/Views/IgnorePatternsView.swiftios/VaultSync/Views/SettingsView.swiftios/VaultSync/Views/SetupChecklistView.swiftios/VaultSync/Views/DeviceDetailView.swiftios/VaultSyncTests/RelayTriggerStoreTests.swiftios/VaultSync/Views/DesignSystem.swiftios/VaultSync/Resources/Theme.swiftios/VaultSync/ViewModels/SetupChecklistViewModel.swiftios/VaultSync/Services/BackgroundSyncService.swiftios/VaultSync/Views/RelayDiagnosticsView.swiftios/VaultSync/Views/ContentView.swiftios/VaultSync/Views/PendingSharesView.swiftios/VaultSync/Views/OnboardingView.swift
⚙️ CodeRabbit configuration file
ios/**/*.swift: Focus on Swift 6 strict concurrency, Sendable/MainActor correctness, Task cancellation,
retain cycles, memory pressure, SwiftUI observation state, StoreKit/APNs flows, and iOS background execution limits.
Pay special attention to BGAppRefreshTask and BGContinuedProcessingTask behavior, expiration handling,
bounded work, and cleanup when the app is suspended or terminated.
Files:
ios/VaultSync/Models/SyncUserError.swiftios/VaultSync/Views/ConflictListView.swiftios/VaultSync/Views/RelayServerSetupView.swiftios/VaultSync/App/VaultSyncApp.swiftios/VaultSync/Views/SyncActivityView.swiftios/VaultSyncWidget/VaultSyncWidget.swiftios/VaultSync/Models/RelayProvisionStatus.swiftios/VaultSync/Views/QRScannerView.swiftios/VaultSync/Views/RelayHomeView.swiftios/VaultSyncTests/TestSupport.swiftios/VaultSync/Views/ConflictDiffView.swiftios/VaultSync/Views/LineDiffView.swiftios/VaultSync/Services/SubscriptionManager.swiftios/VaultSync/Views/SyncIssuesView.swiftios/VaultSync/Views/SubscribePlanPicker.swiftios/VaultSync/Services/VaultManager.swiftios/VaultSync/Views/IgnorePatternsView.swiftios/VaultSync/Views/SettingsView.swiftios/VaultSync/Views/SetupChecklistView.swiftios/VaultSync/Views/DeviceDetailView.swiftios/VaultSyncTests/RelayTriggerStoreTests.swiftios/VaultSync/Views/DesignSystem.swiftios/VaultSync/Resources/Theme.swiftios/VaultSync/ViewModels/SetupChecklistViewModel.swiftios/VaultSync/Services/BackgroundSyncService.swiftios/VaultSync/Views/RelayDiagnosticsView.swiftios/VaultSync/Views/ContentView.swiftios/VaultSync/Views/PendingSharesView.swiftios/VaultSync/Views/OnboardingView.swift
**/*.md
⚙️ CodeRabbit configuration file
**/*.md: Review public documentation for technical accuracy, privacy/security claims, App Store-facing wording,
setup correctness, and consistency with the free app plus optional Cloud Relay subscription model.
Files:
docs/troubleshooting.mdREADME.mddocs/instant-upload.mdnotify/README.mdCHANGELOG.mddocs/architecture.mddocs/relay-spec.md
docs/**
⚙️ CodeRabbit configuration file
docs/**: # ArchitectureVaultSync embeds Syncthing's Go reference implementation as an iOS library via gomobile — no reimplementation of the protocol in Swift, and guaranteed wire compatibility.
┌─────────────────────────────────┐ │ SwiftUI Frontend │ iOS-native UI, Swift 6 ├─────────────────────────────────┤ │ Swift ↔ Go Bridge │ thin API via gomobile │ │ → exported as .xcframework ├─────────────────────────────────┤ │ syncthing/lib (Go) │ protocol, discovery, sync └─────────────────────────────────┘ ↕ filesystem ┌─────────────────────────────────┐ │ Obsidian Vault (direct) │ Obsidian's iOS sandbox └─────────────────────────────────┘🔄 Sync strategy
- Foreground — Syncthing runs unrestricted: immediate, continuous sync.
- Background —
BGAppRefreshTask(requested ~15 min out; iOS decides the actual timing) +BGContinuedProcessingTask(iOS 26+, longer runtime for user-initiated tasks). A ~30s grace window after backgrounding lets in-flight work finish.- Push (Cloud Relay) — optional. Near-realtime
server → iPhonewake-ups via APNs silent push. See relay-spec.md.VaultSync is intentionally asymmetric:
Direction Path Server → iPhone vaultsync-notifyspots outgoing changes → Cloud Relay silent push → VaultSync wakes and pulls.iPhone → Server iOS doesn't guarantee timely background execution for local edits. The reliable path is to open VaultSync and let embedded Syncthing run in the foreground. Cloud Relay is a
server → iPhoneacceleration path, not a guarantee of symmetric real-time background sync.🌉 Go bridge (
go/bridge/)Minimal API exported via gomobile. Only primitives +
string+[]bytecross the bridge; complex data is JSON-serialized (read accessors are namedGet…JSON). QR scanning and conflict diffs are produced on the iOS side, not via the ...
Files:
docs/troubleshooting.mddocs/instant-upload.mddocs/architecture.mddocs/relay-spec.md
ios/project.yml
📄 CodeRabbit inference engine (README.md)
Use XcodeGen for iOS project generation instead of manual Xcode project configuration
Files:
ios/project.yml
⚙️ CodeRabbit configuration file
ios/project.yml: This generates the Xcode project and Info.plist. Review changes for bundle ID,
entitlements, background modes, URL schemes, signing settings, and accidental secret exposure.
Files:
ios/project.yml
notify/**/*.{go,dockerfile,sh}
📄 CodeRabbit inference engine (README.md)
Docker sidecar (vaultsync-notify) must never expose notes, file or folder names, or vault structure to the relay
Files:
notify/syncthing.gonotify/main.gonotify/main_test.go
notify/**/*.go
⚙️ CodeRabbit configuration file
notify/**/*.go: Review goroutine lifecycle, context cancellation, HTTP timeouts, signal handling, debounce behavior,
Syncthing REST API polling, relay API calls, error classification, and API-key handling.
Flag leaked request bodies, note metadata, Syncthing API keys, relay keys, or APNs-related secrets.
Files:
notify/syncthing.gonotify/main.gonotify/main_test.go
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:44:40.163Z
Learning: Sync engine must use Syncthing 2.x via Go/gomobile xcframework
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:44:40.163Z
Learning: Cloud Relay must only ever access Device ID and push token needed to route wake-ups, never notes or vault structure
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:44:40.163Z
Learning: VaultSync must implement peer-to-peer syncing directly between iOS app and Syncthing server with no intermediary cloud storage
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:44:40.163Z
Learning: Files must sync into Obsidian's iOS sandbox without requiring external configuration or manual folder setup
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:44:40.163Z
Learning: Conflict resolution must support side-by-side diffs for Markdown files
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:44:50.824Z
Learning: Use gomobile to embed Syncthing's Go reference implementation as an iOS library with guaranteed wire compatibility; do not reimplement the Syncthing protocol in Swift
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:44:50.824Z
Learning: Organize iOS app code into the following groups: App/, Models/, ViewModels/, Views/, Services/, and Resources/
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:44:50.824Z
Learning: Implement foreground sync by running Syncthing unrestricted for immediate, continuous synchronization
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:44:50.824Z
Learning: Design sync as asymmetric: server-to-iPhone via Cloud Relay silent push acceleration, iPhone-to-server via foreground Syncthing with Shortcuts automation for on-demand uploads
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:44:58.284Z
Learning: Implement Shortcuts personal automation to trigger VaultSync uploads when Obsidian is closed, using the URL scheme `vaultsync://sync`
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:44:58.284Z
Learning: Support URL scheme `vaultsync://sync` to trigger immediate scan and sync operations
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:44:58.284Z
Learning: Support optional folder parameter in URL scheme: `vaultsync://sync?folder=<folder-id>` for vault-specific syncing
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:44:58.284Z
Learning: Ensure VaultSync can be opened and trigger sync operations within the iOS ~30 second grace window after app backgrounding
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:44:58.284Z
Learning: Implement home screen and lock screen widgets with tap-to-sync functionality that triggers the `vaultsync://sync` URL scheme action
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:44:58.284Z
Learning: Implement pull-to-refresh gesture inside VaultSync to allow on-demand rescan and sync operations
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:45:25.062Z
Learning: All endpoints must require TLS (HSTS, minimum TLS 1.2)
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:45:25.062Z
Learning: No file content, folder names, file names, file sizes, or metadata must ever be transmitted from the homeserver to the central relay or iOS app
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:45:25.062Z
Learning: APNs payload must be a silent push with no visible alert or body content
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:45:25.062Z
Learning: Identity is based on Syncthing Device ID; no user accounts or API keys required
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:45:25.062Z
Learning: The central relay is a minimal, horizontally-scalable infrastructure with stateless request handling and a shared database for device tokens
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:45:25.062Z
Learning: Device tokens are personal data; ensure GDPR data processing agreements and privacy policy disclosures are in place
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:45:40.282Z
Learning: When Syncthing is not running (Device ID empty, sync engine offline), force-close VaultSync and reopen it, keep it in foreground for 20-30 seconds, confirm Device ID appears, tap Rescan Vault, and reboot device if still failing
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:45:40.282Z
Learning: The vaultsync-notify service must run as the correct user (matching the uid/gid that owns config.xml) to read Syncthing API key from config.xml; set PUID/PGID in Docker Compose or use -u <uid>:<gid> with docker run
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:45:40.282Z
Learning: Set SYNCTHING_CONFIG environment variable to point to the actual config.xml path when it is not auto-detected (required for Synology/QNAP), and remove SYNCTHING_API_KEY override if set to allow fallback to config.xml auto-detection
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:45:40.282Z
Learning: The relay health check must be verified to be green (relay reachable) and Last Trigger Received timestamp must update to confirm wake-ups are actually delivered, not just that the relay is up
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:45:40.282Z
Learning: On desktop Syncthing, the iOS Device ID must be listed in the folder Sharing settings for pending shares to appear in VaultSync
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:45:40.282Z
Learning: Silent push wake-ups require a valid APNs token and provisioned device; use Retry APNs Registration and Retry Provisioning in Cloud Relay tab when wake-ups do not arrive despite being subscribed
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:45:40.282Z
Learning: The Obsidian folder picker must be directed to On My iPhone → Obsidian (or the vault root containing .obsidian directory) to properly connect the Obsidian folder in VaultSync
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:45:40.282Z
Learning: When vault access fails with bookmark expiration, reconnect to the Obsidian folder by re-selecting it in the Files picker to renew the security-scoped bookmark
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:45:40.282Z
Learning: For background sync issues, ensure VaultSync is opened in foreground to sync iPhone → server reliably; use Cloud Relay for near-realtime server → iPhone sync; consider Shortcuts automation for instant upload if needed
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:45:40.282Z
Learning: When a required device is disconnected, verify desktop Syncthing is running and online, both devices can reach each other, and the device still exists in VaultSync; remove and re-add the device if its ID changed
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T17:45:40.282Z
Learning: When filing issues, include VaultSync version, iOS version, screenshots of Sync Issues and Relay health & diagnostics, vaultsync-notify --doctor output, and relevant log lines
🪛 LanguageTool
docs/instant-upload.md
[style] ~24-~24: Consider an alternative for the overused word “exactly”.
Context: ...ot only when you force-quit it. That is exactly what you want: every editing session en...
(EXACTLY_PRECISELY)
CHANGELOG.md
[uncategorized] ~13-~13: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...ver reported anywhere), warns live when Low Power Mode is deferring silent pushes, and ex...
(EN_COMPOUND_ADJECTIVE_INTERNAL)
🪛 markdownlint-cli2 (0.22.1)
docs/instant-upload.md
[warning] 14-14: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🔇 Additional comments (51)
ios/VaultSync/Services/BackgroundSyncService.swift (1)
27-27: LGTM!Also applies to: 65-65, 111-125, 145-145, 229-255, 266-266, 793-818
ios/VaultSync/App/VaultSyncApp.swift (1)
108-108: LGTM!ios/project.yml (1)
62-63: LGTM!Also applies to: 75-75, 126-127
ios/VaultSync/Views/DeviceDetailView.swift (1)
8-8: LGTM!Also applies to: 33-40, 96-102
ios/VaultSync/Views/PendingSharesView.swift (1)
16-16: LGTM!Also applies to: 39-39, 42-42, 54-54, 58-58, 64-64, 71-73, 77-84, 106-106
ios/VaultSync/Views/RelayServerSetupView.swift (1)
69-69: LGTM!Also applies to: 73-73, 93-93, 95-95
ios/VaultSync/Views/SubscribePlanPicker.swift (1)
81-81: LGTM!Also applies to: 98-98, 103-103
ios/VaultSync/Views/SyncActivityView.swift (1)
16-16: LGTM!Also applies to: 37-37
ios/VaultSync/Views/RelayHomeView.swift (1)
51-51: LGTM!Also applies to: 149-149
ios/VaultSyncWidget/VaultSyncWidget.swift (1)
176-180: LGTM!notify/main.go (7)
34-42: LGTM!
143-151: LGTM!
220-228: LGTM!
411-421: LGTM!
513-543: LGTM!
563-567: LGTM!
569-621: LGTM!notify/syncthing.go (4)
178-191: LGTM!
193-200: LGTM!
202-215: LGTM!
217-244: LGTM!docs/relay-spec.md (4)
59-59: LGTM!
197-197: LGTM!
261-271: LGTM!
274-279: LGTM!docs/architecture.md (2)
23-23: LGTM!
31-31: LGTM!docs/instant-upload.md (3)
1-20: LGTM!
22-27: LGTM!
29-31: LGTM!notify/README.md (1)
64-64: LGTM!docs/troubleshooting.md (1)
142-142: LGTM!README.md (1)
131-131: LGTM!CHANGELOG.md (1)
7-24: LGTM!ios/VaultSync/Models/RelayProvisionStatus.swift (2)
136-176: LGTM!
33-35: LGTM!Also applies to: 590-590
ios/VaultSync/Views/RelayDiagnosticsView.swift (4)
10-10: LGTM!Also applies to: 27-29
270-290: LGTM!
396-411: LGTM!
99-99: LGTM!Also applies to: 111-111, 131-131, 191-191, 221-223, 249-249
ios/VaultSync/Views/SyncIssuesView.swift (1)
69-126: LGTM!ios/VaultSync/Models/SyncUserError.swift (1)
73-73: LGTM!Also applies to: 200-200
ios/VaultSync/Services/VaultManager.swift (1)
251-251: LGTM!Also applies to: 266-266, 279-279
ios/VaultSync/en.lproj/Localizable.strings (1)
70-70: LGTM!Also applies to: 110-110, 123-123, 135-135, 139-139, 151-151, 158-158, 170-170, 231-231, 276-276, 292-292, 299-299, 318-318, 356-356, 463-463, 598-612
ios/VaultSyncTests/TestSupport.swift (1)
22-39: LGTM!ios/VaultSyncTests/RelayTriggerStoreTests.swift (1)
1-42: LGTM!ios/VaultSync/de.lproj/Localizable.strings (1)
10-10: LGTM!Also applies to: 16-23, 54-54, 70-70, 81-81, 97-97, 110-110, 114-114, 116-116, 123-123, 135-135, 139-139, 151-151, 154-154, 158-158, 170-170, 177-177, 231-231, 241-241, 246-246, 260-260, 266-267, 268-268, 271-276, 280-280, 287-289, 293-293, 299-300, 314-315, 318-318, 339-339, 356-356, 393-393, 402-402, 411-413, 426-429, 435-435, 440-440, 463-463, 465-465, 484-484, 494-497, 499-499, 501-501, 518-522, 524-524, 526-527, 539-539, 583-585, 593-593, 598-600, 603-606, 609-612
ios/VaultSync/es.lproj/Localizable.strings (1)
70-70: LGTM!Also applies to: 97-97, 110-110, 123-123, 135-135, 139-139, 151-151, 158-158, 170-170, 176-176, 178-178, 231-231, 276-276, 288-289, 293-293, 297-297, 299-299, 318-318, 356-356, 386-386, 463-463, 484-484, 544-544, 588-588, 598-598, 604-604, 606-606, 609-612
ios/VaultSync/zh-Hans.lproj/Localizable.strings (1)
2-2: LGTM!Also applies to: 70-70, 110-110, 123-123, 135-135, 139-139, 151-151, 158-158, 170-170, 231-231, 276-276, 293-293, 299-299, 318-318, 356-356, 452-452, 463-463, 489-490, 595-595, 598-598, 604-604, 606-606, 609-612
ios/VaultSyncWidget/de.lproj/Localizable.strings (1)
15-15: LGTM!Also applies to: 21-21
ios/VaultSync/Services/SubscriptionManager.swift (1)
33-35: LGTM!Also applies to: 590-590
…outing Filled StatusTag picked its text color by flipping with the color scheme (systemBackground), which made light mode white-on-amber at ~2.5:1 — a WCAG failure — while dark mode's lifted tints were fine. The text now picks black or white by the resolved fill's relative luminance (break-even 0.179), so both modes get the higher-contrast choice for any tint. Checklist remediation routing replaces the 450ms sleep gate with the settings sheet's onDismiss callback: the pending action is stored on tap and runs only after the dismissal transition has actually completed, so it can no longer race slow or interactive transitions. Both from CodeRabbit review on #38 (the suggested Color.primary fix was not used — it would have inverted the problem into dark mode).
The completion stub only rejected paused-peer lookups; a regression in self-device filtering would have stayed green. It now also fails on queries for the local device ID.
|
@coderabbitai review |
✅ Action performedReview finished.
|


Relay reliability
UI consistency pass
Bumps version to 1.6.0 (build 28).
VaultSync 1.6.0: Relay reliability and UI consistency pass
User-visible sync improvements
Relay wake-up reliability:
STALE_RETRIGGER_SECONDS) for devices still reporting outstanding sync need, recovering missed or expired pushes without waiting for local changes.BGProcessingTask(≈180 second budget) that runs when the device is charging with network connectivity, providing a longer execution window alongside existing background primitives.Relay diagnostics visibility:
Workflow automation:
instant-upload.mddocumentation and in-app guidance for iOS Shortcuts personal automation to trigger VaultSync uploads when leaving Obsidian (usesvaultsync://syncURL scheme).UI consistency and design system
vaultAccentFill,vaultSlateFillvariants), hairline stroke color, monospaced font helper (vaultMono), and spacing constantxxs.StatusTagreusable component for state/count badges with consistent styling.StatusRow/ActionCardusage for vault and device listings, andContentUnavailableViewempty states..controlSize(.regular), icon/glyph tint control inStatusRow, widget accessibility labels, conditional button visibility over disabled states.Localization and documentation
→arrows, proper ellipsis).relay-spec.mdto document stale-peer sweep behavior andSTALE_RETRIGGER_SECONDSconfiguration.Background execution changes
BGProcessingTaskregistration requires network connectivity and external power, with ~1 hour earliest begin date; expiration handler only stops Syncthing if not in foreground.Test coverage
RelayTriggerStoreTestsvalidating 7-day window counting and history capping (200 entry limit).notify/main_test.gofor stale-retrigger defaults, parsing, and sweep triggering.Version and metadata
ios/project.ymlfor both app and widget targets.