Skip to content

feat: add foreground upload-only diagnostics - #126

Merged
psimaker merged 4 commits into
mainfrom
agent/app-foreground-upload-only
Jul 15, 2026
Merged

feat: add foreground upload-only diagnostics#126
psimaker merged 4 commits into
mainfrom
agent/app-foreground-upload-only

Conversation

@psimaker

@psimaker psimaker commented Jul 15, 2026

Copy link
Copy Markdown
Owner

What & why

Implement the owner-authorized M5 foreground upload-only leg of Decision 024 against the published helper 2.0.2 runtime. One explicit user tap and localized confirmation may create one exact request; only the exact signed response from the pinned paired helper can set upload observed. Controlled download and roundtrip remain unset.

The signed owner-device suite was not executed — owner-approved physical-device waiver (2026-07-15). It is replaced by fresh exact-head simulator and isolated Syncthing substitute evidence listed under Testing; no real-device, hardware-keychain, real-APNs, background-wake, or TestFlight-hardware behavior is claimed.

Evidence boundary

  • Upload: production app source accepts only the exact active type-5 helper attestation after full tuple, namespace, request, query, signature, digest, TTL, engine, folder, peer, and pinned-transport rechecks.
  • Download: unset; no response authorization, response baseline, response-file acceptance, or exact fresh ItemFinished.
  • Roundtrip: unset; no same-chain controlled download.
  • Cleanup: evidence-orthogonal; no app cleanup workflow is added here.
  • Decision 024 remains unchanged at blob f41f597d3ceca73da102e5e447382dfae07d2e08.

Component(s)

  • go (bridge / Syncthing)
  • ios (app / widget)
  • notify (contract-isolation test only; no runtime or wire change)
  • docs / CI

Testing

  • cd go && make patch && go test -tags noassets ./bridge -count=1
  • cd notify && go test ./... -count=1 on macOS
  • Complete Notify suite in the digest-pinned read-only Linux container
  • Two isolated ephemeral Syncthing instances prove exact request propagation and signed helper attestation (re-run fresh in the isolated no-network container)
  • Focused production Swift suites: 12 passed, zero failed/skipped (re-run fresh at the evidence head)
  • Complete iOS plan: 429 tests / 436 parameterized runs passed, zero failed/skipped (re-run fresh at the evidence head)
  • Release-configuration iOS Simulator build
  • Design-token lint, strings parity (881 keys), privacy lint, plist lint, Go vet, and notify publication-safety policy
  • Signed owner-device focused suite: not executed — owner-approved physical-device waiver; substitute evidence is the fresh exact-head simulator plan, focused suites, Release-configuration simulator build, and isolated two-instance Syncthing E2E above

Compatibility and rollback

Existing-user upgrade, app launch, Settings inspection, Relay/APNs activity, and ordinary/background sync create no key, pairing, trust, namespace, peer, share, artifact, rescan, or configuration change. Old or downgraded helpers yield capability unavailable without fallback. App/helper rollback preserves credentials, namespace authorization, opaque copies, backups, versions, conflicts, history, tombstones, mappings, and user data; forward recovery starts with a fresh capability and never resumes an old proof.

Security and privacy

The operation is foreground-only, explicit, one target, one designated peer, and sendreceive. It uses the real Syncthing ignore matcher, descriptor-relative O_NOFOLLOW access, exclusive request creation, byte-identical bounded polling, exact run tokens, rate/concurrency limits, and terminal late-response rejection. It performs no discovery, trust adoption, namespace creation, Relay call, APNs call, StoreKit call, logging, telemetry, crash annotation, durable proof storage, or global success derivation.

psimaker added 2 commits July 15, 2026 02:47
Add the explicit upload-only D024 app runtime with exact target,
namespace, filesystem, polling, rate, and lifecycle gates.

Bind product tests to cross-language vectors, terminal-state races,
real Syncthing preflight, and upload-only evidence.
Describe explicit consent, retention, compatibility, rollback, and
the separate upload, download, and roundtrip evidence states.

Record local simulator and isolated Syncthing evidence while keeping
the signed owner-device gate explicitly pending.
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Caution

Review failed

An error occurred during the review process. Please try again later.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/app-foreground-upload-only

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.

Replace the pending signed owner-device merge gate with the explicit
owner-approved physical-device waiver for the remaining 2.0 run.

Record the fresh exact-head substitute evidence: complete simulator
plan, focused upload suites, Release-configuration simulator build,
and the isolated two-instance Syncthing E2E. No real-device, keychain,
APNs, background-wake, or TestFlight-hardware claim is added.
@psimaker
psimaker marked this pull request as ready for review July 15, 2026 07:36

@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: 6

🧹 Nitpick comments (2)
ios/VaultSyncTests/DiagnosticsForegroundUploadRuntimeTests.swift (1)

193-196: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Verify the runtime-generated request and query against the pinned fixture.

Query equality only proves retries reuse one value, while the non-nil assertion proves only that some request was written. A production wire-format regression could still pass.

Proposed assertions
 let queries = await transport.uploadQueries()
 `#expect`(queries.count == 2)
 `#expect`(queries[0] == queries[1])
-#expect(requestBox.value() != nil)
+let requestBytes = try `#require`(requestBox.value())
+let runtimeRequest = try DiagnosticsUploadProtocol.decode(requestBytes, record: record)
+let runtimeQuery = try DiagnosticsUploadProtocol.decode(queries[0], record: record)
+#expect(runtimeRequest.body.m1Hex == uploadFixture.requestBodyHex)
+#expect(runtimeRequest.digest.m1Hex == uploadFixture.requestDigestHex)
+#expect(runtimeQuery.body.m1Hex == uploadFixture.queryBodyHex)
+#expect(runtimeQuery.digest.m1Hex == uploadFixture.queryDigestHex)

Based on learnings, verify fresh CryptoKit signatures rather than comparing their bytes with golden signatures.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ios/VaultSyncTests/DiagnosticsForegroundUploadRuntimeTests.swift` around
lines 193 - 196, Strengthen the assertions in the foreground upload runtime test
by decoding the captured request and validating its generated wire-format fields
against the pinned fixture, rather than only checking request presence and query
retry equality. Preserve the query reuse assertion, and verify CryptoKit
signatures by freshly validating them with the expected key/data instead of
comparing signature bytes to golden values.

Source: Learnings

go/bridge/folderstatus_test.go (1)

11-62: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Consider adding a symlink-substitution case.

The test covers non-canonical components, ignore rules, and artifact collisions well, but doesn't exercise the new info.IsSymlink() guard in diagnosticsUploadPathAvailable (e.g. replacing the operations directory with a symlink to an arbitrary path). Since that guard is the last line of defense against escaping the fixed diagnostics namespace, a regression test would be valuable.

🧪 Sketch of an added case
outside := filepath.Join(configDir, "outside-target")
if err := os.MkdirAll(outside, 0o700); err != nil {
    t.Fatal(err)
}
if err := os.RemoveAll(operationsPath); err != nil {
    t.Fatal(err)
}
if err := os.Symlink(outside, operationsPath); err != nil {
    t.Fatal(err)
}
if DiagnosticsUploadPathAvailable("diagnostics-folder", installation, operation) {
    t.Fatal("symlinked operations directory was accepted")
}
🤖 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 `@go/bridge/folderstatus_test.go` around lines 11 - 62, Extend
TestDiagnosticsUploadPathAvailableIsExactAndReadOnly with a symlink-substitution
case for the operations directory: create an outside target, replace
operationsPath with a symlink to it, and assert DiagnosticsUploadPathAvailable
returns false. Keep the existing collision and DiagnosticsUploadPathAllowed
assertions intact.
🤖 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 `@docs/app-capability-pairing-namespace-readiness.md`:
- Around line 7-10: Reconcile the M3-only upload statements throughout the
document with the introduction’s M5 foreground upload claim. Update the evidence
table and compatibility section to reflect the documented M5 upload behavior, or
explicitly scope the “upload unset,” “no request artifact,” and “no transfer
artifact” claims to M3.

In `@docs/m5-upload-attestation-readiness.md`:
- Around line 144-148: Update the Go test command in the readiness checklist to
make its working directory explicit: either prefix it with cd go or rewrite the
package path for execution from the repository root. Preserve the existing
noassets bridge test and count flag.

In `@ios/VaultSync/Services/DiagnosticsPairingController.swift`:
- Around line 612-619: Update the failure handling in the upload flow around the
DiagnosticsProtocolError and generic catch blocks, including the corresponding
logic near the second reported location. Preserve the existing artifactCreated
and transfer semantics when publishing failures instead of overwriting lastError
with .unavailable; classify authenticated protocol, tuple, and mandatory-flag
mismatches as .unsupported. Ensure finishUploadFailure receives the same
corrected classification while retaining the runID guard.

In `@ios/VaultSync/Services/DiagnosticsUploadFileStore.swift`:
- Around line 50-56: Update the cleanup logic around the file’s
open/verification flow so a failed invocation cannot unlink a pathname that has
been renamed or replaced by another process. Use atomic staged publication or
track and remove only the specific entry created by this invocation, preserving
fsync and close behavior while preventing deletion of unrelated files.

In `@ios/VaultSyncTests/DiagnosticsUploadM5Tests.swift`:
- Line 329: Update the privacy sink scan in the test named “Product upload
runtime remains isolated from response, durable state, and external systems” to
include DiagnosticsPairingController.swift alongside the existing protocol/store
sources. Ensure the controller’s upload orchestration and evidence-handling code
is covered by the same persistence, telemetry, and logging checks without
changing the test’s existing boundary assertions.
- Around line 371-379: Update the occurrence assertion in the controlledView
validation to require at least four components after splitting on
cancelAllForegroundUploads(), thereby enforcing the expected three cancellation
hooks. Keep the existing source-content and inactive-phase assertions unchanged.

---

Nitpick comments:
In `@go/bridge/folderstatus_test.go`:
- Around line 11-62: Extend TestDiagnosticsUploadPathAvailableIsExactAndReadOnly
with a symlink-substitution case for the operations directory: create an outside
target, replace operationsPath with a symlink to it, and assert
DiagnosticsUploadPathAvailable returns false. Keep the existing collision and
DiagnosticsUploadPathAllowed assertions intact.

In `@ios/VaultSyncTests/DiagnosticsForegroundUploadRuntimeTests.swift`:
- Around line 193-196: Strengthen the assertions in the foreground upload
runtime test by decoding the captured request and validating its generated
wire-format fields against the pinned fixture, rather than only checking request
presence and query retry equality. Preserve the query reuse assertion, and
verify CryptoKit signatures by freshly validating them with the expected
key/data instead of comparing signature bytes to golden values.
🪄 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: cbae8269-797b-460b-b9fe-725aa7d9afa5

📥 Commits

Reviewing files that changed from the base of the PR and between 7622b22 and b2442fd.

📒 Files selected for processing (22)
  • PRIVACY.md
  • docs/app-capability-pairing-namespace-readiness.md
  • docs/architecture.md
  • docs/m5-upload-attestation-readiness.md
  • go/bridge/diagnostics.go
  • go/bridge/folderstatus_test.go
  • ios/VaultSync/Services/DiagnosticsCapabilityNamespaceProtocol.swift
  • ios/VaultSync/Services/DiagnosticsPairingController.swift
  • ios/VaultSync/Services/DiagnosticsPinnedTransport.swift
  • ios/VaultSync/Services/DiagnosticsUploadFileStore.swift
  • ios/VaultSync/Services/DiagnosticsUploadPreflight.swift
  • ios/VaultSync/Services/DiagnosticsUploadProtocol.swift
  • ios/VaultSync/Services/SyncBridgeService.swift
  • ios/VaultSync/Views/ControlledDiagnosticsView.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/VaultSyncTests/DiagnosticsAppRuntimeM3Tests.swift
  • ios/VaultSyncTests/DiagnosticsForegroundUploadRuntimeTests.swift
  • ios/VaultSyncTests/DiagnosticsUploadM5Tests.swift
  • notify/diagnostics_contract_model_test.go
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Build & Test
🧰 Additional context used
📓 Path-based instructions (11)
**/*

⚙️ 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/VaultSync/es.lproj/Localizable.strings
  • ios/VaultSync/Services/DiagnosticsUploadPreflight.swift
  • ios/VaultSync/de.lproj/Localizable.strings
  • ios/VaultSync/Services/DiagnosticsPinnedTransport.swift
  • ios/VaultSync/en.lproj/Localizable.strings
  • ios/VaultSync/Services/SyncBridgeService.swift
  • ios/VaultSync/Services/DiagnosticsUploadFileStore.swift
  • docs/app-capability-pairing-namespace-readiness.md
  • go/bridge/diagnostics.go
  • notify/diagnostics_contract_model_test.go
  • ios/VaultSync/zh-Hans.lproj/Localizable.strings
  • ios/VaultSync/Services/DiagnosticsCapabilityNamespaceProtocol.swift
  • PRIVACY.md
  • ios/VaultSyncTests/DiagnosticsForegroundUploadRuntimeTests.swift
  • ios/VaultSyncTests/DiagnosticsUploadM5Tests.swift
  • ios/VaultSync/Views/ControlledDiagnosticsView.swift
  • ios/VaultSync/Services/DiagnosticsUploadProtocol.swift
  • go/bridge/folderstatus_test.go
  • ios/VaultSync/Services/DiagnosticsPairingController.swift
  • docs/architecture.md
  • docs/m5-upload-attestation-readiness.md
  • ios/VaultSyncTests/DiagnosticsAppRuntimeM3Tests.swift
**/*.swift

📄 CodeRabbit inference engine (Custom checks)

For Swift background execution changes, pass if work is bounded, cancellation-aware, handles expiration callbacks, and records errors without leaking private vault data. Fail only when background work can continue unbounded, miss cleanup, or violate iOS background execution constraints.

Files:

  • ios/VaultSync/Services/DiagnosticsUploadPreflight.swift
  • ios/VaultSync/Services/DiagnosticsPinnedTransport.swift
  • ios/VaultSync/Services/SyncBridgeService.swift
  • ios/VaultSync/Services/DiagnosticsUploadFileStore.swift
  • ios/VaultSync/Services/DiagnosticsCapabilityNamespaceProtocol.swift
  • ios/VaultSyncTests/DiagnosticsForegroundUploadRuntimeTests.swift
  • ios/VaultSyncTests/DiagnosticsUploadM5Tests.swift
  • ios/VaultSync/Views/ControlledDiagnosticsView.swift
  • ios/VaultSync/Services/DiagnosticsUploadProtocol.swift
  • ios/VaultSync/Services/DiagnosticsPairingController.swift
  • ios/VaultSyncTests/DiagnosticsAppRuntimeM3Tests.swift
ios/**/*.swift

📄 CodeRabbit inference engine (README.md)

ios/**/*.swift: Develop the iOS/iPadOS app using Swift 6 and SwiftUI, targeting iOS/iPadOS 18 or later.
Use VoiceOver and Dynamic Type throughout the iOS/iPadOS app.
Support localization in English, German, Spanish, and Simplified Chinese.

Files:

  • ios/VaultSync/Services/DiagnosticsUploadPreflight.swift
  • ios/VaultSync/Services/DiagnosticsPinnedTransport.swift
  • ios/VaultSync/Services/SyncBridgeService.swift
  • ios/VaultSync/Services/DiagnosticsUploadFileStore.swift
  • ios/VaultSync/Services/DiagnosticsCapabilityNamespaceProtocol.swift
  • ios/VaultSyncTests/DiagnosticsForegroundUploadRuntimeTests.swift
  • ios/VaultSyncTests/DiagnosticsUploadM5Tests.swift
  • ios/VaultSync/Views/ControlledDiagnosticsView.swift
  • ios/VaultSync/Services/DiagnosticsUploadProtocol.swift
  • ios/VaultSync/Services/DiagnosticsPairingController.swift
  • ios/VaultSyncTests/DiagnosticsAppRuntimeM3Tests.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/Services/DiagnosticsUploadPreflight.swift
  • ios/VaultSync/Services/DiagnosticsPinnedTransport.swift
  • ios/VaultSync/Services/SyncBridgeService.swift
  • ios/VaultSync/Services/DiagnosticsUploadFileStore.swift
  • ios/VaultSync/Services/DiagnosticsCapabilityNamespaceProtocol.swift
  • ios/VaultSyncTests/DiagnosticsForegroundUploadRuntimeTests.swift
  • ios/VaultSyncTests/DiagnosticsUploadM5Tests.swift
  • ios/VaultSync/Views/ControlledDiagnosticsView.swift
  • ios/VaultSync/Services/DiagnosticsUploadProtocol.swift
  • ios/VaultSync/Services/DiagnosticsPairingController.swift
  • ios/VaultSyncTests/DiagnosticsAppRuntimeM3Tests.swift
ios/**/*.{swift,plist}

📄 CodeRabbit inference engine (README.md)

ios/**/*.{swift,plist}: Use BGAppRefreshTask and BGContinuedProcessingTask when available for background processing, while allowing iOS to decide whether and when the app runs.
Implement APNs silent push handling for optional Cloud Relay wake-ups.

Files:

  • ios/VaultSync/Services/DiagnosticsUploadPreflight.swift
  • ios/VaultSync/Services/DiagnosticsPinnedTransport.swift
  • ios/VaultSync/Services/SyncBridgeService.swift
  • ios/VaultSync/Services/DiagnosticsUploadFileStore.swift
  • ios/VaultSync/Services/DiagnosticsCapabilityNamespaceProtocol.swift
  • ios/VaultSyncTests/DiagnosticsForegroundUploadRuntimeTests.swift
  • ios/VaultSyncTests/DiagnosticsUploadM5Tests.swift
  • ios/VaultSync/Views/ControlledDiagnosticsView.swift
  • ios/VaultSync/Services/DiagnosticsUploadProtocol.swift
  • ios/VaultSync/Services/DiagnosticsPairingController.swift
  • ios/VaultSyncTests/DiagnosticsAppRuntimeM3Tests.swift
**/*.{md,sh,go,swift}

📄 CodeRabbit inference engine (README.md)

Keep Relay-side request observation and wake-ups received on the iPhone as separate diagnostics evidence; one must not be treated as proof of the other.

Files:

  • ios/VaultSync/Services/DiagnosticsUploadPreflight.swift
  • ios/VaultSync/Services/DiagnosticsPinnedTransport.swift
  • ios/VaultSync/Services/SyncBridgeService.swift
  • ios/VaultSync/Services/DiagnosticsUploadFileStore.swift
  • docs/app-capability-pairing-namespace-readiness.md
  • go/bridge/diagnostics.go
  • notify/diagnostics_contract_model_test.go
  • ios/VaultSync/Services/DiagnosticsCapabilityNamespaceProtocol.swift
  • PRIVACY.md
  • ios/VaultSyncTests/DiagnosticsForegroundUploadRuntimeTests.swift
  • ios/VaultSyncTests/DiagnosticsUploadM5Tests.swift
  • ios/VaultSync/Views/ControlledDiagnosticsView.swift
  • ios/VaultSync/Services/DiagnosticsUploadProtocol.swift
  • go/bridge/folderstatus_test.go
  • ios/VaultSync/Services/DiagnosticsPairingController.swift
  • docs/architecture.md
  • docs/m5-upload-attestation-readiness.md
  • ios/VaultSyncTests/DiagnosticsAppRuntimeM3Tests.swift
docs/**/*.md

📄 CodeRabbit inference engine (docs/troubleshooting.md)

docs/**/*.md: Troubleshooting documentation should map each symptom to a fix, and users should retry from the app after applying each fix.
Document the supported vaultsync-notify installation topologies separately: Docker, systemd, launchd, Docker Compose, and Windows PowerShell.
Treat HTTP 429 from the relay trigger endpoint as a successful reachability result, not a doctor failure.
Treat inactive subscriptions and peer-state warnings as non-failing diagnostic conditions; --healthcheck must omit peer-state checks so offline peers do not make the container unhealthy.
Explain that vaultsync-notify reads the Syncthing API key from config.xml; permission or wrong-file errors should be diagnosed through the process user, SYNCTHING_CONFIG, and any SYNCTHING_API_KEY override rather than requesting a pasted key.
When troubleshooting relay connectivity, verify internet access, RELAY_URL, egress rules, the relay health endpoint, and the app's relay diagnostics; a successful health check proves reachability, while an updated Last Trigger Received proves delivery.
APNs background wake-ups require a valid APNs token and provisioned device, but do not require notification permission; retry APNs registration and provisioning before testing a trigger.
VaultSync should not move, recreate, or delete vault folders automatically; recovery from moved, replaced, or deleted folders requires the user's manual decision and may involve removing and re-accepting the share.
Security-scoped bookmark failures should be resolved by reconnecting and reselecting the Obsidian folder, then rescanning; removing a vault only stops syncing on that iPhone and must not affect other devices.
Foreground iPhone-to-server syncing is reliable only while VaultSync is open; iOS background execution is system-controlled and not guaranteed.
When a required Syncthing device is disconnected, verify that it is online, connectivity exists through LAN/VPN/relay, and its device ID is unchanged before re...

Files:

  • docs/app-capability-pairing-namespace-readiness.md
  • docs/architecture.md
  • docs/m5-upload-attestation-readiness.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/app-capability-pairing-namespace-readiness.md
  • PRIVACY.md
  • docs/architecture.md
  • docs/m5-upload-attestation-readiness.md
go/**/*.go

📄 CodeRabbit inference engine (README.md)

Use Go 1.26+ for the embedded Syncthing sync engine, built through gomobile as an xcframework.

Files:

  • go/bridge/diagnostics.go
  • go/bridge/folderstatus_test.go
go/bridge/**/*.go

⚙️ CodeRabbit configuration file

go/bridge/**/*.go: This code crosses the gomobile Swift-Go boundary. Verify exported signatures use only gomobile-safe primitive types,
preserve the JSON string contract, keep empty-string success conventions intact, and avoid breaking Swift decoding tests.
Review Syncthing lifecycle, locking, error strings, and noassets build assumptions carefully.

Files:

  • go/bridge/diagnostics.go
  • go/bridge/folderstatus_test.go
notify/**/*.{sh,go}

📄 CodeRabbit inference engine (README.md)

The optional notify sidecar must support server-side wake-up requests and must not receive notes, file or folder names, or vault structure.

Files:

  • notify/diagnostics_contract_model_test.go
notify/**/*.go

⚙️ CodeRabbit configuration file

notify/**/*.go: Review goroutine lifecycle, context cancellation, HTTP timeouts, signal handling, debounce behavior,
Syncthing REST API polling, relay API calls, error classification, and API-key handling.
Flag leaked request bodies, note metadata, Syncthing API keys, relay keys, or APNs-related secrets.

Files:

  • notify/diagnostics_contract_model_test.go
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-07-15T07:36:41.938Z
Learning: Controlled Diagnostics must be entirely user-controlled: app launch, upgrade, settings visits, ordinary sync, Relay wake-up, and background runs must not create or adopt keys, markers, pairings, endpoints, namespaces, artifacts, Syncthing shares, peers, trust decisions, or folder configuration.
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-07-15T07:36:41.938Z
Learning: The first mutation requires explicit device and folder selection, localized consent, a five-minute operator-generated D022 invitation, validation of all target and cryptographic bindings, matching transcript fingerprints, and user confirmation before persisting types 3, 5, and 7.
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-07-15T07:36:41.938Z
Learning: The app must not automatically discover helpers or use mDNS, UPnP, public port defaults, Cloud Relay, APNs, Syncthing discovery, or Relay tunnels for the diagnostics control plane.
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-07-15T07:36:41.938Z
Learning: Store diagnostics credentials only in the dedicated non-synchronizable generic-password Keychain service `eu.vaultsync.app.diagnostics.v1`, using `WhenUnlockedThisDeviceOnly`, with no shared access group or cloud escrow; require a matching complete-protection marker and treat missing or mismatched halves as re-pair required.
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-07-15T07:36:41.938Z
Learning: Network errors must produce `capability unavailable`; authenticated protocol, tuple, or mandatory-flag mismatches must produce `unsupported`; neither state may fall back to weaker success.
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-07-15T07:36:41.938Z
Learning: Persisted pending D022/D023 operations must enforce both a `mach_continuous_time` deadline and the signed wall-clock interval; restart reconstruction must not allow clock rollback to extend attempts beyond five elapsed minutes.
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-07-15T07:36:41.938Z
Learning: Credential, app-key, helper-key, and TLS-pin changes must use explicit signed transitions; old credentials remain authoritative until terminal acknowledgement and validated capability under the proposed state, and new key generations must not be selected while non-revoked authorizations are unstable.
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-07-15T07:36:41.938Z
Learning: Namespace enablement must be a separate explicit action. The app must not create or adopt `VaultSync Diagnostics`; it may read only fixed D023 paths beneath the existing settled folder using descriptor-relative, `O_NOFOLLOW` opens, and must validate regular single-link size-bounded immutable files and the epoch chain.
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-07-15T07:36:41.938Z
Learning: The namespace becomes active only after the helper-countersigned authorization file arrives through Syncthing and validates against the exact app-signed candidate; rotation must use append-only authorization epochs 2 through 9.
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-07-15T07:36:41.938Z
Learning: Disabling, revoking, downgrading, or resetting credentials must not delete namespace roots, helper state, shares, user data, peer copies, backups, conflict copies, remote history, or tombstones.
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-07-15T07:36:41.938Z
Learning: M3 must expose only the four fixed pairing, capability, namespace-enablement, and namespace-authorization paths; upload, download, roundtrip, response-authorization, and cleanup calls remain unavailable in this milestone.
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-07-15T07:36:41.938Z
Learning: Old apps and Relay v1 behavior must remain unchanged; additive diagnostics records must be ignored by downgraded apps, and re-upgrade must perform read-only reconstruction, fresh capability validation, and current namespace authorization without resuming operations.
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-07-15T07:36:52.219Z
Learning: VaultSync must preserve wire compatibility by embedding Syncthing rather than reimplementing its protocol.
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-07-15T07:36:52.219Z
Learning: The server-to-iPhone relay is an acceleration path, not a guarantee of symmetric real-time background synchronization; iPhone-to-server upload reliability requires foreground execution or an explicit Shortcuts automation.
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-07-15T07:37:02.915Z
Learning: Keep upload, download, and roundtrip evidence as separate fields; a helper signature proves helper authorship and signed causal bindings only, not transport route, direct peer, exact network bytes, block provenance, future delivery, or global synchronization health.
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-07-15T07:37:02.915Z
Learning: Maintain additive compatibility: old app/helper combinations retain existing behavior, incapable or unpaired helpers produce no artifact or fallback success, downgrades preserve credentials and user data, and re-upgrades require fresh capability, pairing, mapping, and namespace validation.
Learnt from: CR
Repo: psimaker/vaultsync

Timestamp: 2026-07-15T07:37:02.915Z
Learning: Do not describe simulator, substitute, or isolated-container evidence as real-device evidence; hardware keychain behavior, real APNs delivery, background waking, and TestFlight hardware installation remain unclaimed.
📚 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/Services/DiagnosticsUploadPreflight.swift
  • ios/VaultSync/Services/DiagnosticsPinnedTransport.swift
  • ios/VaultSync/Services/SyncBridgeService.swift
  • ios/VaultSync/Services/DiagnosticsUploadFileStore.swift
  • ios/VaultSync/Services/DiagnosticsCapabilityNamespaceProtocol.swift
  • ios/VaultSync/Views/ControlledDiagnosticsView.swift
  • ios/VaultSync/Services/DiagnosticsUploadProtocol.swift
  • ios/VaultSync/Services/DiagnosticsPairingController.swift
📚 Learning: 2026-07-12T23:03:04.680Z
Learnt from: psimaker
Repo: psimaker/vaultsync PR: 107
File: ios/VaultSyncTests/DiagnosticsContractTests.swift:39-46
Timestamp: 2026-07-12T23:03:04.680Z
Learning: In iOS Swift tests that use CryptoKit’s `Curve25519.Signing.PrivateKey.signature(for:)` (Ed25519), don’t assert that a generated signature’s bytes exactly match deterministic “golden”/fixture signatures. CryptoKit signatures may be randomized (different but valid for the same key+message). Instead, verify correctness by calling `isValidSignature` (or equivalent) against (1) the golden bytes and (2) the freshly generated signature, and avoid byte-for-byte equality assertions between CryptoKit output and reference vectors.

Applied to files:

  • ios/VaultSyncTests/DiagnosticsForegroundUploadRuntimeTests.swift
  • ios/VaultSyncTests/DiagnosticsUploadM5Tests.swift
  • ios/VaultSyncTests/DiagnosticsAppRuntimeM3Tests.swift
🪛 LanguageTool
docs/m5-upload-attestation-readiness.md

[style] ~33-~33: Consider removing “of” to be more concise
Context: ...e creating an artifact the app requires all of the following: - one active D022 pairing a...

(ALL_OF_THE)

🔇 Additional comments (23)
PRIVACY.md (1)

80-83: LGTM!

Also applies to: 137-139, 235-274

docs/app-capability-pairing-namespace-readiness.md (1)

59-64: LGTM!

docs/architecture.md (1)

49-60: LGTM!

Also applies to: 70-70, 89-124, 217-224

docs/m5-upload-attestation-readiness.md (1)

1-24: LGTM!

Also applies to: 25-138, 149-182

ios/VaultSyncTests/DiagnosticsAppRuntimeM3Tests.swift (1)

1192-1192: LGTM!

Also applies to: 1234-1234, 1247-1247, 1328-1328, 1780-1780, 1815-1815, 1848-1848

ios/VaultSyncTests/DiagnosticsForegroundUploadRuntimeTests.swift (1)

1-192: LGTM!

Also applies to: 198-822

ios/VaultSyncTests/DiagnosticsUploadM5Tests.swift (1)

4-6: LGTM!

Also applies to: 36-130, 426-472

notify/diagnostics_contract_model_test.go (1)

295-295: LGTM!

Also applies to: 320-320, 333-335

go/bridge/diagnostics.go (2)

20-113: LGTM! Symlink guards, base32 component validation, and .stignore handling all match Syncthing's own idioms and correctly reject non-canonical/traversal input per the accompanying test.


36-51: 🩺 Stability & Availability

No additional locking needed
getFolderConfigs() already takes mu internally, and folder.Filesystem() is a value method that just builds a filesystem from the copied folder config. No extra lock is needed on this path.

			> Likely an incorrect or invalid review comment.
ios/VaultSync/Services/DiagnosticsCapabilityNamespaceProtocol.swift (1)

626-675: LGTM! The visibility change is the minimal fix needed for cross-file reuse, and the new path-component builder matches the Go-side layout (go/bridge/diagnostics.go) segment-for-segment.

ios/VaultSync/Services/DiagnosticsUploadProtocol.swift (2)

46-124: LGTM elsewhere - signer selection by message type in decode, commonFieldsEqual's field-4 exclusion, and the request/query binding all look correct and consistent with the Go-side path layout and the fixture-based tests.

Also applies to: 153-349


126-151: 🎯 Functional Correctness

Remove the attestation/query issuedAt ordering concern

The attestation is already bound to the exact request/query pair, and its timestamp is only freshness-bound against now; a cross-clock ordering check here would reject valid attestations under normal skew.

			> Likely an incorrect or invalid review comment.
ios/VaultSync/en.lproj/Localizable.strings (1)

917-935: LGTM! All new keys line up exactly with the uploadStatusLabel/namespaceAction call sites shown in the ControlledDiagnosticsView.swift snippets, placeholders preserved.

ios/VaultSync/es.lproj/Localizable.strings (1)

917-935: LGTM! Keys match the English source exactly and placeholders are preserved in translation.

ios/VaultSync/zh-Hans.lproj/Localizable.strings (1)

917-935: LGTM! Keys match the English source exactly and placeholders are preserved in translation.

ios/VaultSync/Services/DiagnosticsUploadPreflight.swift (1)

1-117: LGTM!

ios/VaultSync/Services/SyncBridgeService.swift (1)

194-212: LGTM!

ios/VaultSync/Services/DiagnosticsUploadFileStore.swift (1)

1-49: LGTM!

Also applies to: 59-100

ios/VaultSync/Services/DiagnosticsPinnedTransport.swift (1)

46-46: LGTM!

Also applies to: 111-111

ios/VaultSync/Services/DiagnosticsPairingController.swift (1)

27-128: LGTM!

Also applies to: 387-611, 623-713, 714-720, 732-738

ios/VaultSync/Views/ControlledDiagnosticsView.swift (1)

6-6: LGTM!

Also applies to: 16-17, 40-47, 86-103, 320-353, 518-584

ios/VaultSync/de.lproj/Localizable.strings (1)

917-935: LGTM!

Comment thread docs/app-capability-pairing-namespace-readiness.md
Comment thread docs/m5-upload-attestation-readiness.md Outdated
Comment thread ios/VaultSync/Services/DiagnosticsPairingController.swift
Comment thread ios/VaultSync/Services/DiagnosticsUploadFileStore.swift
Comment thread ios/VaultSyncTests/DiagnosticsUploadM5Tests.swift
Comment thread ios/VaultSyncTests/DiagnosticsUploadM5Tests.swift Outdated
Address the six review findings on the upload-only PR:

- Suppress the generic error banner once the request artifact exists;
  the phase carries the truthful state, so the UI can no longer claim
  nothing was created after an artifact was written.
- Terminate invalid pinned-channel protocol messages as unsupported;
  conflict stays reserved for unexpected authenticated namespace
  content per Decision 024.
- Unlink a failed request file only after fstatat proves the directory
  entry still is the exact inode this invocation created, so a rename
  or replacement by Syncthing can never lose an unrelated entry.
- Add DiagnosticsPairingController.swift to the forbidden-sink scan.
- Count the view's cancellation hooks explicitly (two lifecycle hooks).
- Scope the M3 document's upload claims and fix the Go gate command.
@psimaker
psimaker merged commit e7334d3 into main Jul 15, 2026
31 checks passed
@psimaker
psimaker deleted the agent/app-foreground-upload-only branch July 15, 2026 08:15
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