feat: self-activating Cloud Relay + key-free server setup (1.5.1)#33
Conversation
…uto-detect
Promotes the verified relay-activation work from the lab onto a clean
feature branch off main (1.5.1 base). Two strands:
B-Weg (activation UX + proof):
- notify fires one "startup-announce" trigger after a successful startup
health check, so a freshly (re)started helper wakes the iPhone once on
its own — activation self-confirms with zero user action.
- iOS: reactivation card for paid-but-never-delivered subs, honest 3-state
(not-active / active / went-quiet), one-time "Connected" celebration,
cross-surface nil-vs-stale labels, cold-start persistence of provisioned
device IDs. DEBUG-only safety gate (RELAY_BASE_URL_OVERRIDE) keeps the
app pointed at production unless a DEBUG launch arg redirects it; the app
has no trigger sender (every silent push is a genuine relay delivery).
A+C (Phase-3 server onboarding):
- notify auto-detects the Syncthing API key + URL from config.xml when the
env vars are unset (encoding/xml on gui/{apikey,address,tls}); RELAY_URL
stays required (no production default). The API key is never logged.
- docker-compose runs the helper as the config.xml owner uid over a
read-only volume, so `docker compose up -d` needs no key copy.
Verified: gofmt/vet/test green; bare-binary + full docker path (real
syncthing/syncthing v2.1.0 image) auto-detect -> startup-announce -> 202;
Debug+Release build; Release strings audit clean (override=0, /trigger=0,
prod URL present).
RelayServerSetupView no longer asks for a Syncthing API key: drops the "Get your API key" step and shows a key-free `docker run` that mounts the Syncthing config dir read-only and lets the helper auto-detect the key. - Keeps `--network host` so the address auto-inferred from config.xml (typically 127.0.0.1:8384) reaches a same-host Syncthing. - Interpolates RelayService.productionRelayURL (not the DEBUG-overridable relayURL) so a lab build pointed at a mock can't leak that URL. - Softens over-promised promptness (B-Weg §8): "active" the moment the first wake-up arrives; later changes wake the app in the background. - L10n lockstep en/de/es/zh-Hans: 6 keys removed, 4 added, identical key sets, plutil -lint OK. es/zh-Hans need a native review.
…compose - First-boot race: loadConfigAwaitingSyncthing waits (bounded) for a not-yet-written config.xml instead of exiting immediately. Opt-in via SYNCTHING_CONFIG_WAIT_SECONDS (0/unset keeps fast-fail; compose sets 60). Missing RELAY_URL / permission denial / malformed config still fail fast (matched via the errNoSyncthingConfig sentinel). Tests cover both paths. - bootstrap.sh: <gui>-scope the address awk (was matching a leading <device><address>dynamic</address> and building http://dynamic); align the candidate list with the Go parser. - docker-compose.yml: STARTUP_ANNOUNCE + SYNCTHING_CONFIG_WAIT_SECONDS made explicit; document that `compose up` self-activates against the production-default RELAY_URL (override when testing).
CHANGELOG 1.5.1 "Added": self-activating Cloud Relay + key-free server setup (honest copy). docs/relay-activation-release-checklist.md records the pre-App-Store gates: real-device receive-leg test, aps-environment per-config + TestFlight, es/zh-Hans native review, NAS uid/path follow-ups. Integrated now with the risk documented (owner decision).
📝 WalkthroughWalkthroughVaultSync v1.5.1 adds key‑free Cloud Relay activation: the notify helper auto-detects Syncthing credentials from config.xml and can send a startup-announce trigger; the iOS app gains reactivation tracking and multi‑state UI to reflect delivery, first‑delivery celebration, and recovery prompts. ChangesCloud Relay auto-activation (v1.5.1)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
🚥 Pre-merge checks | ✅ 7✅ Passed checks (7 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
ios/VaultSync/Services/SubscriptionManager.swift (1)
441-463:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winPersist the cleared provision state in
deprovisionRelay().
Transaction.updatescan hit this path directly on revoke/expire. Right now the method returns before any status cleanup when the APNs token is missing, and even on success it never rewritesrelay-provisioned-device-ids. A later cold launch can therefore rehydrate stale.provisionedentries and briefly surface a false “likely working” state until the next explicit refresh.Suggested fix
private func deprovisionRelay() async { + let deviceIDs = loadStoredDeviceIDs() + ensureProvisionStateEntries(for: deviceIDs) + for deviceID in deviceIDs { + relayProvisionStatuses[deviceID] = .notAttempted + } + persistProvisionedDeviceIDs() + // Clear the per-activation "Connected" celebration on every transition to // inactive (revocation/expiration paths route here, not just // checkSubscriptionStatus), so a later resubscribe celebrates again. // Done before the token guard so it still clears when no token exists. UserDefaults.standard.removeObject(forKey: "relay-connected-celebrated") guard let token = KeychainService.getAPNsDeviceToken() else { return } - - let deviceIDs = loadStoredDeviceIDs() - ensureProvisionStateEntries(for: deviceIDs) for deviceID in deviceIDs { do { try await RelayService.deprovision(deviceID: deviceID, apnsToken: token) relayProvisionStatuses[deviceID] = .notAttempted } catch { logger.error("Failed to deprovision relay for device \(deviceID.prefix(8))...: \(error)") let userError = RelayService.userError(from: error) relayProvisionStatuses[deviceID] = .failed(reason: userError.message) errorMessage = userError.userVisibleDescription } } + persistProvisionedDeviceIDs() refreshStoredRelayDiagnostics() }
🧹 Nitpick comments (1)
notify/main.go (1)
253-253: 💤 Low valueMinor: RELAY_URL check after failed loadConfig is slightly redundant.
Line 253 checks
strings.TrimSpace(os.Getenv("RELAY_URL")) == ""again afterloadConfig()already failed. Since loadConfig validates RELAY_URL and includes it in the missing-keys error, you could instead check whether the error indicates a missing RELAY_URL (e.g., viaerrors.Isagainst a typed sentinel or inspecting the message). The current approach is safe but slightly inelegant—consider refactoring for clarity if you revisit this path.🤖 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.go` at line 253, The current conditional at the top-level that combines wait, err, errNoSyncthingConfig and a strings.TrimSpace(os.Getenv("RELAY_URL")) == "" is redundant because loadConfig already validates RELAY_URL; replace the env lookup with an explicit check on the loadConfig error instead. Concretely, in the branch that currently tests errors.Is(err, errNoSyncthingConfig) and strings.TrimSpace(os.Getenv("RELAY_URL")) == "", change it to inspect the loadConfig error (err) for the specific missing-RELAY_URL sentinel or message (e.g., use errors.Is(err, errMissingRelayURL) or match the error text returned by loadConfig) so the code relies on the typed sentinel from loadConfig (or a clear error string) rather than re-reading os.Getenv("RELAY_URL"); this keeps the logic in sync with loadConfig and removes the redundant getenv check.
🤖 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/project.yml`:
- Around line 84-85: The NSAppTransportSecurity/NSAllowsLocalNetworking key is
being applied to all builds; change ios/project.yml so NSAllowsLocalNetworking:
true is only added for Debug builds (scope the key to the Debug configuration).
Locate the NSAppTransportSecurity block and move or conditionally set
NSAllowsLocalNetworking under the Debug configuration (so Release builds do not
include NSAllowsLocalNetworking), keeping NSAppTransportSecurity present as
needed for other non-debug keys.
In `@ios/VaultSync/de.lproj/Localizable.strings`:
- Line 584: Remove the duplicated localization entry for the key "Could not
accept share '%@'.\n\n%@" so each locale file contains a single definition;
locate the duplicate occurrences of the exact key string in the
Localizable.strings files (e.g., the entries at lines referenced in the review)
and delete the redundant one, leaving only one mapping per locale (for example
keep the first or the correct translation and remove the second) to prevent
shadowing.
In `@ios/VaultSync/zh-Hans.lproj/Localizable.strings`:
- Line 584: The file contains a duplicated localization key "Could not accept
share '%@'.\n\n%@" — remove the later duplicate definition and keep a single
entry for that key (or if translations differ, consolidate into the correct
translation) so the key is defined only once; locate the duplicate by searching
for the exact key string and delete or replace the redundant line to avoid
silent overrides.
---
Nitpick comments:
In `@notify/main.go`:
- Line 253: The current conditional at the top-level that combines wait, err,
errNoSyncthingConfig and a strings.TrimSpace(os.Getenv("RELAY_URL")) == "" is
redundant because loadConfig already validates RELAY_URL; replace the env lookup
with an explicit check on the loadConfig error instead. Concretely, in the
branch that currently tests errors.Is(err, errNoSyncthingConfig) and
strings.TrimSpace(os.Getenv("RELAY_URL")) == "", change it to inspect the
loadConfig error (err) for the specific missing-RELAY_URL sentinel or message
(e.g., use errors.Is(err, errMissingRelayURL) or match the error text returned
by loadConfig) so the code relies on the typed sentinel from loadConfig (or a
clear error string) rather than re-reading os.Getenv("RELAY_URL"); this keeps
the logic in sync with loadConfig and removes the redundant getenv check.
🪄 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: cd0a39c7-ba9a-40ef-987f-af8a8f23610b
📒 Files selected for processing (25)
.gitignoreCHANGELOG.mddocs/relay-activation-release-checklist.mdios/VaultSync/App/AppDelegate.swiftios/VaultSync/App/VaultSyncApp.swiftios/VaultSync/Models/RelayProvisionStatus.swiftios/VaultSync/Services/RelayService.swiftios/VaultSync/Services/SubscriptionManager.swiftios/VaultSync/Views/ContentView.swiftios/VaultSync/Views/RelayDiagnosticsView.swiftios/VaultSync/Views/RelayHomeView.swiftios/VaultSync/Views/RelayServerSetupView.swiftios/VaultSync/de.lproj/Localizable.stringsios/VaultSync/en.lproj/Localizable.stringsios/VaultSync/es.lproj/Localizable.stringsios/VaultSync/zh-Hans.lproj/Localizable.stringsios/project.ymlnotify/.env.examplenotify/README.mdnotify/docker-compose.ymlnotify/main.gonotify/main_test.gonotify/scripts/bootstrap.shnotify/syncthing_config.gonotify/syncthing_config_test.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 (7)
**/*
⚙️ 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/project.ymlios/VaultSync/App/VaultSyncApp.swiftnotify/README.mddocs/relay-activation-release-checklist.mdios/VaultSync/App/AppDelegate.swiftnotify/docker-compose.ymlnotify/scripts/bootstrap.shios/VaultSync/Models/RelayProvisionStatus.swiftios/VaultSync/Services/RelayService.swiftios/VaultSync/Views/RelayDiagnosticsView.swiftCHANGELOG.mdnotify/syncthing_config.goios/VaultSync/Views/ContentView.swiftios/VaultSync/Views/RelayHomeView.swiftnotify/main.goios/VaultSync/en.lproj/Localizable.stringsios/VaultSync/Views/RelayServerSetupView.swiftios/VaultSync/es.lproj/Localizable.stringsios/VaultSync/zh-Hans.lproj/Localizable.stringsnotify/syncthing_config_test.goios/VaultSync/de.lproj/Localizable.stringsios/VaultSync/Services/SubscriptionManager.swiftnotify/main_test.go
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
**/*.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/App/VaultSyncApp.swiftios/VaultSync/App/AppDelegate.swiftios/VaultSync/Models/RelayProvisionStatus.swiftios/VaultSync/Services/RelayService.swiftios/VaultSync/Views/RelayDiagnosticsView.swiftios/VaultSync/Views/ContentView.swiftios/VaultSync/Views/RelayHomeView.swiftios/VaultSync/Views/RelayServerSetupView.swiftios/VaultSync/Services/SubscriptionManager.swift
ios/**/*.swift
📄 CodeRabbit inference engine (README.md)
ios/**/*.swift: Follow Swift API Design Guidelines for Swift code
Use Swift strict concurrency where applicable
Implement VoiceOver support throughout the app for accessibility
Support Dynamic Type for responsive text sizing
Files:
ios/VaultSync/App/VaultSyncApp.swiftios/VaultSync/App/AppDelegate.swiftios/VaultSync/Models/RelayProvisionStatus.swiftios/VaultSync/Services/RelayService.swiftios/VaultSync/Views/RelayDiagnosticsView.swiftios/VaultSync/Views/ContentView.swiftios/VaultSync/Views/RelayHomeView.swiftios/VaultSync/Views/RelayServerSetupView.swiftios/VaultSync/Services/SubscriptionManager.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/App/VaultSyncApp.swiftios/VaultSync/App/AppDelegate.swiftios/VaultSync/Models/RelayProvisionStatus.swiftios/VaultSync/Services/RelayService.swiftios/VaultSync/Views/RelayDiagnosticsView.swiftios/VaultSync/Views/ContentView.swiftios/VaultSync/Views/RelayHomeView.swiftios/VaultSync/Views/RelayServerSetupView.swiftios/VaultSync/Services/SubscriptionManager.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:
notify/README.mddocs/relay-activation-release-checklist.mdCHANGELOG.md
notify/docker-compose.yml
⚙️ CodeRabbit configuration file
notify/docker-compose.yml: Check that deployment examples do not hard-code real secrets and that environment variables,
restart policy, health checks, and network exposure are reasonable for self-hosted users.
Files:
notify/docker-compose.yml
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_config.gonotify/main.gonotify/syncthing_config_test.gonotify/main_test.go
🧠 Learnings (34)
📓 Common learnings
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/architecture.md:0-0
Timestamp: 2026-05-31T08:18:03.446Z
Learning: Applies to docs/notify/** : Cloud Relay is an optional Docker sidecar service that watches Syncthing on the homeserver for outgoing changes and sends APNs silent-push wake-ups to the iPhone. Refer to relay-spec.md for protocol details and troubleshooting.md for end-user issues.
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/architecture.md:0-0
Timestamp: 2026-05-31T08:18:03.446Z
Learning: VaultSync background sync is intentionally asymmetric: Server→iPhone sync is accelerated via Cloud Relay push notifications (server-side file watch + APNs); iPhone→Server sync relies on foreground execution in the VaultSync app, with background refresh as opportunistic only—not guaranteed real-time.
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: For Cloud Relay users, validate `vaultsync-notify --doctor` output and relay health status to ensure background sync dependencies are configured correctly
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/app-store-connect-relay.md:0-0
Timestamp: 2026-05-31T14:09:03.884Z
Learning: The Cloud Relay server-side helper setup must be framed as part of using the product in the in-app 'Set Up Your Server' step shown right after purchase, not as a gate to unlock paid content, to comply with App Store Guideline 3.1.2(a)
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Force-close VaultSync and reopen it when Syncthing is not running; keep the app in foreground for at least 20-30 seconds to allow the Syncthing bridge to start
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: README.md:0-0
Timestamp: 2026-06-01T08:37:12.985Z
Learning: Include relevant logs, iOS/iPadOS versions, Syncthing version, and Cloud Relay status when reporting bugs
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Capture VaultSync version, iOS version, **Sync Issues** screenshot, **Relay Diagnostics** screenshot, and `vaultsync-notify --doctor` output when filing bug reports
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T05:46:32.197Z
Learning: Verify the Receive leg on real hardware with a StoreKit sandbox subscription by running `vaultsync-notify` against the relay and confirming the startup-announce flips the app to 'Cloud Relay active' before App Store release
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T05:46:32.197Z
Learning: Verify per-config entitlements configuration (Debug = development / Release = production) through TestFlight before release
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T05:46:32.197Z
Learning: Conduct native language review for Spanish (es) and Simplified Chinese (zh-Hans) translations of the four new `RelayServerSetupView` strings (key-free command, uid hint, 'It activates itself') before release
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T05:46:32.197Z
Learning: Document that the default `user:` compose configuration is `1000:1000` (correct for official `syncthing/syncthing` image, but wrong for linuxserver/Unraid and Synology), and that linuxserver/Unraid users must set `PUID`/`PGID`, and Synology/QNAP users must set `SYNCTHING_CONFIG` with correct path `/volume1/appdata/syncthing/config.xml`
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T05:46:32.197Z
Learning: Ensure `RELAY_URL` environment variable is required and causes exit code 1 if missing, with no relay trigger occurring when undefined
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T05:46:32.197Z
Learning: Verify that the Release binary audit is clean: `RELAY_BASE_URL_OVERRIDE`=0, `/api/v1/trigger`=0, `relay.vaultsync.eu` present, and `provision`/`health` endpoints present
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T05:46:32.197Z
Learning: Prefer the binary's relay detection logic over shell script detection to avoid maintaining two separate detection paths
📚 Learning: 2026-05-31T08:18:40.401Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/setup.md:0-0
Timestamp: 2026-05-31T08:18:40.401Z
Learning: Applies to docs/ios/project.yml : Run `xcodegen generate` in the ios directory to generate the Xcode project from configuration
Applied to files:
ios/project.yml
📚 Learning: 2026-06-01T08:37:12.985Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: README.md:0-0
Timestamp: 2026-06-01T08:37:12.985Z
Learning: Applies to ios/**/*.pbxproj : iOS/iPadOS target version must be 18 or later
Applied to files:
ios/project.yml
📚 Learning: 2026-05-09T10:24:55.794Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt:0-0
Timestamp: 2026-05-09T10:24:55.794Z
Learning: Do not introduce logging, analytics, crash reporting, diagnostics, or network requests that include Obsidian note contents, sensitive filenames, absolute vault paths, security-scoped bookmark data, Syncthing API keys, relay API keys, APNs tokens, or StoreKit receipt details. Pass if changed code does not leak this data. Fail only when the changed code creates a concrete privacy or secret-leak risk.
Applied to files:
ios/project.yml
📚 Learning: 2026-05-31T08:18:40.401Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/setup.md:0-0
Timestamp: 2026-05-31T08:18:40.401Z
Learning: Applies to docs/ios/*.xcconfig : For device builds, copy `ios/Signing.local.xcconfig.example` to `ios/Signing.local.xcconfig` and set DEVELOPMENT_TEAM; this file must be gitignored
Applied to files:
ios/project.yml
📚 Learning: 2026-05-31T08:19:08.076Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Capture VaultSync version, iOS version, **Sync Issues** screenshot, **Relay Diagnostics** screenshot, and `vaultsync-notify --doctor` output when filing bug reports
Applied to files:
ios/project.ymlios/VaultSync/App/VaultSyncApp.swiftios/VaultSync/Views/RelayDiagnosticsView.swiftCHANGELOG.mdios/VaultSync/en.lproj/Localizable.strings
📚 Learning: 2026-05-31T14:09:03.884Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/app-store-connect-relay.md:0-0
Timestamp: 2026-05-31T14:09:03.884Z
Learning: Applies to docs/**/*.storekit* : The app code and `ios/VaultSync.storekit` already expect Cloud Relay monthly product ID `eu.vaultsync.app.relay.monthly` and yearly product ID `eu.vaultsync.app.relay.yearly` in App Store Connect
Applied to files:
ios/project.ymlios/VaultSync/App/VaultSyncApp.swiftdocs/relay-activation-release-checklist.mdios/VaultSync/Models/RelayProvisionStatus.swiftios/VaultSync/Services/RelayService.swiftCHANGELOG.mdios/VaultSync/en.lproj/Localizable.stringsios/VaultSync/Views/RelayServerSetupView.swiftios/VaultSync/es.lproj/Localizable.stringsios/VaultSync/zh-Hans.lproj/Localizable.stringsios/VaultSync/de.lproj/Localizable.stringsios/VaultSync/Services/SubscriptionManager.swift
📚 Learning: 2026-05-31T08:18:03.446Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/architecture.md:0-0
Timestamp: 2026-05-31T08:18:03.446Z
Learning: VaultSync background sync is intentionally asymmetric: Server→iPhone sync is accelerated via Cloud Relay push notifications (server-side file watch + APNs); iPhone→Server sync relies on foreground execution in the VaultSync app, with background refresh as opportunistic only—not guaranteed real-time.
Applied to files:
ios/project.ymlios/VaultSync/App/VaultSyncApp.swiftios/VaultSync/App/AppDelegate.swiftios/VaultSync/Models/RelayProvisionStatus.swiftCHANGELOG.mdios/VaultSync/en.lproj/Localizable.stringsios/VaultSync/Views/RelayServerSetupView.swiftios/VaultSync/es.lproj/Localizable.stringsios/VaultSync/zh-Hans.lproj/Localizable.stringsios/VaultSync/de.lproj/Localizable.strings
📚 Learning: 2026-05-31T08:18:40.401Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/setup.md:0-0
Timestamp: 2026-05-31T08:18:40.401Z
Learning: macOS with Xcode 26+ must be installed as a prerequisite for building VaultSync
Applied to files:
ios/project.yml
📚 Learning: 2026-05-31T08:18:03.446Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/architecture.md:0-0
Timestamp: 2026-05-31T08:18:03.446Z
Learning: Applies to docs/ios/**/*.swift : iOS app structure must follow the established directory layout: App/ for entry points, Models/ for data types, ViewModels/ for presentation logic, Views/ for SwiftUI components, Services/ for business logic (SyncBridgeService, SyncthingManager, BackgroundSyncService, RelayService, KeychainService, etc.), Resources/ for assets and theme, and *.lproj/ directories for localizations.
Applied to files:
ios/project.yml
📚 Learning: 2026-05-31T08:19:08.076Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: For Cloud Relay users, validate `vaultsync-notify --doctor` output and relay health status to ensure background sync dependencies are configured correctly
Applied to files:
ios/VaultSync/App/VaultSyncApp.swiftnotify/README.mddocs/relay-activation-release-checklist.mdnotify/docker-compose.ymlCHANGELOG.md
📚 Learning: 2026-05-31T08:18:03.446Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/architecture.md:0-0
Timestamp: 2026-05-31T08:18:03.446Z
Learning: Applies to docs/ios/**/*.swift : iOS background sync strategy: use `BGAppRefreshTask` (with ~15 min request interval) and `BGContinuedProcessingTask` (iOS 26+ for longer runtime). Implement a ~30s grace window after backgrounding to allow in-flight sync to complete.
Applied to files:
ios/VaultSync/App/VaultSyncApp.swift
📚 Learning: 2026-05-31T08:19:08.076Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Force-close VaultSync and reopen it when Syncthing is not running; keep the app in foreground for at least 20-30 seconds to allow the Syncthing bridge to start
Applied to files:
ios/VaultSync/App/VaultSyncApp.swiftnotify/docker-compose.yml
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: The iOS app must implement background push notification handling via `BGAppRefreshTask`, restoring vault bookmarks, starting Syncthing, polling for completion within the ~30s background budget, then stopping Syncthing
Applied to files:
ios/VaultSync/App/VaultSyncApp.swiftios/VaultSync/App/AppDelegate.swift
📚 Learning: 2026-05-31T08:18:58.296Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/sync-filters-ux.md:0-0
Timestamp: 2026-05-31T08:18:58.296Z
Learning: Applies to docs/go/bridge/**/*.go : Use `GetFolderIgnores` and `SetFolderIgnores` Go bridge methods to expose Syncthing per-folder ignore patterns in the UI
Applied to files:
.gitignore
📚 Learning: 2026-05-31T08:18:40.401Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/setup.md:0-0
Timestamp: 2026-05-31T08:18:40.401Z
Learning: Applies to docs/go/Makefile : Run `make patch` to apply dependency patches and create _syncthing_patched/ with applied fixes
Applied to files:
.gitignore
📚 Learning: 2026-05-31T08:19:08.076Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Applies to docs/**/notify/.env : Ensure `SYNCTHING_API_KEY` in `notify/.env` matches the Syncthing GUI API key and restart the container after updating
Applied to files:
notify/README.mdnotify/docker-compose.ymlnotify/.env.examplenotify/main.go
📚 Learning: 2026-05-31T08:19:08.076Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Applies to docs/**/notify/.env : Verify `RELAY_URL` in `notify/.env` is correctly set (default cloud value: `https://relay.vaultsync.eu`)
Applied to files:
notify/README.mddocs/relay-activation-release-checklist.mdnotify/docker-compose.ymlios/VaultSync/Services/RelayService.swiftCHANGELOG.mdnotify/.env.example
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: The `vaultsync-notify` container must accept configuration via environment variables: `SYNCTHING_API_URL`, `SYNCTHING_API_KEY`, `RELAY_URL`, `DEBOUNCE_SECONDS`, and `WATCHED_FOLDERS`
Applied to files:
notify/README.mdnotify/docker-compose.ymlnotify/.env.example
📚 Learning: 2026-05-31T08:18:03.446Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/architecture.md:0-0
Timestamp: 2026-05-31T08:18:03.446Z
Learning: Applies to docs/notify/** : Cloud Relay is an optional Docker sidecar service that watches Syncthing on the homeserver for outgoing changes and sends APNs silent-push wake-ups to the iPhone. Refer to relay-spec.md for protocol details and troubleshooting.md for end-user issues.
Applied to files:
notify/README.mddocs/relay-activation-release-checklist.mdios/VaultSync/App/AppDelegate.swiftnotify/docker-compose.ymlios/VaultSync/Models/RelayProvisionStatus.swiftios/VaultSync/Services/RelayService.swiftCHANGELOG.mdnotify/.env.examplenotify/main.goios/VaultSync/en.lproj/Localizable.stringsios/VaultSync/Views/RelayServerSetupView.swiftios/VaultSync/es.lproj/Localizable.stringsios/VaultSync/zh-Hans.lproj/Localizable.stringsios/VaultSync/de.lproj/Localizable.stringsios/VaultSync/Services/SubscriptionManager.swift
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: The vaultsync-notify container must automatically read the Syncthing Device ID from the `/rest/system/status` endpoint at startup — no manual Device ID configuration required
Applied to files:
notify/README.mdnotify/docker-compose.yml
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: Implement configurable debouncing (default: 5 seconds) in the vaultsync-notify container to batch rapid file-change signals into a single push notification
Applied to files:
notify/README.mdnotify/docker-compose.yml
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: The vaultsync-notify container must treat subscription-state errors (400/401/402/403) as recoverable and re-check on a slow cadence, but treat misconfiguration errors (404) as fatal and exit
Applied to files:
notify/README.mdnotify/docker-compose.yml
📚 Learning: 2026-05-31T14:09:03.884Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/app-store-connect-relay.md:0-0
Timestamp: 2026-05-31T14:09:03.884Z
Learning: The Cloud Relay server-side helper setup must be framed as part of using the product in the in-app 'Set Up Your Server' step shown right after purchase, not as a gate to unlock paid content, to comply with App Store Guideline 3.1.2(a)
Applied to files:
docs/relay-activation-release-checklist.mdios/VaultSync/en.lproj/Localizable.stringsios/VaultSync/Views/RelayServerSetupView.swiftios/VaultSync/es.lproj/Localizable.stringsios/VaultSync/zh-Hans.lproj/Localizable.stringsios/VaultSync/de.lproj/Localizable.strings
📚 Learning: 2026-05-31T08:19:08.076Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Check homeserver egress rules (firewall, VPN, proxy) to ensure they do not block `relay.vaultsync.eu`
Applied to files:
docs/relay-activation-release-checklist.md
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: APNs payload must be a silent push with no visible alert or body content, using `content-available: 1` to wake the app in the background
Applied to files:
ios/VaultSync/App/AppDelegate.swift
📚 Learning: 2026-05-09T10:24:55.794Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt:0-0
Timestamp: 2026-05-09T10:24:55.794Z
Learning: Applies to **/*.swift : 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.
Applied to files:
ios/VaultSync/App/AppDelegate.swift
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: The central relay's /health endpoint must return `status: ok` when reachable; a 200 response indicates the relay is reachable but not that pushes are delivered end-to-end
Applied to files:
ios/VaultSync/Views/RelayDiagnosticsView.swift
📚 Learning: 2026-06-01T08:37:12.985Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: README.md:0-0
Timestamp: 2026-06-01T08:37:12.985Z
Learning: Include relevant logs, iOS/iPadOS versions, Syncthing version, and Cloud Relay status when reporting bugs
Applied to files:
CHANGELOG.md
📚 Learning: 2026-05-31T14:09:03.884Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/app-store-connect-relay.md:0-0
Timestamp: 2026-05-31T14:09:03.884Z
Learning: Subscription disclosures (price/period, auto-renew until canceled, how to cancel, Terms/Privacy links) must be shown in Settings → Cloud Relay and the in-context upsell, satisfying App Store Guideline 3.1.2
Applied to files:
ios/VaultSync/Views/RelayHomeView.swiftios/VaultSync/en.lproj/Localizable.stringsios/VaultSync/Services/SubscriptionManager.swift
📚 Learning: 2026-05-31T08:18:58.296Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/sync-filters-ux.md:0-0
Timestamp: 2026-05-31T08:18:58.296Z
Learning: Applies to docs/**/*.{strings,json,xml,yaml} **/Localizable.strings **/en.lproj/** **/de.lproj/** **/es.lproj/** **/zh-Hans.lproj/** : Ship all new strings in English, German, Spanish, and Simplified Chinese locales
Applied to files:
ios/VaultSync/en.lproj/Localizable.stringsios/VaultSync/es.lproj/Localizable.stringsios/VaultSync/zh-Hans.lproj/Localizable.stringsios/VaultSync/de.lproj/Localizable.strings
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: Cloud Relay subscription pricing must be read from StoreKit at runtime and never hard-coded; support monthly and yearly auto-renewable subscription options
Applied to files:
ios/VaultSync/Services/SubscriptionManager.swift
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: The central relay must verify StoreKit signed transactions (JWS) offline against Apple's certificate chain for subscription expiry validation; accept legacy numeric transaction IDs for backward compatibility
Applied to files:
ios/VaultSync/Services/SubscriptionManager.swift
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: The iOS app must store provisioned Device IDs in the Keychain and re-provision on APNs token rotation; deprovision tokens on subscription expiry
Applied to files:
ios/VaultSync/Services/SubscriptionManager.swift
🪛 LanguageTool
notify/README.md
[style] ~87-~87: Try using a descriptive adverb here.
Context: ...vaultsync.eu`). Has no built-in default on purpose, so the helper never triggers a relay y...
(ON_PURPOSE_DELIBERATELY)
🔇 Additional comments (23)
notify/syncthing_config.go (1)
1-250: LGTM!notify/scripts/bootstrap.sh (2)
88-108: LGTM!
160-186: LGTM!notify/syncthing_config_test.go (1)
1-230: LGTM!notify/main.go (3)
26-33: Configuration auto-detection and precedence logic is well-structured.The per-field precedence (explicit env wins, detection fills gaps) correctly handles mixed scenarios where an operator sets one value explicitly and relies on auto-detection for the other. The privacy-conscious "API key never logged" contract is clearly documented and enforced.
Also applies to: 134-145, 158-193, 220-231
235-290: Bounded first-boot wait logic is sound.The fail-fast conditions (line 253) correctly distinguish "Syncthing hasn't written config.xml yet" (recoverable, wait) from "missing RELAY_URL" or "permission denied" (immediate misconfigurations, exit now). The poll-with-deadline pattern prevents burning the entire wait budget on a problem that won't self-resolve.
360-380: Startup announce best-effort behavior is correct.The bounded 15-second context keeps startup responsive, and the "only fatal exits" logic correctly allows the service to continue when the subscription is inactive (a self-resolving runtime state) while still catching genuine misconfigurations like a 404 from the wrong RELAY_URL.
notify/main_test.go (5)
16-43: LGTM!Also applies to: 45-61, 63-92, 94-123
125-158: Bounded wait tests correctly verify fail-fast and retry behavior.The first test uses a goroutine to simulate Syncthing writing config.xml after a delay, and shrinks the poll interval to 20ms for a fast test run. The second test verifies the fast-fail path when RELAY_URL is missing, asserting elapsed time < 1s to confirm the wait budget isn't burned on a genuine misconfiguration.
Also applies to: 160-190
463-538: Existing service tests correctly isolate change-detection behavior.Setting
StartupAnnounce: falsewith clear comments ensures these tests focus on change-driven triggers, subscription state transitions, and fatal error handling without interference from the startup announce path.Also applies to: 544-626, 631-669
671-695: Startup announce test coverage is comprehensive.The quiet Syncthing stub isolates announce behavior by never emitting change events, allowing tests to verify: exactly one announce fires when enabled, zero triggers when disabled, inactive subscription doesn't kill the service (best-effort), and fatal misconfiguration does exit. This validates the B1 activation requirement and anti-spam constraints.
Also applies to: 697-762, 764-813, 815-877, 879-917
919-956: LGTM!notify/.env.example (1)
1-11: LGTM!Also applies to: 14-20
notify/README.md (1)
84-87: LGTM!Also applies to: 91-94
notify/docker-compose.yml (1)
13-16: LGTM!Also applies to: 18-25, 27-38, 42-43, 45-48
.gitignore (1)
50-51: LGTM!ios/VaultSync/Services/RelayService.swift (1)
10-36: LGTM!ios/VaultSync/App/VaultSyncApp.swift (1)
28-32: LGTM!Also applies to: 132-147
ios/VaultSync/Models/RelayProvisionStatus.swift (1)
138-143: LGTM!ios/VaultSync/App/AppDelegate.swift (1)
55-59: LGTM!CHANGELOG.md (1)
9-13: LGTM!docs/relay-activation-release-checklist.md (1)
1-56: LGTM!ios/VaultSync/es.lproj/Localizable.strings (1)
599-602: Please confirm a native Spanish review on the new relay setup copy.These new setup strings are release-facing, and this exact area is already called out as a pre-release language check.
Based on learnings: "Conduct native language review for Spanish (es) and Simplified Chinese (zh-Hans) translations of the four new
RelayServerSetupViewstrings (key-free command, uid hint, 'It activates itself') before release"
govulncheck (notify) and (go bridge) failed on two Go standard-library
vulnerabilities present in go1.26.3 and fixed in go1.26.4:
- GO-2026-5039 (net/textproto: unescaped inputs in errors)
- GO-2026-5037 (crypto/x509: inefficient candidate hostname parsing)
Both are reached through pre-existing code (relay.go/doctor.go/main.go,
bridge/*) and affect main too — they surfaced because 1.26.4 shipped the
fixes. CI installs Go via go-version-file: {notify,go}/go.mod, so bumping
the `go` directive to 1.26.4 makes setup-go provision the patched toolchain.
Verified locally: notify `govulncheck ./...` on go1.26.4 reports "No
vulnerabilities found"; vet/test green.
…vision) - ATS: drop NSAllowsLocalNetworking from the committed project so Release keeps the strictest ATS (verified absent from the built Release Info.plist). It was relaxing ATS for all builds. A Debug-only build-script strip proved unreliable (Info.plist is finalized after script phases), so the loopback exception now lives only in the local gitignored Info.plist for the DEBUG mock-relay loop (per METHODS.md §1.3). - L10n: remove the duplicated key "Could not accept share '%@'.\n\n%@" (present twice in all four catalogs from the lab L10n pass); keep one definition each. plutil -lint OK, 570 keys, identical sets (lockstep). - deprovisionRelay(): clear and persist the provision state up front, before the APNs-token guard, so a revoke/expire via Transaction.updates (which can arrive without a token) can't leave stale `.provisioned` entries to rehydrate on the next cold launch and fake a "likely working" state. Verified: Debug+Release BUILD SUCCEEDED; Release binary audit still clean (override=0, /trigger=0, prod URL present).
- compose/.env: loud PRODUCTION-default warning + local-mock testing hint; keep the intentional zero-config self-activation default (not reversed) (F-1) - compose: user: comment flags non-1000 uids (linuxserver 911, Unraid 99:100, Synology `syncthing`) + SYNCTHING_CONFIG for NAS (F-8) - bootstrap.sh: write a compose-safe .env (Syncthing key/URL only on explicit override, secret never written) and drop the incoherent compose autostart — bootstrap is the host/bare-binary path, compose is a separate key-free stack (F-3) - README: lead with the key-free Docker Compose path; reframe bootstrap as guided host setup (F-6) - CHANGELOG: soften the activation-promptness copy to match the honest in-app wording (F-2) - SubscriptionManager.deprovisionRelay(): sweep ALL provision statuses (not just keychain-stored IDs) before persisting, closing the stale-rehydrate edge on the Transaction.updates revoke/expire path (F-4) - project.yml: correct the ATS comment — a loopback (127.0.0.1) mock needs no ATS exception; NSAllowsLocalNetworking never governed numeric IP literals (F-5) Verified: iOS Debug+Release BUILD SUCCEEDED; Release safety audit unchanged (override=0, /api/v1/trigger=0, ATS absent); `docker compose config` OK; `sh -n` OK; bootstrap.sh end-to-end vs mock writes a compose-safe .env and passes doctor.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
notify/scripts/bootstrap.sh (1)
166-178:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winBootstrap probe list has drifted from the runtime detector.
This list skips the
XDG_STATE_HOME/XDG_CONFIG_HOMElocations thatnotify/syncthing_config.gochecks first. On hosts with non-default XDG paths, bootstrap falls back to manual setup even though the helper itself can auto-detect the sameconfig.xml. Either mirror those candidates here or delegate detection to the binary so the two paths stay aligned. Based on learnings, "Prefer the binary's address and relay detection over shell script detection to avoid maintaining two separate detection paths".🤖 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/scripts/bootstrap.sh` around lines 166 - 178, Update the bootstrap probe to align with the runtime detector by either adding XDG_STATE_HOME and XDG_CONFIG_HOME derived candidates to the for-loop that iterates over "for candidate in ..." in notify/scripts/bootstrap.sh (mirroring the same precedence used by notify/syncthing_config.go) or by delegating detection to the existing helper in notify/syncthing_config.go (invoke the binary/detection routine and use its reported config.xml path) so the script and the binary share a single source of truth.
🤖 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 `@notify/scripts/bootstrap.sh`:
- Around line 312-333: The .env writer currently omits SYNCTHING_CONFIG and
comments out auto-detected SYNCTHING_API_URL/KEY even when detection was
performed from a custom path, breaking later runs; modify the bootstrap block
that writes to ENV_FILE so that if a custom SYNCTHING_CONFIG was used for
detection (i.e. SYNCTHING_CONFIG is set and equals the path you used to detect
config) you explicitly write "SYNCTHING_CONFIG=$SYNCTHING_CONFIG" into the
generated file and — in that case — write the concrete SYNCTHING_API_URL and/or
SYNCTHING_API_KEY values (SYNCTHING_API_URL=$SYNCTHING_API_URL and
SYNCTHING_API_KEY=$SYNCTHING_API_KEY) instead of the commented auto-detect
lines, preserving the custom-path flow for vaultsync-notify.
---
Outside diff comments:
In `@notify/scripts/bootstrap.sh`:
- Around line 166-178: Update the bootstrap probe to align with the runtime
detector by either adding XDG_STATE_HOME and XDG_CONFIG_HOME derived candidates
to the for-loop that iterates over "for candidate in ..." in
notify/scripts/bootstrap.sh (mirroring the same precedence used by
notify/syncthing_config.go) or by delegating detection to the existing helper in
notify/syncthing_config.go (invoke the binary/detection routine and use its
reported config.xml path) so the script and the binary share a single source of
truth.
🪄 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: 027ad2b0-4ab4-4a83-b8e9-1df90e81fcca
📒 Files selected for processing (13)
CHANGELOG.mdgo/go.modios/VaultSync/Services/SubscriptionManager.swiftios/VaultSync/de.lproj/Localizable.stringsios/VaultSync/en.lproj/Localizable.stringsios/VaultSync/es.lproj/Localizable.stringsios/VaultSync/zh-Hans.lproj/Localizable.stringsios/project.ymlnotify/.env.examplenotify/README.mdnotify/docker-compose.ymlnotify/go.modnotify/scripts/bootstrap.sh
💤 Files with no reviewable changes (4)
- ios/VaultSync/de.lproj/Localizable.strings
- ios/VaultSync/en.lproj/Localizable.strings
- ios/VaultSync/es.lproj/Localizable.strings
- ios/VaultSync/zh-Hans.lproj/Localizable.strings
✅ Files skipped from review due to trivial changes (4)
- notify/go.mod
- notify/.env.example
- CHANGELOG.md
- notify/README.md
🚧 Files skipped from review as they are similar to previous changes (1)
- ios/VaultSync/Services/SubscriptionManager.swift
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
**/*
⚙️ 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:
go/go.modios/project.ymlnotify/scripts/bootstrap.shnotify/docker-compose.yml
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/docker-compose.yml
⚙️ CodeRabbit configuration file
notify/docker-compose.yml: Check that deployment examples do not hard-code real secrets and that environment variables,
restart policy, health checks, and network exposure are reasonable for self-hosted users.
Files:
notify/docker-compose.yml
🧠 Learnings (23)
📓 Common learnings
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/architecture.md:0-0
Timestamp: 2026-05-31T08:18:03.446Z
Learning: Applies to docs/notify/** : Cloud Relay is an optional Docker sidecar service that watches Syncthing on the homeserver for outgoing changes and sends APNs silent-push wake-ups to the iPhone. Refer to relay-spec.md for protocol details and troubleshooting.md for end-user issues.
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: README.md:0-0
Timestamp: 2026-06-01T08:37:12.985Z
Learning: Include relevant logs, iOS/iPadOS versions, Syncthing version, and Cloud Relay status when reporting bugs
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: For Cloud Relay users, validate `vaultsync-notify --doctor` output and relay health status to ensure background sync dependencies are configured correctly
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/architecture.md:0-0
Timestamp: 2026-05-31T08:18:03.446Z
Learning: VaultSync background sync is intentionally asymmetric: Server→iPhone sync is accelerated via Cloud Relay push notifications (server-side file watch + APNs); iPhone→Server sync relies on foreground execution in the VaultSync app, with background refresh as opportunistic only—not guaranteed real-time.
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Applies to docs/**/notify/.env : Verify `RELAY_URL` in `notify/.env` is correctly set (default cloud value: `https://relay.vaultsync.eu`)
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Capture VaultSync version, iOS version, **Sync Issues** screenshot, **Relay Diagnostics** screenshot, and `vaultsync-notify --doctor` output when filing bug reports
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: The vaultsync-notify container must automatically read the Syncthing Device ID from the `/rest/system/status` endpoint at startup — no manual Device ID configuration required
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: The `vaultsync-notify` container must accept configuration via environment variables: `SYNCTHING_API_URL`, `SYNCTHING_API_KEY`, `RELAY_URL`, `DEBOUNCE_SECONDS`, and `WATCHED_FOLDERS`
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Force-close VaultSync and reopen it when Syncthing is not running; keep the app in foreground for at least 20-30 seconds to allow the Syncthing bridge to start
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T08:07:20.933Z
Learning: Verify the Cloud Relay activation receive path on real hardware with a StoreKit sandbox subscription by running `vaultsync-notify` against the relay and confirming the startup-announce flips the app to 'Cloud Relay active' state
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T08:07:20.933Z
Learning: Configure per-config entitlements in ios/project.yml with `aps-environment` set to `development` for Debug builds and `production` for Release builds, and verify via TestFlight before shipping
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T08:07:20.933Z
Learning: Ensure localized strings for RelayServerSetupView (key-free command, uid hint, 'It activates itself') in Spanish (es) and Simplified Chinese (zh-Hans) are reviewed by native speakers; German (de) and English (en) are already reviewed
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T08:07:20.933Z
Learning: Prefer the binary's address and relay detection over shell script detection to avoid maintaining two separate detection paths
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T08:07:20.933Z
Learning: Ensure RELAY_URL environment variable is always set and validated; exit with code 1 if missing to prevent unintended relay triggers
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T08:07:20.933Z
Learning: Verify that Release builds pass security audit checks: RELAY_BASE_URL_OVERRIDE must be 0, /api/v1/trigger must be 0, and relay.vaultsync.eu and provision/health endpoints must be present
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T08:07:20.933Z
Learning: Do not include a production relay default in the binary's RELAY_URL; rely on docker-compose.yml to set the production relay default for real subscribers
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T08:07:20.933Z
Learning: When testing locally, override RELAY_URL environment variable to a mock relay to prevent accidental connections to the production relay
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T08:07:20.933Z
Learning: Verify that the notify utility passes gofmt, go vet, go build, and go test with no skipped tests before release
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T08:07:20.933Z
Learning: Verify that Syncthing binary auto-detection correctly reads the API key from config.xml and extracts relay URLs without logging the API key
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T08:07:20.933Z
Learning: Verify permission handling: binary running as `-u 1000` can read relay configuration, and running as `-u 99` is properly denied with read-only mounts
📚 Learning: 2026-05-31T08:18:40.401Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/setup.md:0-0
Timestamp: 2026-05-31T08:18:40.401Z
Learning: Go 1.26+ must be installed via `brew install go` as a prerequisite for building VaultSync
Applied to files:
go/go.mod
📚 Learning: 2026-05-31T08:18:40.401Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/setup.md:0-0
Timestamp: 2026-05-31T08:18:40.401Z
Learning: Applies to docs/go/Makefile : Run `make patch` to apply dependency patches and create _syncthing_patched/ with applied fixes
Applied to files:
go/go.mod
📚 Learning: 2026-05-31T08:18:58.296Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/sync-filters-ux.md:0-0
Timestamp: 2026-05-31T08:18:58.296Z
Learning: Applies to docs/go/bridge/folderscan.go : Vault scanner should check both the sync folder root and one level deep to detect heavy folders in typical 'Obsidian root with vault subdirs' layout
Applied to files:
go/go.mod
📚 Learning: 2026-05-31T08:18:40.401Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/setup.md:0-0
Timestamp: 2026-05-31T08:18:40.401Z
Learning: gomobile and gobind must be installed via `make setup` as prerequisites for building VaultSync
Applied to files:
go/go.mod
📚 Learning: 2026-05-31T08:18:58.296Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/sync-filters-ux.md:0-0
Timestamp: 2026-05-31T08:18:58.296Z
Learning: Applies to docs/go/bridge/folderscan.go : Vault scanner should check for `.git`, `.copilot-index`, `node_modules`, and `.obsidian/cache` as heavy directory candidates
Applied to files:
go/go.mod
📚 Learning: 2026-05-31T08:18:58.296Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/sync-filters-ux.md:0-0
Timestamp: 2026-05-31T08:18:58.296Z
Learning: Applies to docs/go/bridge/folderscan.go : Aggregate scanner matches per pattern across multiple vaults (e.g., '.git in 3 vaults — 127 MB total')
Applied to files:
go/go.mod
📚 Learning: 2026-05-31T08:18:40.401Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/setup.md:0-0
Timestamp: 2026-05-31T08:18:40.401Z
Learning: Applies to docs/ios/project.yml : Run `xcodegen generate` in the ios directory to generate the Xcode project from configuration
Applied to files:
ios/project.yml
📚 Learning: 2026-05-09T10:24:55.794Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt:0-0
Timestamp: 2026-05-09T10:24:55.794Z
Learning: Do not introduce logging, analytics, crash reporting, diagnostics, or network requests that include Obsidian note contents, sensitive filenames, absolute vault paths, security-scoped bookmark data, Syncthing API keys, relay API keys, APNs tokens, or StoreKit receipt details. Pass if changed code does not leak this data. Fail only when the changed code creates a concrete privacy or secret-leak risk.
Applied to files:
ios/project.yml
📚 Learning: 2026-05-31T14:09:03.884Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/app-store-connect-relay.md:0-0
Timestamp: 2026-05-31T14:09:03.884Z
Learning: Applies to docs/**/*.storekit* : The app code and `ios/VaultSync.storekit` already expect Cloud Relay monthly product ID `eu.vaultsync.app.relay.monthly` and yearly product ID `eu.vaultsync.app.relay.yearly` in App Store Connect
Applied to files:
ios/project.yml
📚 Learning: 2026-05-31T08:19:08.076Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Capture VaultSync version, iOS version, **Sync Issues** screenshot, **Relay Diagnostics** screenshot, and `vaultsync-notify --doctor` output when filing bug reports
Applied to files:
ios/project.yml
📚 Learning: 2026-05-31T08:18:03.446Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/architecture.md:0-0
Timestamp: 2026-05-31T08:18:03.446Z
Learning: Applies to docs/ios/**/*.swift : iOS app structure must follow the established directory layout: App/ for entry points, Models/ for data types, ViewModels/ for presentation logic, Views/ for SwiftUI components, Services/ for business logic (SyncBridgeService, SyncthingManager, BackgroundSyncService, RelayService, KeychainService, etc.), Resources/ for assets and theme, and *.lproj/ directories for localizations.
Applied to files:
ios/project.yml
📚 Learning: 2026-05-31T08:18:03.446Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/architecture.md:0-0
Timestamp: 2026-05-31T08:18:03.446Z
Learning: VaultSync background sync is intentionally asymmetric: Server→iPhone sync is accelerated via Cloud Relay push notifications (server-side file watch + APNs); iPhone→Server sync relies on foreground execution in the VaultSync app, with background refresh as opportunistic only—not guaranteed real-time.
Applied to files:
ios/project.yml
📚 Learning: 2026-05-31T08:18:40.401Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/setup.md:0-0
Timestamp: 2026-05-31T08:18:40.401Z
Learning: macOS with Xcode 26+ must be installed as a prerequisite for building VaultSync
Applied to files:
ios/project.yml
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: The iOS app must implement background push notification handling via `BGAppRefreshTask`, restoring vault bookmarks, starting Syncthing, polling for completion within the ~30s background budget, then stopping Syncthing
Applied to files:
ios/project.yml
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: The `vaultsync-notify` container must accept configuration via environment variables: `SYNCTHING_API_URL`, `SYNCTHING_API_KEY`, `RELAY_URL`, `DEBOUNCE_SECONDS`, and `WATCHED_FOLDERS`
Applied to files:
notify/scripts/bootstrap.shnotify/docker-compose.yml
📚 Learning: 2026-05-31T08:19:08.076Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Applies to docs/**/notify/.env : Ensure `SYNCTHING_API_KEY` in `notify/.env` matches the Syncthing GUI API key and restart the container after updating
Applied to files:
notify/scripts/bootstrap.shnotify/docker-compose.yml
📚 Learning: 2026-05-31T08:19:08.076Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Applies to docs/**/notify/.env : Verify `RELAY_URL` in `notify/.env` is correctly set (default cloud value: `https://relay.vaultsync.eu`)
Applied to files:
notify/scripts/bootstrap.shnotify/docker-compose.yml
📚 Learning: 2026-05-31T08:19:08.076Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: For Cloud Relay users, validate `vaultsync-notify --doctor` output and relay health status to ensure background sync dependencies are configured correctly
Applied to files:
notify/scripts/bootstrap.sh
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: The vaultsync-notify container must automatically read the Syncthing Device ID from the `/rest/system/status` endpoint at startup — no manual Device ID configuration required
Applied to files:
notify/scripts/bootstrap.shnotify/docker-compose.yml
📚 Learning: 2026-05-31T08:18:03.446Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/architecture.md:0-0
Timestamp: 2026-05-31T08:18:03.446Z
Learning: Applies to docs/notify/** : Cloud Relay is an optional Docker sidecar service that watches Syncthing on the homeserver for outgoing changes and sends APNs silent-push wake-ups to the iPhone. Refer to relay-spec.md for protocol details and troubleshooting.md for end-user issues.
Applied to files:
notify/scripts/bootstrap.shnotify/docker-compose.yml
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: Implement configurable debouncing (default: 5 seconds) in the vaultsync-notify container to batch rapid file-change signals into a single push notification
Applied to files:
notify/docker-compose.yml
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: The vaultsync-notify container must treat subscription-state errors (400/401/402/403) as recoverable and re-check on a slow cadence, but treat misconfiguration errors (404) as fatal and exit
Applied to files:
notify/docker-compose.yml
🔇 Additional comments (1)
ios/project.yml (1)
95-99: 🏗️ Heavy liftMake
aps-environmentbuild-configuration specific (Debug=development, Release=production).
ios/project.yml(lines 95-99) sets the entitlementsaps-environmenttodevelopment. Ensure Release builds useproduction; otherwise production silent push delivery (and any Cloud Relay/background wake-ups depending on it) can fail outside Debug. If XcodeGen/this block generates a single entitlements file for all configurations, switch to per-config entitlements (separate.entitlements+CODE_SIGN_ENTITLEMENTSset per build configuration).
TestLoadConfigAwaitingSyncthingWaitsForConfigXML wrote the fixture config.xml via a non-atomic os.WriteFile while the wait loop polls every 20ms; a poll landing mid-write read an empty/partial file and failed on a transient parse error (which loadConfigAwaitingSyncthing correctly treats as non-retryable). The window is tiny on a fast local disk but wider on a slow/contended CI runner, so it flaked there. Publish the fixture atomically (temp file + rename) so a poll never observes a partial write. No production-code change. Verified: gofmt/vet clean; `go test -count=1 ./...` ok; the test green 300x.
Follow-up to the F-3 compose-safe .env: that writer comments out the auto-detected Syncthing URL/key. When config.xml was only found via an explicit SYNCTHING_CONFIG (a non-standard path), the generated .env then had no way to point a later bare-binary/systemd start at that config — auto-detection would probe only the standard locations and fail. Now, when detection came from an explicit SYNCTHING_CONFIG, the .env pins SYNCTHING_CONFIG (the binary still auto-detects key/URL from it, so no secret is written). Standard-path detection still omits it, keeping the .env compose-safe. Addresses the CodeRabbit PR #33 review comment. Verified: sh -n OK; bootstrap end-to-end vs mock — custom path => .env pins SYNCTHING_CONFIG and a later start (only that .env, key/URL unset) auto-detects the config and fires the announce; standard path => no SYNCTHING_CONFIG written, Syncthing values stay commented (compose-safe).
The root README was not touched by the activation work, so it had drifted from CHANGELOG.md and from the shipped key-free flow: - "What's New — v1.5.1" now leads with Cloud Relay self-activation (startup-announce) and the key-free server setup, matching the CHANGELOG; the #25 vault self-healing follows. - "Cloud Relay sidecar" leads with the key-free Docker Compose path (shared config volume, no API key to paste) instead of a snippet that hardcoded SYNCTHING_API_KEY (which contradicted "No API key to copy"); bootstrap.sh is reframed as the guided host path and no longer described as starting the stack (it doesn't, since the compose-autostart was removed). CHANGELOG.md needs no change (already updated); the other branch fixes are internal.
Native-level review of the new 1.5.1 relay-activation copy. Keys, parity (570/locale) and plutil lint unchanged; only target values touched. zh-Hans (high): the setup body told users VaultSync flips to "Cloud Relay 已激活", but the actual badge renders "Cloud Relay 已启用" — the instruction pointed at a label that never appears. Aligned to the badge. Also: 已沉寂 → 暂无动静 (calmer, recoverable tone), 完成 Cloud Relay 的激活 → 完成激活 Cloud Relay (verb-led title), 联系到 → 联系上了 and 更改/瞬间 → 改动/发生的那一刻 (match shipped sibling phrasing), and "唤醒信号即会开始" → "…开始送达" (a signal is delivered, not "begun"). es: unify the helper term — every shipped Relay string says "asistente" but the new strings introduced "ayudante" (6×); normalized to "asistente". "Paso 2 —" → "Paso 2:" to match the sibling step headers (Paso 1/Paso 3). "acaba de alcanzar" → "acaba de conectarse con" (networking sense), "quedó en silencio" → "está en silencio" (present-tense status), "hace poco que no llega" → "últimamente no ha llegado" (fixes inverted time sense), "ese primer aviso" → "esa primera señal de activación" (consistent wake-up term), "se ejecuta" → "esté en marcha" (subjunctive). de: "Cloud Relay fertig aktivieren" → "Cloud-Relay-Aktivierung abschließen" (idiomatic, matches "Server-Einrichtung abschließen"); restore "Weck-Signal" in the step-2 body (was "Lieferung"); "seit Kurzem" → "in letzter Zeit" (was understating the gap); warmer celebration line via "im selben Moment".
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@README.md`:
- Line 177: Replace the unsafe "curl ... | bash" invocation in the README (the
line with "curl -fsSL
https://raw.githubusercontent.com/psimaker/vaultsync/main/notify/scripts/bootstrap.sh
| bash") with a pinned-tag or commit URL and an inspect-then-run example: point
the URL to a specific release tag or commit SHA, show commands to download the
script to a temp file (curl -o /tmp/bootstrap.sh), verify its checksum or
signature, open/review the file, and only then run it (bash /tmp/bootstrap.sh);
update the README text to recommend verifying integrity and using pinned URLs
rather than piping from the mutable main branch.
🪄 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: 57747d85-cb99-4621-a5e9-6f2d0534d8a8
📒 Files selected for processing (6)
README.mdios/VaultSync/de.lproj/Localizable.stringsios/VaultSync/es.lproj/Localizable.stringsios/VaultSync/zh-Hans.lproj/Localizable.stringsnotify/main_test.gonotify/scripts/bootstrap.sh
✅ Files skipped from review due to trivial changes (2)
- ios/VaultSync/de.lproj/Localizable.strings
- ios/VaultSync/es.lproj/Localizable.strings
🚧 Files skipped from review as they are similar to previous changes (3)
- notify/scripts/bootstrap.sh
- ios/VaultSync/zh-Hans.lproj/Localizable.strings
- notify/main_test.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 (2)
**/*
⚙️ 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:
README.md
**/*.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:
README.md
🧠 Learnings (11)
📓 Common learnings
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/architecture.md:0-0
Timestamp: 2026-05-31T08:18:03.446Z
Learning: Applies to docs/notify/** : Cloud Relay is an optional Docker sidecar service that watches Syncthing on the homeserver for outgoing changes and sends APNs silent-push wake-ups to the iPhone. Refer to relay-spec.md for protocol details and troubleshooting.md for end-user issues.
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Applies to docs/**/notify/.env : Verify `RELAY_URL` in `notify/.env` is correctly set (default cloud value: `https://relay.vaultsync.eu`)
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: For Cloud Relay users, validate `vaultsync-notify --doctor` output and relay health status to ensure background sync dependencies are configured correctly
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/architecture.md:0-0
Timestamp: 2026-05-31T08:18:03.446Z
Learning: VaultSync background sync is intentionally asymmetric: Server→iPhone sync is accelerated via Cloud Relay push notifications (server-side file watch + APNs); iPhone→Server sync relies on foreground execution in the VaultSync app, with background refresh as opportunistic only—not guaranteed real-time.
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Capture VaultSync version, iOS version, **Sync Issues** screenshot, **Relay Diagnostics** screenshot, and `vaultsync-notify --doctor` output when filing bug reports
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: README.md:0-0
Timestamp: 2026-06-01T08:37:12.985Z
Learning: Include relevant logs, iOS/iPadOS versions, Syncthing version, and Cloud Relay status when reporting bugs
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: The vaultsync-notify container must automatically read the Syncthing Device ID from the `/rest/system/status` endpoint at startup — no manual Device ID configuration required
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: The `vaultsync-notify` container must accept configuration via environment variables: `SYNCTHING_API_URL`, `SYNCTHING_API_KEY`, `RELAY_URL`, `DEBOUNCE_SECONDS`, and `WATCHED_FOLDERS`
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Force-close VaultSync and reopen it when Syncthing is not running; keep the app in foreground for at least 20-30 seconds to allow the Syncthing bridge to start
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T09:06:11.240Z
Learning: Use Conventional Commits for commit messages
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T09:06:11.240Z
Learning: Provide clear PR descriptions when contributing
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T09:06:23.810Z
Learning: Verify the receive leg for Cloud Relay activation on real hardware with a StoreKit sandbox subscription by running `vaultsync-notify` against the relay and confirming the startup-announce flips the app to 'Cloud Relay active' before App Store release
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T09:06:23.810Z
Learning: Verify `aps-environment` configuration through TestFlight before App Store submission to ensure silent push delivery is not broken for paying users
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T09:06:23.810Z
Learning: Ensure localization strings for `RelayServerSetupView` (key-free command, uid hint, 'It activates itself') are reviewed by native speakers for Spanish (es) and Simplified Chinese (zh-Hans) before release
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T09:06:23.810Z
Learning: Document that the default `user:` compose configuration of `1000:1000` is correct for `syncthing/syncthing` image but not for linuxserver/Unraid (PUID/PGID 99:100) or Synology (`sc-syncthing`), and those users must set `PUID`/`PGID` environment variables
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T09:06:23.810Z
Learning: For longer-term improvements, prefer the binary's relay detection over shell script detection to avoid maintaining two separate detection paths
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T09:06:23.810Z
Learning: Ensure all iOS app builds (Debug and Release configurations) compile successfully with `BUILD SUCCEEDED` status before release
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T09:06:23.810Z
Learning: Verify Release binary audit shows no occurrences of `RELAY_BASE_URL_OVERRIDE`, `/api/v1/trigger`, hardcoded `relay.vaultsync.eu`, or leaked credential strings before release
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T09:06:23.810Z
Learning: Verify that all silent pushes delivered to the app are genuine relay deliveries and that the 'Cloud Relay active' state cannot be faked due to the absence of a local trigger sender
📚 Learning: 2026-05-31T08:18:03.446Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/architecture.md:0-0
Timestamp: 2026-05-31T08:18:03.446Z
Learning: Applies to docs/notify/** : Cloud Relay is an optional Docker sidecar service that watches Syncthing on the homeserver for outgoing changes and sends APNs silent-push wake-ups to the iPhone. Refer to relay-spec.md for protocol details and troubleshooting.md for end-user issues.
Applied to files:
README.md
📚 Learning: 2026-05-31T08:19:08.076Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: For Cloud Relay users, validate `vaultsync-notify --doctor` output and relay health status to ensure background sync dependencies are configured correctly
Applied to files:
README.md
📚 Learning: 2026-05-31T08:18:03.446Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/architecture.md:0-0
Timestamp: 2026-05-31T08:18:03.446Z
Learning: VaultSync background sync is intentionally asymmetric: Server→iPhone sync is accelerated via Cloud Relay push notifications (server-side file watch + APNs); iPhone→Server sync relies on foreground execution in the VaultSync app, with background refresh as opportunistic only—not guaranteed real-time.
Applied to files:
README.md
📚 Learning: 2026-05-31T08:19:08.076Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Capture VaultSync version, iOS version, **Sync Issues** screenshot, **Relay Diagnostics** screenshot, and `vaultsync-notify --doctor` output when filing bug reports
Applied to files:
README.md
📚 Learning: 2026-05-31T08:19:08.076Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Install and open Obsidian at least once on the iOS device before attempting to connect the Obsidian folder in VaultSync
Applied to files:
README.md
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: The `vaultsync-notify` container must accept configuration via environment variables: `SYNCTHING_API_URL`, `SYNCTHING_API_KEY`, `RELAY_URL`, `DEBOUNCE_SECONDS`, and `WATCHED_FOLDERS`
Applied to files:
README.md
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: The vaultsync-notify container must automatically read the Syncthing Device ID from the `/rest/system/status` endpoint at startup — no manual Device ID configuration required
Applied to files:
README.md
📚 Learning: 2026-05-31T08:19:08.076Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Applies to docs/**/notify/.env : Verify `RELAY_URL` in `notify/.env` is correctly set (default cloud value: `https://relay.vaultsync.eu`)
Applied to files:
README.md
📚 Learning: 2026-05-31T08:19:08.076Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Applies to docs/**/notify/.env : Ensure `SYNCTHING_API_KEY` in `notify/.env` matches the Syncthing GUI API key and restart the container after updating
Applied to files:
README.md
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: Implement configurable debouncing (default: 5 seconds) in the vaultsync-notify container to batch rapid file-change signals into a single push notification
Applied to files:
README.md
🔇 Additional comments (1)
README.md (1)
65-67: LGTM!Also applies to: 158-175
Cut to ~145 lines (from 270): single Mermaid diagram, key-free Quick Start and Cloud Relay self-activation lead, NAS uid warning. Drop the What's New narrative, the Features table, and the prose that restated the diagram.
Lead with the key-free Docker Compose path, add the exact in-app `docker run` host command, and document the new env vars (STARTUP_ANNOUNCE, SYNCTHING_CONFIG_WAIT_SECONDS, SYNCTHING_CONFIG). NAS uid mismatch is now a callout; first-boot noise moved to a details block. Troubleshooting anchors preserved.
Add a symptom→section triage table and a tight Looks like / Fix shape per issue. Replace the API-key-paste section with the key-free permission/wrong-config reality, and point relay steps at the redesigned Cloud Relay tab. All nine heading anchors the app deep-links to are preserved verbatim.
setup.md: prerequisites/troubleshooting as tables, build steps as one annotated block. architecture.md: front-load the diagram and sync strategy, move the bridge export list into a details block, tabulate the app structure.
Update status to 1.5.1, fix the stale homeserver-container env table (RELAY_URL is the only required var; key/URL auto-detected), add STARTUP_ANNOUNCE/SYNCTHING_CONFIG/wait, document startup-announce on POST /trigger, and point relay UI references at the redesigned Cloud Relay tab.
Lead with the benefit and a 3-step paste-and-go flow that surfaces this PR's real wins (one in-app command, no API key, self-activating) instead of opening with a docker-compose block. Move the compose/NAS uid detail to notify/README.md and drop the dev-only mock-relay note. Privacy and honest directional wording kept, reframed as a selling point rather than a warning box.
…inds RELAY_URL bootstrap.sh wrote RELAY_URL only into notify/.env, then printed `./vaultsync-notify` as the start command. The binary has no .env autoload (only os.Getenv) and RELAY_URL is required with no default, so that command failed with "required configuration not set: RELAY_URL" — while the preceding --doctor step (which passes RELAY_URL inline) masked it. The run hint now sources the .env, and the systemd alternative spells out EnvironmentFile.
…uide label, README anchor) - README: "changes arrive the moment they happen" -> "arrive near-realtime, usually within seconds", matching architecture.md / relay-spec.md (5s debounce, ~30s coalescing, APNs priority 5). - release checklist: linuxserver PUID is 911:911 (was wrongly lumped with Unraid as 99:100); drop the dangling EVAL-bweg-ac.md reference (not tracked in the repo; the corrected Synology path is already inline). - notify/README: replace the fragile #product-scope anchor (emoji-heading slug differs on GitHub) with plain "Product scope below". - RelayServerSetupView + L10n (en/de/es/zh-Hans): the linked guide installs nothing in a single command, so "one-command install/installer" becomes "guided setup script".
Whitespace-only: fix the right-hand box borders in the homeserver architecture diagram and the Sync Filters mockups so the columns line up.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
docs/relay-spec.md (1)
220-220:⚠️ Potential issue | 🟠 Major | ⚡ Quick winFix provisioning rate-limit scope in the spec.
This line currently states provisioning is rate limited per Device ID, but the relay requirement is provisioning per IP and triggers per Device ID. Please correct the scope to avoid implementing the wrong protection model.
As per coding guidelines, "
docs/**/relay*: Rate limit relay endpoints: provisioning at 60 requests/minute per IP, triggers at 10 requests/minute per Device ID".🤖 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 `@docs/relay-spec.md` at line 220, Update the incorrect rate-limit scope sentence that currently reads "Rate limiting per Device ID: 60 requests/minute for provisioning, 10 triggers/minute" so it matches the relay requirement: change it to explicitly state "Rate limiting: provisioning at 60 requests/minute per IP, triggers at 10 requests/minute per Device ID". Ensure the revised wording appears where the original line exists in docs/relay-spec.md (the line shown in the diff) and follows the repository guideline phrasing for relay endpoints.
🤖 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.
Outside diff comments:
In `@docs/relay-spec.md`:
- Line 220: Update the incorrect rate-limit scope sentence that currently reads
"Rate limiting per Device ID: 60 requests/minute for provisioning, 10
triggers/minute" so it matches the relay requirement: change it to explicitly
state "Rate limiting: provisioning at 60 requests/minute per IP, triggers at 10
requests/minute per Device ID". Ensure the revised wording appears where the
original line exists in docs/relay-spec.md (the line shown in the diff) and
follows the repository guideline phrasing for relay endpoints.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 0b559edc-08d8-4ad1-b041-ae500d65a4fb
📒 Files selected for processing (14)
README.mddocs/architecture.mddocs/relay-activation-release-checklist.mddocs/relay-spec.mddocs/setup.mddocs/sync-filters-ux.mddocs/troubleshooting.mdios/VaultSync/Views/RelayServerSetupView.swiftios/VaultSync/de.lproj/Localizable.stringsios/VaultSync/en.lproj/Localizable.stringsios/VaultSync/es.lproj/Localizable.stringsios/VaultSync/zh-Hans.lproj/Localizable.stringsnotify/README.mdnotify/scripts/bootstrap.sh
✅ Files skipped from review due to trivial changes (4)
- docs/sync-filters-ux.md
- docs/setup.md
- docs/relay-activation-release-checklist.md
- ios/VaultSync/en.lproj/Localizable.strings
🚧 Files skipped from review as they are similar to previous changes (4)
- ios/VaultSync/de.lproj/Localizable.strings
- ios/VaultSync/zh-Hans.lproj/Localizable.strings
- ios/VaultSync/Views/RelayServerSetupView.swift
- notify/scripts/bootstrap.sh
📜 Review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*
⚙️ 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:
docs/troubleshooting.mddocs/relay-spec.mdREADME.mdios/VaultSync/es.lproj/Localizable.stringsdocs/architecture.mdnotify/README.md
**/*.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.mddocs/relay-spec.mdREADME.mddocs/architecture.mdnotify/README.md
docs/**/relay*
📄 CodeRabbit inference engine (docs/relay-spec.md)
POST /provision endpoint must verify StoreKit signed transaction (JWS) offline against Apple's certificate chain and extract/validate expiry; accept legacy numeric transaction IDs for backward compatibility
Store device APNs tokens encrypted at rest using AES-256-GCM with encryption keys stored separately (environment variable or secrets manager) in the central relay database
Automatically remove device tokens from the relay database if they are reported invalid by APNs (BadDeviceToken or Unregistered) on the next trigger request
Implement server-side rate limiting on the relay trigger endpoint at approximately 1 push per Device ID per 30-second window
Rate limit relay endpoints: provisioning at 60 requests/minute per IP, triggers at 10 requests/minute per Device ID
Accept only Device IDs with an active, non-expired subscription (verified via StoreKit transaction expiry) on relay trigger requests; deny triggers for expired or cancelled subscriptions
Require TLS 1.2 or higher on all relay API endpoints; implement HSTS header
Configure APNs push notifications with type
background,content-available: 1, topiceu.vaultsync.app, priority 5 (low), and expiration +1 hourUse token-based APNs authentication (p8 key) instead of certificate-based authentication
DELETE /provision endpoint must remove device token registration for the given Device ID and APNs token pair
POST /trigger endpoint must return 202 Accepted immediately without waiting for push delivery (async processing)
Files:
docs/relay-spec.md
**/*.strings
📄 CodeRabbit inference engine (README.md)
Provide localization in English, German, Spanish, and Simplified Chinese
Files:
ios/VaultSync/es.lproj/Localizable.strings
🧠 Learnings (31)
📓 Common learnings
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:51:18.788Z
Learning: Use MPL-2.0 license for all code
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:51:18.788Z
Learning: Maintain privacy by ensuring Cloud Relay only accesses Syncthing Device ID and APNs token, never vault contents or file metadata
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:51:18.788Z
Learning: Syncthing integration must sync files peer-to-peer into Obsidian's iOS sandbox without requiring cloud storage or user accounts
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:51:31.020Z
Learning: Use gomobile to embed Syncthing's Go reference implementation as an iOS library — no reimplementation of the protocol in Swift
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:51:31.020Z
Learning: Ensure guaranteed wire compatibility by using Syncthing's Go reference implementation rather than Swift reimplementation
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:51:31.020Z
Learning: Export the Go bridge as `.xcframework` via gomobile with a thin API interface
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:51:31.020Z
Learning: Implement Server → iPhone sync via `vaultsync-notify` spotting outgoing changes and sending Cloud Relay silent push notifications
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:51:31.020Z
Learning: Document that Cloud Relay is an acceleration path for `server → iPhone` sync, not a guarantee of symmetric real-time background sync
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:51:31.020Z
Learning: Use Syncthing's foreground mode for immediate, continuous, unrestricted sync when app is in foreground
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:51:41.364Z
Learning: Use the binary's built-in detection logic for GUI address and config candidate list instead of shell script detection to maintain a single source of truth
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:51:41.364Z
Learning: Release builds must pass binary audit validation with `RELAY_BASE_URL_OVERRIDE=0`, no `/api/v1/trigger` endpoint exposure, production relay domain present, and provision/health endpoints verified
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:51:41.364Z
Learning: On real iOS devices, the `didReceiveRemoteNotification` → `markReceived` → 'Cloud Relay active' path must be verified on a StoreKit sandbox subscription before App Store release
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:51:41.364Z
Learning: Update Synology/QNAP configuration documentation to reflect the correct config path as `/volume1/appdata/syncthing/config.xml` instead of `…/syncthing/var/config.xml`
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:06.835Z
Learning: No file content, folder names, file names, file sizes, or metadata must be transmitted from homeserver to relay — only Syncthing Device ID is sent
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:06.835Z
Learning: APNs payload must be a silent push with no visible content (content-available only, no alert or body text)
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:06.835Z
Learning: Identity is based on Syncthing Device ID; no user accounts, API keys, or Bearer token authentication required for trigger requests
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:06.835Z
Learning: Central relay is horizontally scalable: stateless request handling with shared database for token storage
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:06.835Z
Learning: vaultsync-notify container is stateless except for configuration — no persistent storage required
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:16.804Z
Learning: Requires macOS + Xcode 26+ for development setup
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:16.804Z
Learning: Requires Go 1.26+ for building the project
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:16.804Z
Learning: Requires gomobile + gobind tools for building the xcframework bridge
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:16.804Z
Learning: Requires XcodeGen for generating iOS projects
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:16.804Z
Learning: Run `make setup` to install gomobile + gobind and initialize gomobile
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:16.804Z
Learning: Run `make patch` to create patched Syncthing with applied fixes before building xcframework
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:16.804Z
Learning: Build Go bridge by running `make xcframework` to generate SyncBridge.xcframework (~160 MB)
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:16.804Z
Learning: Run `xcodegen generate` in the ios/ directory to generate VaultSync.xcodeproj
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:16.804Z
Learning: Simulator builds can pass CODE_SIGNING_ALLOWED=NO to bypass signing team requirement
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:16.804Z
Learning: Run Go bridge tests with `make test` in the go/ directory
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:16.804Z
Learning: Run iOS tests using xcodebuild with appropriate simulator destination and CODE_SIGNING_ALLOWED=NO
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:16.804Z
Learning: Add $(go env GOPATH)/bin to $PATH if gomobile command is not found
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:16.804Z
Learning: Run `make clean` then retry `make xcframework` if xcframework build fails
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:16.804Z
Learning: If Xcode project is missing, run `xcodegen generate` in the ios/ directory
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:16.804Z
Learning: Ensure an iOS 18+ simulator runtime is installed in Xcode if simulator is unavailable
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:16.804Z
Learning: Reference troubleshooting.md for runtime and sync issues
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:26.613Z
Learning: In `ConflictDiffView`, write a pair of patterns to `.stignore` when skipping a file: the exact relative path plus a matching `<path>.sync-conflict-*` glob pattern
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:26.613Z
Learning: Use straightforward user-facing terminology: prefer 'Skip on this iPhone' and 'Sync Filters' over jargon like 'Ignore patterns', 'Exclusions', or 'Filter rules'
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:26.613Z
Learning: Recommended presets (Workspace state and Trash) should be silently applied when a new vault folder is added, preventing immediate sync conflicts from `workspace.json`
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:26.613Z
Learning: All new strings in the Sync Filters feature must be shipped in English, German, Spanish, and Simplified Chinese
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:26.613Z
Learning: When a user skips a conflicted file via 'Always skip on this iPhone', delete any sync-conflict copies from disk but preserve the original file
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:26.613Z
Learning: In the Sync Filters UI, render conflict-copy pattern pairs as a single row with a '+ conflict copies' caption rather than as separate entries
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:26.613Z
Learning: The preset catalog should include specific narrow presets per common use case (e.g., `.git`, `.copilot-index`) rather than umbrella toggles for broad categories like 'Plugin caches'
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:37.612Z
Learning: Ensure Syncthing is running and keep the VaultSync app in the foreground for 20-30 seconds during initial setup to allow the bridge to start and the Device ID to appear
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:37.612Z
Learning: Ensure vaultsync-notify runs with correct file ownership permissions (PUID/PGID) to read Syncthing's config.xml in 0600 mode
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:37.612Z
Learning: Point SYNCTHING_CONFIG to the correct path if auto-detection fails, especially on Synology/QNAP systems
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:37.612Z
Learning: Remove manual SYNCTHING_API_KEY overrides in environment to allow auto-detection from config.xml, or update it to match Syncthing's current key
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:37.612Z
Learning: Test relay health and connectivity using curl to verify the relay endpoint is reachable before troubleshooting iOS app issues
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:37.612Z
Learning: Verify that Desktop Syncthing has the iOS Device ID listed in the folder sharing configuration before expecting pending shares to appear in the app
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:37.612Z
Learning: Retry APNs registration and provisioning from the Cloud Relay diagnostics tab when push notifications are not arriving
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:37.612Z
Learning: Ensure Obsidian app is installed and opened at least once before attempting to connect the Obsidian folder in VaultSync setup
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:37.612Z
Learning: Re-select the Obsidian folder through the Files picker when 'Reconnect to Obsidian' is needed to renew security-scoped bookmark access
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:37.612Z
Learning: Resolve all Sync Issues and ensure Obsidian folder access is connected before expecting background sync to function reliably
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-03T10:52:37.612Z
Learning: Use vaultsync-notify --doctor command to diagnose relay, Syncthing API, and APNs configuration issues during troubleshooting
📚 Learning: 2026-05-31T08:19:08.076Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Capture VaultSync version, iOS version, **Sync Issues** screenshot, **Relay Diagnostics** screenshot, and `vaultsync-notify --doctor` output when filing bug reports
Applied to files:
docs/troubleshooting.mddocs/architecture.md
📚 Learning: 2026-06-01T08:37:12.985Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: README.md:0-0
Timestamp: 2026-06-01T08:37:12.985Z
Learning: Include relevant logs, iOS/iPadOS versions, Syncthing version, and Cloud Relay status when reporting bugs
Applied to files:
docs/troubleshooting.mddocs/relay-spec.mddocs/architecture.md
📚 Learning: 2026-05-31T08:19:08.076Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Resolve all **Sync Issues** and reconnect Obsidian folder if access warnings appear before attempting background sync troubleshooting
Applied to files:
docs/troubleshooting.md
📚 Learning: 2026-05-31T08:19:08.076Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: For Cloud Relay users, validate `vaultsync-notify --doctor` output and relay health status to ensure background sync dependencies are configured correctly
Applied to files:
docs/troubleshooting.mddocs/relay-spec.mdnotify/README.md
📚 Learning: 2026-05-31T08:18:03.446Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/architecture.md:0-0
Timestamp: 2026-05-31T08:18:03.446Z
Learning: VaultSync background sync is intentionally asymmetric: Server→iPhone sync is accelerated via Cloud Relay push notifications (server-side file watch + APNs); iPhone→Server sync relies on foreground execution in the VaultSync app, with background refresh as opportunistic only—not guaranteed real-time.
Applied to files:
docs/troubleshooting.mddocs/relay-spec.mddocs/architecture.md
📚 Learning: 2026-05-31T08:18:03.446Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/architecture.md:0-0
Timestamp: 2026-05-31T08:18:03.446Z
Learning: Applies to docs/notify/** : Cloud Relay is an optional Docker sidecar service that watches Syncthing on the homeserver for outgoing changes and sends APNs silent-push wake-ups to the iPhone. Refer to relay-spec.md for protocol details and troubleshooting.md for end-user issues.
Applied to files:
docs/troubleshooting.mddocs/relay-spec.mddocs/architecture.mdnotify/README.md
📚 Learning: 2026-05-31T08:19:08.076Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Confirm both iOS device and homeserver have verified internet access before troubleshooting relay connectivity issues
Applied to files:
docs/troubleshooting.md
📚 Learning: 2026-05-31T08:19:08.076Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Install and open Obsidian at least once on the iOS device before attempting to connect the Obsidian folder in VaultSync
Applied to files:
docs/troubleshooting.md
📚 Learning: 2026-05-31T08:19:08.076Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Force-close VaultSync and reopen it when Syncthing is not running; keep the app in foreground for at least 20-30 seconds to allow the Syncthing bridge to start
Applied to files:
docs/troubleshooting.mddocs/architecture.md
📚 Learning: 2026-05-31T08:19:08.076Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Check homeserver egress rules (firewall, VPN, proxy) to ensure they do not block `relay.vaultsync.eu`
Applied to files:
docs/troubleshooting.md
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: The `vaultsync-notify` container must accept configuration via environment variables: `SYNCTHING_API_URL`, `SYNCTHING_API_KEY`, `RELAY_URL`, `DEBOUNCE_SECONDS`, and `WATCHED_FOLDERS`
Applied to files:
docs/relay-spec.mdnotify/README.md
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: The vaultsync-notify container must automatically read the Syncthing Device ID from the `/rest/system/status` endpoint at startup — no manual Device ID configuration required
Applied to files:
docs/relay-spec.mdnotify/README.md
📚 Learning: 2026-05-31T08:19:08.076Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Applies to docs/**/notify/.env : Verify `RELAY_URL` in `notify/.env` is correctly set (default cloud value: `https://relay.vaultsync.eu`)
Applied to files:
docs/relay-spec.mdnotify/README.md
📚 Learning: 2026-05-31T08:19:08.076Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/troubleshooting.md:0-0
Timestamp: 2026-05-31T08:19:08.076Z
Learning: Applies to docs/**/notify/.env : Ensure `SYNCTHING_API_KEY` in `notify/.env` matches the Syncthing GUI API key and restart the container after updating
Applied to files:
docs/relay-spec.mdnotify/README.md
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: The vaultsync-notify container must treat subscription-state errors (400/401/402/403) as recoverable and re-check on a slow cadence, but treat misconfiguration errors (404) as fatal and exit
Applied to files:
docs/relay-spec.md
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: Implement configurable debouncing (default: 5 seconds) in the vaultsync-notify container to batch rapid file-change signals into a single push notification
Applied to files:
docs/relay-spec.mdnotify/README.md
📚 Learning: 2026-05-31T14:09:03.884Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/app-store-connect-relay.md:0-0
Timestamp: 2026-05-31T14:09:03.884Z
Learning: Subscription disclosures (price/period, auto-renew until canceled, how to cancel, Terms/Privacy links) must be shown in Settings → Cloud Relay and the in-context upsell, satisfying App Store Guideline 3.1.2
Applied to files:
docs/relay-spec.md
📚 Learning: 2026-05-31T14:09:03.884Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/app-store-connect-relay.md:0-0
Timestamp: 2026-05-31T14:09:03.884Z
Learning: The Cloud Relay server-side helper setup must be framed as part of using the product in the in-app 'Set Up Your Server' step shown right after purchase, not as a gate to unlock paid content, to comply with App Store Guideline 3.1.2(a)
Applied to files:
docs/relay-spec.md
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: Cloud Relay subscription pricing must be read from StoreKit at runtime and never hard-coded; support monthly and yearly auto-renewable subscription options
Applied to files:
docs/relay-spec.md
📚 Learning: 2026-05-31T14:09:03.884Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/app-store-connect-relay.md:0-0
Timestamp: 2026-05-31T14:09:03.884Z
Learning: Applies to docs/**/*.storekit* : The app code and `ios/VaultSync.storekit` already expect Cloud Relay monthly product ID `eu.vaultsync.app.relay.monthly` and yearly product ID `eu.vaultsync.app.relay.yearly` in App Store Connect
Applied to files:
docs/relay-spec.mddocs/architecture.md
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: The iOS app must store provisioned Device IDs in the Keychain and re-provision on APNs token rotation; deprovision tokens on subscription expiry
Applied to files:
docs/relay-spec.md
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: The central relay must verify StoreKit signed transactions (JWS) offline against Apple's certificate chain for subscription expiry validation; accept legacy numeric transaction IDs for backward compatibility
Applied to files:
docs/relay-spec.md
📚 Learning: 2026-05-31T21:28:16.829Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/relay-spec.md:0-0
Timestamp: 2026-05-31T21:28:16.829Z
Learning: The iOS app must implement background push notification handling via `BGAppRefreshTask`, restoring vault bookmarks, starting Syncthing, polling for completion within the ~30s background budget, then stopping Syncthing
Applied to files:
docs/relay-spec.mddocs/architecture.md
📚 Learning: 2026-05-31T08:18:03.446Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/architecture.md:0-0
Timestamp: 2026-05-31T08:18:03.446Z
Learning: Applies to docs/go/bridge/**/*.go : Go bridge exports must follow the documented API contract: lifecycle management (StartSyncthing, StopSyncthing, IsRunning), device/folder management (AddDevice, RemoveDevice, AddFolder, RemoveFolder, etc.), status queries (GetFolderStatusJSON, GetConnectionsJSON, GetConfigJSON), conflict resolution (GetConflictFilesJSON, ResolveConflict, KeepBothConflict), folder ignores (GetFolderIgnores, SetFolderIgnores), and event polling (GetEventsSince).
Applied to files:
docs/architecture.md
📚 Learning: 2026-05-31T08:18:03.446Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/architecture.md:0-0
Timestamp: 2026-05-31T08:18:03.446Z
Learning: Applies to docs/ios/**/*.swift : QR code scanning and conflict diff visualization (QRScannerView, ConflictDiffView/LineDiffView) must be implemented on the iOS side in Swift, not via the Go bridge.
Applied to files:
docs/architecture.md
📚 Learning: 2026-05-31T08:18:03.446Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/architecture.md:0-0
Timestamp: 2026-05-31T08:18:03.446Z
Learning: Applies to docs/go/bridge/**/*.go : Go bridge exports must use only primitive types, `string`, and `[]byte` across the Swift ↔ Go boundary; complex data must be JSON-serialized. Read-only accessors returning JSON must be named with the `Get…JSON` prefix.
Applied to files:
docs/architecture.md
📚 Learning: 2026-05-31T08:18:03.446Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/architecture.md:0-0
Timestamp: 2026-05-31T08:18:03.446Z
Learning: Applies to docs/ios/**/*.swift : iOS app structure must follow the established directory layout: App/ for entry points, Models/ for data types, ViewModels/ for presentation logic, Views/ for SwiftUI components, Services/ for business logic (SyncBridgeService, SyncthingManager, BackgroundSyncService, RelayService, KeychainService, etc.), Resources/ for assets and theme, and *.lproj/ directories for localizations.
Applied to files:
docs/architecture.md
📚 Learning: 2026-05-31T08:18:03.446Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/architecture.md:0-0
Timestamp: 2026-05-31T08:18:03.446Z
Learning: Applies to docs/ios/**/*.swift : iOS background sync strategy: use `BGAppRefreshTask` (with ~15 min request interval) and `BGContinuedProcessingTask` (iOS 26+ for longer runtime). Implement a ~30s grace window after backgrounding to allow in-flight sync to complete.
Applied to files:
docs/architecture.md
📚 Learning: 2026-05-09T10:24:55.794Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt:0-0
Timestamp: 2026-05-09T10:24:55.794Z
Learning: Applies to **/{*bridge*,*service*}.{swift,go} : For Go bridge or Swift bridge-service changes, pass if gomobile-compatible types, JSON response shapes, empty-string success conventions, and corresponding tests remain compatible. Fail only when the PR breaks the documented Swift-Go bridge contract.
Applied to files:
docs/architecture.md
📚 Learning: 2026-05-31T08:18:58.296Z
Learnt from: CR
Repo: psimaker/vaultsync PR: 0
File: docs/sync-filters-ux.md:0-0
Timestamp: 2026-05-31T08:18:58.296Z
Learning: Applies to docs/**/*vaultDetailView* : Place the Sync Filters link on the vault detail screen between the Conflicts and Shared With sections in `vaultDetailView`
Applied to files:
docs/architecture.md
🪛 LanguageTool
notify/README.md
[style] ~40-~40: Consider using “who” when you are referring to a person instead of an object.
Context: ...rror? Add -u <uid>:<gid> for the user that owns config.xml. **B. Guided `bootst...
(THAT_WHO)
🪛 markdownlint-cli2 (0.22.1)
docs/architecture.md
[warning] 5-5: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
Rework the setup block so it can't be misread: a numbered "Turn it on" flow that states where to run the command, that the app shows the same command with a Copy button, and a single unmissable callout for the one edit (the config path). Drop the misleading "generates this for you" comment; the command stays byte-identical to the in-app one. Restore "the moment they happen" (matches in-app copy) without a quantified latency promise.
Untrack docs/app-store-connect-relay.md and docs/relay-activation-release-checklist.md and add them to .gitignore. They are internal process notes (release gates, App Store Connect pricing playbook) with no place in the public tree. Files stay on disk; only this branch's tip drops them.
What
Promotes the verified Cloud Relay activation in 1.5.1:
vaultsync-notifyfires a startup announce upon launch → activation self-confirms, zero user interaction required. iOS: reactivation card + honest 3-state (not-active / active / went-quiet). The app has no trigger sender (every silent push = real delivery).config.xml(RELAY_URLremains required, no prod default, key is never logged);docker compose up -dis key-free.docker run(no more "paste API key"); softened promptness copy; L10n lockstep en/de/es/zh-Hans.SYNCTHING_CONFIG_WAIT_SECONDS);bootstrap.sh<gui>address fix.Verified (local, mock relay only — never production)
syncthing/syncthingv2.1.0: Auto-detect → startup announce → 202;-u 1000reads /-u 99denied; key never logged.plutil -lintOK, identical key sets.aps-environmentper-config (Debug/Release) + TestFlight.Release 1.5.1 adds Cloud Relay self-activation plus a key-free server-helper onboarding path so server operators no longer need to copy Syncthing API keys.
User-visible behavior
Privacy & security
Background execution & reliability
Tests & verification
Other notes