feat: one-line Cloud Relay installer, prebuilt notify binaries, exact uid:gid permission hint#39
Conversation
scripts/install.sh turns server setup into a single constant command (curl -fsSL https://vaultsync.eu/notify.sh | sh): it probes config.xml in the same order as the helper binary, reads the file owner so the helper always runs as that uid:gid (the #1 setup failure), and starts the helper via Docker — or, without Docker, installs a prebuilt release binary behind a systemd service (Linux) or launchd agent (macOS), verified against the release SHA256SUMS. Every path ends with the --doctor preflight; --dry-run previews all actions without changing anything. Nothing in the command is user-specific, so it is stable and short enough to type from the phone screen. The helper's permission error now names config.xml's actual owner and the exact -u uid:gid flag (falling back to the directory owner when even stat on the file is denied) instead of the generic uid-1000 hint.
Cross-compiles vaultsync-notify for linux/amd64, linux/arm64, darwin/amd64, darwin/arm64, and windows/amd64 (pure CGO_ENABLED=0 Go, no QEMU or goreleaser needed), generates SHA256SUMS, and attaches everything to a GitHub release for the tag (--latest=false so app releases keep the latest slot). install.sh resolves these assets for the no-Docker path; previously that path required a Go toolchain.
The setup screen now shows the constant installer command as Step 1 — it has no user-specific values, so there is nothing to edit and it is short enough to type into a server shell straight from the screen. The docker run snippet (with its /PATH/TO/syncthing placeholder and -u footgun) moves to a clearly-labeled manual alternative section. The footer explains what the installer does and points skeptics at --dry-run. Localized in en/de/es/zh-Hans.
- Guard $HOME for set -u (HOME-less contexts like containers/cron) - Verify the helper container stays running after start instead of reporting success on a restart loop; surface its log on failure - 1.6.0 has not shipped to the App Store yet, so the installer work rides in the existing 1.6.0 changelog section instead of Unreleased
fail() inside the find_syncthing_config command substitution exits only the subshell, so the caller's generic not-found error printed on top of the specific one. Validate the explicit path in the main flow instead. Verified end-to-end on Linux (docker:dind, BusyBox sh): full install against a real Syncthing and the production relay (doctor passes with an unprovisioned device ID), idempotent re-run, owner-follow after chown, clean failure paths; dash and pipe-mode (curl|sh) checked; the checksum pipeline matches the release workflow output format.
|
Warning Review limit reached
More reviews will be available in 42 minutes and 27 seconds. Learn how PR review limits work. To continue reviewing without waiting, purchase usage credits in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThis PR consolidates VaultSync's Cloud Relay setup into a single one-liner installer. It introduces file-owner detection to show users exact permission fixes, an intelligent bash installer that deploys via Docker or systemd/launchd, a CI job to publish prebuilt binaries, and updated iOS UI and docs reflecting the simplified flow. ChangesOne-line installer, prebuilt binaries, and permission guidance
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes The installer script is substantial and densely packed (449 lines) with multi-platform service lifecycle logic (systemd, launchd), GitHub API interaction, asset verification, and privilege escalation. The Go owner-detection layer is smaller but security-sensitive. iOS UI changes are moderate in scope. Localization and docs are repetitive. The heterogeneity of shell, Go, Swift, YAML, and Markdown requires separate reasoning per file. Poem
🚥 Pre-merge checks | ✅ 7✅ Passed checks (7 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 @.github/workflows/docker.yml:
- Around line 98-103: The release-binaries job exposes unnecessary write
credentials and uses floating action refs; update the job (referencing
release-binaries and its permissions: contents: write) to restrict permissions
(e.g., contents: read instead of write) and pin external actions (replace
actions/checkout@v6 and actions/setup-go@v6 with specific commit SHAs) and for
the checkout step set persist-credentials: false to avoid leaving tokens in the
workspace; ensure you pin both actions to immutable SHAs and adjust the checkout
step options accordingly.
In `@notify/README.md`:
- Around line 21-23: The example sets SYNCTHING_CONFIG on curl instead of the
installer; change it so the env var is applied to the shell that runs notify.sh.
Replace the pipeline so the env is set for sh (e.g. use curl -fsSL
https://vaultsync.eu/notify.sh | SYNCTHING_CONFIG=/path/to/config.xml sh -s --
or export SYNCTHING_CONFIG=/path/to/config.xml before piping), updating the
README line that currently shows `SYNCTHING_CONFIG=/path/to/config.xml curl
-fsSL https://vaultsync.eu/notify.sh | sh` to the corrected form.
🪄 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: 5e9c2bc6-9d0e-4ef3-98ca-f29d499dc79d
📒 Files selected for processing (14)
.github/workflows/docker.ymlCHANGELOG.mddocs/relay-spec.mdios/VaultSync/Views/RelayServerSetupView.swiftios/VaultSync/de.lproj/Localizable.stringsios/VaultSync/en.lproj/Localizable.stringsios/VaultSync/es.lproj/Localizable.stringsios/VaultSync/zh-Hans.lproj/Localizable.stringsnotify/README.mdnotify/owner_other.gonotify/owner_unix.gonotify/scripts/install.shnotify/syncthing_config.gonotify/syncthing_config_test.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (8)
**/*
⚙️ 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:
notify/owner_other.goCHANGELOG.mddocs/relay-spec.mdios/VaultSync/de.lproj/Localizable.stringsios/VaultSync/zh-Hans.lproj/Localizable.stringsnotify/syncthing_config.gonotify/owner_unix.goios/VaultSync/es.lproj/Localizable.stringsios/VaultSync/en.lproj/Localizable.stringsnotify/README.mdnotify/syncthing_config_test.goios/VaultSync/Views/RelayServerSetupView.swiftnotify/scripts/install.sh
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/owner_other.gonotify/syncthing_config.gonotify/owner_unix.gonotify/syncthing_config_test.go
**
⚙️ CodeRabbit configuration file
**:![]()
VaultSync
Self-hosted Obsidian vault sync for iPhone and iPad.
Your notes sync peer-to-peer over Syncthing, straight into Obsidian's iOS sandbox — no note cloud, no account, no tracking.![]()
![]()
![]()
🔭 Why VaultSync
- Peer-to-peer & private — syncs directly between your own devices over Syncthing. No note cloud, no account, no tracking.
- Lands in Obsidian — files sync into Obsidian's iOS sandbox, where the app already looks for them.
- Pair by QR, resolve conflicts — connect your server in seconds; settle Markdown conflicts with side-by-side diffs.
- Server changes wake your iPhone — optional Cloud Relay nudges the app the moment your server updates, so incoming notes land eve...
Files:
notify/owner_other.goCHANGELOG.mddocs/relay-spec.mdios/VaultSync/de.lproj/Localizable.stringsios/VaultSync/zh-Hans.lproj/Localizable.stringsnotify/syncthing_config.gonotify/owner_unix.goios/VaultSync/es.lproj/Localizable.stringsios/VaultSync/en.lproj/Localizable.stringsnotify/README.mdnotify/syncthing_config_test.goios/VaultSync/Views/RelayServerSetupView.swiftnotify/scripts/install.sh
**/*.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:
CHANGELOG.mddocs/relay-spec.mdnotify/README.md
docs/**
⚙️ CodeRabbit configuration file
docs/**: # ArchitectureVaultSync embeds Syncthing's Go reference implementation as an iOS library via gomobile — no reimplementation of the protocol in Swift, and guaranteed wire compatibility.
┌─────────────────────────────────┐ │ SwiftUI Frontend │ iOS-native UI, Swift 6 ├─────────────────────────────────┤ │ Swift ↔ Go Bridge │ thin API via gomobile │ │ → exported as .xcframework ├─────────────────────────────────┤ │ syncthing/lib (Go) │ protocol, discovery, sync └─────────────────────────────────┘ ↕ filesystem ┌─────────────────────────────────┐ │ Obsidian Vault (direct) │ Obsidian's iOS sandbox └─────────────────────────────────┘🔄 Sync strategy
- Foreground — Syncthing runs unrestricted: immediate, continuous sync.
- Background —
BGAppRefreshTask(requested ~15 min out; iOS decides the actual timing) +BGProcessingTask(overnight catch-up: multi-minute budget while charging with network) +BGContinuedProcessingTask(iOS 26+, longer runtime for user-initiated tasks). A ~30s grace window after backgrounding lets in-flight work finish.- Push (Cloud Relay) — optional. Near-realtime
server → iPhonewake-ups via APNs silent push. See relay-spec.md.VaultSync is intentionally asymmetric:
Direction Path Server → iPhone vaultsync-notifyspots outgoing changes → Cloud Relay silent push → VaultSync wakes and pulls.iPhone → Server iOS doesn't guarantee timely background execution for local edits. The reliable path is to open VaultSync and let embedded Syncthing run in the foreground — a Shortcuts automation can do that automatically whenever you leave Obsidian. Cloud Relay is a
server → iPhoneacceleration path, not a guarantee of symmetric real-time background sync.🌉 Go bridge (
go/bridge/)Minimal API exported via gomobile. Only prim...
Files:
docs/relay-spec.md
.github/workflows/**/*.yml
⚙️ CodeRabbit configuration file
.github/workflows/**/*.yml: Review CI for correct Go test tags, Xcode/iOS simulator assumptions, secret scoping,
dependency integrity, permissions, Docker publishing safety, and unnecessary privilege escalation.
Files:
.github/workflows/docker.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.
**/*.swift: Swift source code must use Swift 6 language version
Use SwiftUI for all UI implementation
Background sync must use BGAppRefreshTask and BGContinuedProcessingTask (iOS 26+ when available)
Push notifications must integrate with APNs silent push via Cloud Relay
App must support VoiceOver and Dynamic Type throughout the entire UI
Files:
ios/VaultSync/Views/RelayServerSetupView.swift
ios/**/*.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/Views/RelayServerSetupView.swift
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T21:32:03.294Z
Learning: vaultsync-notify must be stateless except for configuration; no persistent storage or state files required
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T21:32:03.294Z
Learning: Central relay must be horizontally scalable with stateless request handling and shared database for token storage
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T21:32:03.294Z
Learning: Syncthing Device ID is the sole identity mechanism; no user accounts, API keys, or Bearer tokens are used for authentication
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T21:32:03.294Z
Learning: No file content, folder names, file names, file sizes, or metadata should ever be transmitted between homeserver and central relay
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T21:32:03.294Z
Learning: APNs push delivery is asynchronous; trigger endpoint must return 202 Accepted immediately without waiting for push confirmation
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-06-10T21:32:03.294Z
Learning: Relay health endpoint validates reachability only; end-to-end delivery confirmation comes only from actual received trigger signals
📚 Learning: 2026-06-10T18:47:10.724Z
Learnt from: psimaker
Repo: psimaker/vaultsync PR: 38
File: ios/VaultSync/Views/ContentView.swift:605-611
Timestamp: 2026-06-10T18:47:10.724Z
Learning: In the SwiftUI codebase under ios/VaultSync, do not flag missing localization for SwiftUI string literals used as Text("…") or DisclosureGroup("…") titles/labels. In SwiftUI, these string literals are treated as LocalizedStringKey and resolve via the app’s Localizable.strings automatically—so they only need attention if the corresponding key is actually missing. Only require an explicit localization helper (e.g., L10n.tr(…)) when the string is not being passed through SwiftUI’s LocalizedStringKey path (e.g., plain String values provided to non-SwiftUI APIs).
Applied to files:
ios/VaultSync/Views/RelayServerSetupView.swift
🪛 LanguageTool
CHANGELOG.md
[style] ~11-~11: Consider using “who” when you are referring to a person instead of an object.
Context: ...wn, runs the helper as exactly the user that owns it (the #1 setup failure), starts ...
(THAT_WHO)
[uncategorized] ~12-~12: Did you mean the proper noun “Apple Silicon”?
Context: ...ux/amd64, linux/arm64, macOS (Intel and Apple silicon), and Windows, with a SHA256SUMS file...
(APPLE_PRODUCTS)
notify/README.md
[style] ~22-~22: The adverb ‘usually’ usually goes after the verb ‘is’.
Context: ... in a non-standard place (Synology/QNAP usually is)? `SYNCTHING_CONFIG=/path/to/config.xml...
(ADVERB_WORD_ORDER)
🪛 zizmor (1.25.2)
.github/workflows/docker.yml
[warning] 98-98: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 98-98: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 100-100: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 100-100: runtime artifacts potentially vulnerable to a cache poisoning attack (cache-poisoning): enables caching by default
(cache-poisoning)
🔇 Additional comments (6)
ios/VaultSync/Views/RelayServerSetupView.swift (1)
13-23: LGTM!Also applies to: 61-67, 77-90, 96-123
ios/VaultSync/de.lproj/Localizable.strings (1)
528-535: LGTM!ios/VaultSync/en.lproj/Localizable.strings (1)
528-535: LGTM!ios/VaultSync/es.lproj/Localizable.strings (1)
528-535: LGTM!ios/VaultSync/zh-Hans.lproj/Localizable.strings (1)
528-535: LGTM!CHANGELOG.md (1)
11-13: LGTM!
- README/installer error message: the SYNCTHING_CONFIG override must prefix sh (the installer), not curl — in a pipeline the assignment only applied to curl, so the installer never saw it. Both examples now show the correct form and say so explicitly. - release-binaries job: persist-credentials: false on checkout (the job holds a contents:write token) and cache: false on setup-go so release binaries are never assembled from cacheable state another workflow run could have written. Not taken: pinning actions to commit SHAs — the repo convention is floating major tags managed by Dependabot across all workflows; switching pinning strategy is a repo-wide decision, not a PR-local one.


Summary
curl -fsSL https://vaultsync.eu/notify.sh | sh(notify/scripts/install.sh) — finds config.xml, runs the helper as the uid:gid owning it, picks Docker or a prebuilt-binary service (systemd/launchd), ends with the --doctor preflight.--dry-runpreviews all actions.-u uid:gidflag (falls back to the directory owner).Verified end-to-end on Linux (docker:dind, BusyBox sh) against a real Syncthing and the production relay: doctor passes with an unprovisioned device ID, idempotent re-run, owner-follow after chown, clean failure paths; dash and curl|sh pipe mode checked; checksum pipeline matches the release workflow format.
Rollout notes
vaultsync.eu/notify.shto exist (redirect to raw.githubusercontent or static copy) before the App Store release.Cloud Relay One-Line Installer with Prebuilt Binaries and Exact Permission Hints
This PR introduces a one-line installer for the VaultSync Cloud Relay notification helper (
curl -fsSL https://vaultsync.eu/notify.sh | sh), along with cross-platform prebuilt binaries, improved permission error messages, and updated server setup UI.Server Deployment
The new
notify/scripts/install.shinstaller automates helper deployment with the following behavior:uid:gid--doctorat the end; supports--dry-runto preview actions without installationCI now publishes prebuilt static helper binaries for Linux/amd64, Linux/arm64, macOS/amd64, macOS/arm64, and Windows/amd64 as GitHub release assets on
notify-v*tag pushes, along withSHA256SUMSfor verification. The binary path fails gracefully until the first tag is published; Docker path works immediately.Permission Error Improvements
When permission is denied accessing
config.xml, the helper now reports:-u uid:gidflag to use in Docker to fix accessThis eliminates guesswork for operators running the helper in restricted environments. Tests verify both the file-owner and directory-owner fallback paths.
iOS App Server Setup UI
The RelayServerSetupView now leads with the one-liner installer command, provides the Docker alternative in a collapsible "Manual alternative" section, and includes helper functions for command copying with success feedback. Updated for English, German, Spanish, and Simplified Chinese.
Documentation
Updated
notify/README.mdwith "Prebuilt binaries" section and clarified installer preflight behavior, Docker Compose production-use warning, and--dry-runpreview flag. The relay specification now describes both Docker and binary deployment paths.User-Visible Behavior
--dry-runfor testing