Skip to content

feat: self-activating Cloud Relay + key-free server setup (1.5.1)#33

Merged
psimaker merged 22 commits into
mainfrom
feat/relay-activation
Jun 3, 2026
Merged

feat: self-activating Cloud Relay + key-free server setup (1.5.1)#33
psimaker merged 22 commits into
mainfrom
feat/relay-activation

Conversation

@psimaker

@psimaker psimaker commented Jun 3, 2026

Copy link
Copy Markdown
Owner

What

Promotes the verified Cloud Relay activation in 1.5.1:

  • Path Bvaultsync-notify fires 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).
  • A+C — the helper reads the Syncthing key + URL itself from config.xml (RELAY_URL remains required, no prod default, key is never logged); docker compose up -d is key-free.
  • §12 — key-free in-app docker run (no more "paste API key"); softened promptness copy; L10n lockstep en/de/es/zh-Hans.
  • Hardenings — bounded first-boot wait (SYNCTHING_CONFIG_WAIT_SECONDS); bootstrap.sh <gui> address fix.

Verified (local, mock relay only — never production)

  • notify gofmt/vet/build/test green (including new first-boot tests).
  • Bare binary + full Docker path against real syncthing/syncthing v2.1.0: Auto-detect → startup announce → 202; -u 1000 reads / -u 99 denied; key never logged.
  • App Debug+Release build; release binary audit clean (override=0, /trigger=0, prod URL present).
  • L10n: plutil -lint OK, identical key sets.

⚠️ Before App Store Release

  • Real-device testing of the receiving leg (simulator does not deliver silent pushes).
  • aps-environment per-config (Debug/Release) + TestFlight.
  • es / zh-Hans native string review.
  • NAS uid/path follow-ups (Unraid 99:100, Synology).

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

  • iOS: Devices auto-activate Cloud Relay after subscription when the server-helper issues a startup announce. The app exposes an explicit 3‑state relay status (not-active / active / went-quiet), a prominent “Finish activating Cloud Relay” reactivation card when reactivation is needed, and a one-time “Connected” celebration persisted until dismissed. The app no longer emits its own trigger — every silent push from the relay is treated as a real delivery and drives delivery-confirmation/recovery UI.
  • Server helper / Docker: vaultsync-notify can auto-detect Syncthing API URL and API key from a mounted config.xml if env vars are unset. Docker Compose and the documented docker run command support a key-free flow (read-only mount of Syncthing config, configurable PUID:PGID). RELAY_URL remains required and has no implicit production default.

Privacy & security

  • Syncthing API credentials may be read from local config.xml when needed; the API key is not logged. README/.env guidance documents permission and UID/GID concerns and warns about keeping explicit SYNCTHING_API_KEY values file-protected (0600).
  • bootstrap.sh will pin a user-specified SYNCTHING_CONFIG into the generated .env to avoid silent config mismatches.

Background execution & reliability

  • vaultsync-notify supports a STARTUP_ANNOUNCE (default true) that posts a short-timeout startup announce so newly subscribed devices become immediately active; startup announce is best-effort and only fatal on misconfiguration.
  • First-boot wait for config.xml is bounded by SYNCTHING_CONFIG_WAIT_SECONDS to avoid indefinite hangs; loadConfigAwaitingSyncthing waits/retries appropriately.
  • Cold-launch persistence of provisioned device IDs ensures UI reflects last known provisioned state immediately.
  • Hardenings include improved Syncthing config discovery and parsing, guarded GUI address extraction, read-only syncthing mount handling, and .env/source fixes in bootstrap.sh.

Tests & verification

  • New unit tests cover Syncthing config parsing, candidate ordering, env-vs-config precedence, first-boot/race handling, permission-denied cases, and startup-announce behaviors. First-boot tests and run-service tests added/expanded. Local verification reported gofmt/vet/build/test green (including new tests). go toolchain bumped to go 1.26.4.
  • Remaining verification before App Store: real-device silent-push testing (APNs silent delivery), per-config aps-environment/TestFlight checks, native-string review for es/zh-Hans, and platform/NAS UID/path follow-ups.

Other notes

  • UX and docs updated: in-app copy softened, localized activation strings added for en/de/es/zh-Hans (plutil lint parity), README/notify README/docker-compose/docs rewritten to lead with key-free flows and document startup-announce/first-boot semantics.

psimaker added 4 commits June 2, 2026 19:19
…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).
@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

VaultSync 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.

Changes

Cloud Relay auto-activation (v1.5.1)

Layer / File(s) Summary
Syncthing config auto-detection foundation
notify/syncthing_config.go, notify/syncthing_config_test.go, notify/scripts/bootstrap.sh
New detection probes candidate config.xml paths, parses GUI <address>/tls/API key with a 16 MiB bound, returns permission-aware errors, infers REST base URL, and includes tests for parsing, candidate order, unreadable candidates, and error messages.
Server-helper config loading and startup announce
notify/main.go, notify/main_test.go
Config loading now can wait for config.xml, treats SYNCTHING_* as auto-detectable, parses STARTUP_ANNOUNCE (default true), and sends a best-effort startup-announce on startup; tests cover precedence, first-boot race handling, and announce outcomes.
Helper deployment and bootstrap
notify/docker-compose.yml, notify/.env.example, notify/README.md, notify/scripts/bootstrap.sh, .gitignore, notify/go.mod, go/go.mod, README.md
Docker Compose adds configurable UID/GID and read-only Syncthing mount, makes SYNCTHING_API_KEY optional, adds SYNCTHING_CONFIG and STARTUP_ANNOUNCE defaults, bootstrap.sh probes additional candidate paths and writes conditional .env entries, built binary /notify/notify is ignored, and Go toolchain directives bumped to 1.26.4.
iOS relay debug plumbing and semantics
ios/VaultSync/Services/RelayService.swift, ios/VaultSync/App/VaultSyncApp.swift, ios/VaultSync/App/AppDelegate.swift, ios/VaultSync/Models/RelayProvisionStatus.swift, ios/project.yml
RelayService adds productionRelayURL and a DEBUG-only relayURL override via UserDefaults plus isUsingRelayOverride; VaultSyncApp logs the active relay URL in DEBUG and supports a DEBUG-only vaultsync://relay-wake deep link to mark a trigger; comments document wake-up semantics and ATS posture.
Subscription reactivation logic and persistence
ios/VaultSync/Services/SubscriptionManager.swift
SubscriptionManager records original purchase date, exposes needsRelayReactivation gated by a grace period (DEBUG-tunable), rehydrates persisted provisioned device IDs on init, persists changes, and updates deprovision/provision ordering.
Multi-state activation UI and server-setup
ios/VaultSync/Views/ContentView.swift, ios/VaultSync/Views/RelayHomeView.swift, ios/VaultSync/Views/RelayDiagnosticsView.swift, ios/VaultSync/Views/RelayServerSetupView.swift
Dashboard and home views show reactivation card, recovery row, first-delivery celebration, steady/post-delivery quiet, and not-yet-delivered states; diagnostics add override messaging and a DEBUG simulate-wake button; server-setup command mounts config.xml and uses RelayService.productionRelayURL.
Localizations, docs, and release metadata
ios/VaultSync/*/Localizable.strings, CHANGELOG.md, docs/relay-activation-release-checklist.md
Adds Cloud Relay activation/setup localized strings across multiple languages, updates README/docs and release checklist for v1.5.1 describing verification steps, and documents the new key-free setup flow in CHANGELOG.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

"No more copying keys in haste,
Config.xml wakes the helper's grace.
A startup ping — the relay sings,
The app celebrates the first small thing.
Docs, tests, and debug lights align."

🚥 Pre-merge checks | ✅ 7
✅ Passed checks (7 passed)
Check name Status Explanation
Title check ✅ Passed The PR title follows conventional-commit style with 'feat:' prefix and clearly summarizes the main changes: self-activating Cloud Relay and key-free server setup for v1.5.1.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
No Private Note Leakage ✅ Passed Code does not leak private notes, API keys, APNs tokens, receipts, or vault paths. API key explicitly never logged; device IDs truncated; .env stored at mode 600.
Bounded Ios Background Work ✅ Passed All iOS background execution properly bounded: completionHandler always called, deadlines enforced, expirationHandlers set, relay provisioning guarded, errors safe without private leaks.
Bridge Contract Compatibility ✅ Passed PR does not modify go/bridge files; bridge API signatures, JSON response shapes, and success conventions remain intact and compatible.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/relay-activation

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@psimaker psimaker self-assigned this Jun 3, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Persist the cleared provision state in deprovisionRelay().

Transaction.updates can 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 rewrites relay-provisioned-device-ids. A later cold launch can therefore rehydrate stale .provisioned entries 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 value

Minor: RELAY_URL check after failed loadConfig is slightly redundant.

Line 253 checks strings.TrimSpace(os.Getenv("RELAY_URL")) == "" again after loadConfig() 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., via errors.Is against 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

📥 Commits

Reviewing files that changed from the base of the PR and between 8cb5fb8 and 1b864ec.

📒 Files selected for processing (25)
  • .gitignore
  • CHANGELOG.md
  • docs/relay-activation-release-checklist.md
  • ios/VaultSync/App/AppDelegate.swift
  • ios/VaultSync/App/VaultSyncApp.swift
  • ios/VaultSync/Models/RelayProvisionStatus.swift
  • ios/VaultSync/Services/RelayService.swift
  • ios/VaultSync/Services/SubscriptionManager.swift
  • ios/VaultSync/Views/ContentView.swift
  • ios/VaultSync/Views/RelayDiagnosticsView.swift
  • ios/VaultSync/Views/RelayHomeView.swift
  • ios/VaultSync/Views/RelayServerSetupView.swift
  • 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
  • ios/project.yml
  • notify/.env.example
  • notify/README.md
  • notify/docker-compose.yml
  • notify/main.go
  • notify/main_test.go
  • notify/scripts/bootstrap.sh
  • notify/syncthing_config.go
  • notify/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.yml
  • ios/VaultSync/App/VaultSyncApp.swift
  • notify/README.md
  • docs/relay-activation-release-checklist.md
  • ios/VaultSync/App/AppDelegate.swift
  • notify/docker-compose.yml
  • notify/scripts/bootstrap.sh
  • ios/VaultSync/Models/RelayProvisionStatus.swift
  • ios/VaultSync/Services/RelayService.swift
  • ios/VaultSync/Views/RelayDiagnosticsView.swift
  • CHANGELOG.md
  • notify/syncthing_config.go
  • ios/VaultSync/Views/ContentView.swift
  • ios/VaultSync/Views/RelayHomeView.swift
  • notify/main.go
  • ios/VaultSync/en.lproj/Localizable.strings
  • ios/VaultSync/Views/RelayServerSetupView.swift
  • ios/VaultSync/es.lproj/Localizable.strings
  • ios/VaultSync/zh-Hans.lproj/Localizable.strings
  • notify/syncthing_config_test.go
  • ios/VaultSync/de.lproj/Localizable.strings
  • ios/VaultSync/Services/SubscriptionManager.swift
  • notify/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.swift
  • ios/VaultSync/App/AppDelegate.swift
  • ios/VaultSync/Models/RelayProvisionStatus.swift
  • ios/VaultSync/Services/RelayService.swift
  • ios/VaultSync/Views/RelayDiagnosticsView.swift
  • ios/VaultSync/Views/ContentView.swift
  • ios/VaultSync/Views/RelayHomeView.swift
  • ios/VaultSync/Views/RelayServerSetupView.swift
  • ios/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.swift
  • ios/VaultSync/App/AppDelegate.swift
  • ios/VaultSync/Models/RelayProvisionStatus.swift
  • ios/VaultSync/Services/RelayService.swift
  • ios/VaultSync/Views/RelayDiagnosticsView.swift
  • ios/VaultSync/Views/ContentView.swift
  • ios/VaultSync/Views/RelayHomeView.swift
  • ios/VaultSync/Views/RelayServerSetupView.swift
  • ios/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.swift
  • ios/VaultSync/App/AppDelegate.swift
  • ios/VaultSync/Models/RelayProvisionStatus.swift
  • ios/VaultSync/Services/RelayService.swift
  • ios/VaultSync/Views/RelayDiagnosticsView.swift
  • ios/VaultSync/Views/ContentView.swift
  • ios/VaultSync/Views/RelayHomeView.swift
  • ios/VaultSync/Views/RelayServerSetupView.swift
  • ios/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.md
  • docs/relay-activation-release-checklist.md
  • CHANGELOG.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.go
  • notify/main.go
  • notify/syncthing_config_test.go
  • notify/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.yml
  • ios/VaultSync/App/VaultSyncApp.swift
  • ios/VaultSync/Views/RelayDiagnosticsView.swift
  • CHANGELOG.md
  • ios/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.yml
  • ios/VaultSync/App/VaultSyncApp.swift
  • docs/relay-activation-release-checklist.md
  • ios/VaultSync/Models/RelayProvisionStatus.swift
  • ios/VaultSync/Services/RelayService.swift
  • CHANGELOG.md
  • ios/VaultSync/en.lproj/Localizable.strings
  • ios/VaultSync/Views/RelayServerSetupView.swift
  • ios/VaultSync/es.lproj/Localizable.strings
  • ios/VaultSync/zh-Hans.lproj/Localizable.strings
  • ios/VaultSync/de.lproj/Localizable.strings
  • ios/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.yml
  • ios/VaultSync/App/VaultSyncApp.swift
  • ios/VaultSync/App/AppDelegate.swift
  • ios/VaultSync/Models/RelayProvisionStatus.swift
  • CHANGELOG.md
  • ios/VaultSync/en.lproj/Localizable.strings
  • ios/VaultSync/Views/RelayServerSetupView.swift
  • ios/VaultSync/es.lproj/Localizable.strings
  • ios/VaultSync/zh-Hans.lproj/Localizable.strings
  • ios/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.swift
  • notify/README.md
  • docs/relay-activation-release-checklist.md
  • notify/docker-compose.yml
  • CHANGELOG.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.swift
  • 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 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.swift
  • ios/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.md
  • notify/docker-compose.yml
  • notify/.env.example
  • notify/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.md
  • docs/relay-activation-release-checklist.md
  • notify/docker-compose.yml
  • ios/VaultSync/Services/RelayService.swift
  • CHANGELOG.md
  • notify/.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.md
  • notify/docker-compose.yml
  • notify/.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.md
  • docs/relay-activation-release-checklist.md
  • ios/VaultSync/App/AppDelegate.swift
  • notify/docker-compose.yml
  • ios/VaultSync/Models/RelayProvisionStatus.swift
  • ios/VaultSync/Services/RelayService.swift
  • CHANGELOG.md
  • notify/.env.example
  • notify/main.go
  • ios/VaultSync/en.lproj/Localizable.strings
  • ios/VaultSync/Views/RelayServerSetupView.swift
  • ios/VaultSync/es.lproj/Localizable.strings
  • ios/VaultSync/zh-Hans.lproj/Localizable.strings
  • ios/VaultSync/de.lproj/Localizable.strings
  • 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 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.md
  • 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: 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.md
  • 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/README.md
  • notify/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.md
  • ios/VaultSync/en.lproj/Localizable.strings
  • ios/VaultSync/Views/RelayServerSetupView.swift
  • ios/VaultSync/es.lproj/Localizable.strings
  • ios/VaultSync/zh-Hans.lproj/Localizable.strings
  • ios/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.swift
  • ios/VaultSync/en.lproj/Localizable.strings
  • ios/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.strings
  • ios/VaultSync/es.lproj/Localizable.strings
  • ios/VaultSync/zh-Hans.lproj/Localizable.strings
  • ios/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: false with 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 RelayServerSetupView strings (key-free command, uid hint, 'It activates itself') before release"

Comment thread ios/project.yml Outdated
Comment thread ios/VaultSync/de.lproj/Localizable.strings
Comment thread ios/VaultSync/zh-Hans.lproj/Localizable.strings
psimaker and others added 3 commits June 3, 2026 08:21
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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Bootstrap probe list has drifted from the runtime detector.

This list skips the XDG_STATE_HOME / XDG_CONFIG_HOME locations that notify/syncthing_config.go checks first. On hosts with non-default XDG paths, bootstrap falls back to manual setup even though the helper itself can auto-detect the same config.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

📥 Commits

Reviewing files that changed from the base of the PR and between 1b864ec and 421ee2e.

📒 Files selected for processing (13)
  • CHANGELOG.md
  • go/go.mod
  • ios/VaultSync/Services/SubscriptionManager.swift
  • 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
  • ios/project.yml
  • notify/.env.example
  • notify/README.md
  • notify/docker-compose.yml
  • notify/go.mod
  • notify/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.mod
  • ios/project.yml
  • notify/scripts/bootstrap.sh
  • notify/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.sh
  • notify/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.sh
  • notify/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.sh
  • notify/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.sh
  • notify/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.sh
  • 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: 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 lift

Make aps-environment build-configuration specific (Debug=development, Release=production).

ios/project.yml (lines 95-99) sets the entitlements aps-environment to development. Ensure Release builds use production; 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_ENTITLEMENTS set per build configuration).

Comment thread notify/scripts/bootstrap.sh
psimaker added 4 commits June 3, 2026 10:17
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".

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 421ee2e and abbe9d6.

📒 Files selected for processing (6)
  • README.md
  • ios/VaultSync/de.lproj/Localizable.strings
  • ios/VaultSync/es.lproj/Localizable.strings
  • ios/VaultSync/zh-Hans.lproj/Localizable.strings
  • notify/main_test.go
  • notify/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

Comment thread README.md Outdated
psimaker added 9 commits June 3, 2026 11:21
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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Fix 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

📥 Commits

Reviewing files that changed from the base of the PR and between abbe9d6 and bed5c0c.

📒 Files selected for processing (14)
  • README.md
  • docs/architecture.md
  • docs/relay-activation-release-checklist.md
  • docs/relay-spec.md
  • docs/setup.md
  • docs/sync-filters-ux.md
  • docs/troubleshooting.md
  • ios/VaultSync/Views/RelayServerSetupView.swift
  • 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
  • notify/README.md
  • notify/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.md
  • docs/relay-spec.md
  • README.md
  • ios/VaultSync/es.lproj/Localizable.strings
  • docs/architecture.md
  • notify/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.md
  • docs/relay-spec.md
  • README.md
  • docs/architecture.md
  • notify/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, topic eu.vaultsync.app, priority 5 (low), and expiration +1 hour

Use 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.md
  • docs/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.md
  • docs/relay-spec.md
  • docs/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.md
  • docs/relay-spec.md
  • notify/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.md
  • docs/relay-spec.md
  • 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/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.md
  • docs/relay-spec.md
  • docs/architecture.md
  • notify/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.md
  • docs/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.md
  • notify/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.md
  • notify/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.md
  • notify/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.md
  • notify/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.md
  • notify/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.md
  • docs/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.md
  • 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 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)

psimaker added 2 commits June 3, 2026 13:16
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.
@psimaker psimaker merged commit 6c41df3 into main Jun 3, 2026
13 checks passed
@psimaker psimaker deleted the feat/relay-activation branch June 3, 2026 11:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant