feat: add controlled diagnostics download - #127
Conversation
Add the M6 controlled-download leg to the explicit D024 operation: after the exact accepted upload the app captures a fresh event-cursor, wall-clock, and engine-generation baseline, sends one signed type-6 response authorization over the pinned endpoint, and sets download observed only after a fresh ItemFinished apply of the exact expected response path plus complete type-7 chain validation. Bind product code to the cross-language M6 golden vectors and cover stale, tampered, generation-changed, cancelled, and restarted downloads plus partial-result preservation. Prove the response transport with a second two-instance Syncthing E2E driving the real helper foundation.
Record the M6 evidence boundary: separate upload and download fields, partial semantics after an accepted upload, retained opaque response copies, unchanged helper 2.0.2 wire surface, and the owner-approved physical-device waiver with simulator plus isolated Syncthing substitute evidence. Roundtrip remains unset and VaultSync 2.0 NO-GO.
📝 WalkthroughWalkthroughThe PR adds a signed diagnostics response protocol, controlled download evidence tracking, updated foreground-check UI and localization, runtime boundary tests, a Syncthing download E2E test, and CI coverage for both upload and response transfer. ChangesResponse protocol and acceptance
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 7✅ Passed checks (7 passed)
✨ 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: 1
🧹 Nitpick comments (1)
ios/VaultSync/Services/DiagnosticsPairingController.swift (1)
732-748: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMisleading "retransmit until accepted" comment — the retry can never actually happen.
transport.post(path: DiagnosticsResponseProtocol.path, ..., responseBody: false)only returns without throwing on HTTP 202, which immediately setsauthorizationAccepted = true. Any other outcome (4xx/429/network error) throws, and since this call sits inside the same un-caughttrychain as the rest ofrunControlledDownload, that throw aborts the whole controlled-download attempt (propagating torunForegroundUpload's outer catch). So the loop can never re-enter this block withauthorizationAccepted == falseon a later iteration — it either succeeds once or the whole operation dies on the first attempt. Unlike the upload leg's poll (where both 200 and 202 are non-throwing, socontinuegenuinely retries "not ready yet"), there's no non-throwing "pending" outcome here for the guard to protect against.This matches what
docs/m6-controlled-download-readiness.mddocuments ("the authorization is sent once"), so it's not a behavior bug — but the comment and theif !authorizationAccepted { … }wrapping imply resilience against a single transient failure that doesn't exist, which could mislead a future maintainer relying on it.✏️ Suggested clarification
- if !authorizationAccepted { - // Retransmit the byte-identical signed authorization until the - // idempotent helper accepts it; a 202 carries no body and is - // transport diagnostics only, never download evidence. - try consumeUploadRequest(recordID: recordID) + if !authorizationAccepted { + // Sent at most once: any transport failure here aborts the + // whole controlled-download attempt rather than retrying on a + // later poll (202 is the only non-throwing outcome, and it + // immediately marks acceptance). Matches the documented + // "sent once" behavior in m6-controlled-download-readiness.md. + try consumeUploadRequest(recordID: recordID)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@ios/VaultSync/Services/DiagnosticsPairingController.swift` around lines 732 - 748, Clarify the comment above the authorization POST to state that the canonical authorization is sent once and that a successful HTTP 202 marks it accepted; remove the misleading “retransmit until accepted” wording. Keep the existing `if !authorizationAccepted` flow and error propagation unchanged, since no retry behavior is requested.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@ios/VaultSync/Views/ControlledDiagnosticsView.swift`:
- Around line 538-545: Update the events closure to fetch and store the result
of SyncBridgeService.getEventsSince(Int(sinceID)) before reading
SyncBridgeService.eventStreamGeneration(), then pass the fetched events and
newly read generation to DiagnosticsResponseProtocol.eventSnapshot. Preserve the
existing sinceID conversion and snapshot structure.
---
Nitpick comments:
In `@ios/VaultSync/Services/DiagnosticsPairingController.swift`:
- Around line 732-748: Clarify the comment above the authorization POST to state
that the canonical authorization is sent once and that a successful HTTP 202
marks it accepted; remove the misleading “retransmit until accepted” wording.
Keep the existing `if !authorizationAccepted` flow and error propagation
unchanged, since no retry behavior is requested.
🪄 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: 65201fa3-a46e-4cb3-a5ad-178ceefde089
📒 Files selected for processing (18)
.github/workflows/ci.ymlPRIVACY.mddocs/architecture.mddocs/m6-controlled-download-readiness.mdios/VaultSync/Services/DiagnosticsCapabilityNamespaceProtocol.swiftios/VaultSync/Services/DiagnosticsPairingController.swiftios/VaultSync/Services/DiagnosticsPinnedTransport.swiftios/VaultSync/Services/DiagnosticsResponseProtocol.swiftios/VaultSync/Views/ControlledDiagnosticsView.swiftios/VaultSync/de.lproj/Localizable.stringsios/VaultSync/en.lproj/Localizable.stringsios/VaultSync/es.lproj/Localizable.stringsios/VaultSync/zh-Hans.lproj/Localizable.stringsios/VaultSyncTests/DiagnosticsControlledDownloadRuntimeTests.swiftios/VaultSyncTests/DiagnosticsForegroundUploadRuntimeTests.swiftios/VaultSyncTests/DiagnosticsUploadM5Tests.swiftnotify/diagnostics_contract_model_test.gonotify/diagnostics_download_syncthing_e2e_test.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{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:
docs/m6-controlled-download-readiness.mdios/VaultSync/Services/DiagnosticsPinnedTransport.swiftnotify/diagnostics_contract_model_test.goios/VaultSync/Services/DiagnosticsCapabilityNamespaceProtocol.swiftios/VaultSyncTests/DiagnosticsUploadM5Tests.swiftnotify/diagnostics_download_syncthing_e2e_test.goios/VaultSync/Services/DiagnosticsResponseProtocol.swiftPRIVACY.mdios/VaultSyncTests/DiagnosticsControlledDownloadRuntimeTests.swiftdocs/architecture.mdios/VaultSync/Views/ControlledDiagnosticsView.swiftios/VaultSync/Services/DiagnosticsPairingController.swiftios/VaultSyncTests/DiagnosticsForegroundUploadRuntimeTests.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 supportedvaultsync-notifyinstallation 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;--healthcheckmust omit peer-state checks so offline peers do not make the container unhealthy.
Explain thatvaultsync-notifyreads the Syncthing API key fromconfig.xml; permission or wrong-file errors should be diagnosed through the process user,SYNCTHING_CONFIG, and anySYNCTHING_API_KEYoverride 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/m6-controlled-download-readiness.mddocs/architecture.md
**/*
⚙️ 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/m6-controlled-download-readiness.mdios/VaultSync/Services/DiagnosticsPinnedTransport.swiftnotify/diagnostics_contract_model_test.goios/VaultSync/Services/DiagnosticsCapabilityNamespaceProtocol.swiftios/VaultSyncTests/DiagnosticsUploadM5Tests.swiftnotify/diagnostics_download_syncthing_e2e_test.goios/VaultSync/de.lproj/Localizable.stringsios/VaultSync/Services/DiagnosticsResponseProtocol.swiftPRIVACY.mdios/VaultSyncTests/DiagnosticsControlledDownloadRuntimeTests.swiftios/VaultSync/en.lproj/Localizable.stringsdocs/architecture.mdios/VaultSync/zh-Hans.lproj/Localizable.stringsios/VaultSync/Views/ControlledDiagnosticsView.swiftios/VaultSync/Services/DiagnosticsPairingController.swiftios/VaultSync/es.lproj/Localizable.stringsios/VaultSyncTests/DiagnosticsForegroundUploadRuntimeTests.swift
**/*.md
⚙️ CodeRabbit configuration file
**/*.md: Review public documentation for technical accuracy, privacy/security claims, App Store-facing wording,
setup correctness, and consistency with the free app plus optional Cloud Relay subscription model.
Files:
docs/m6-controlled-download-readiness.mdPRIVACY.mddocs/architecture.md
**/*.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/DiagnosticsPinnedTransport.swiftios/VaultSync/Services/DiagnosticsCapabilityNamespaceProtocol.swiftios/VaultSyncTests/DiagnosticsUploadM5Tests.swiftios/VaultSync/Services/DiagnosticsResponseProtocol.swiftios/VaultSyncTests/DiagnosticsControlledDownloadRuntimeTests.swiftios/VaultSync/Views/ControlledDiagnosticsView.swiftios/VaultSync/Services/DiagnosticsPairingController.swiftios/VaultSyncTests/DiagnosticsForegroundUploadRuntimeTests.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/DiagnosticsPinnedTransport.swiftios/VaultSync/Services/DiagnosticsCapabilityNamespaceProtocol.swiftios/VaultSyncTests/DiagnosticsUploadM5Tests.swiftios/VaultSync/Services/DiagnosticsResponseProtocol.swiftios/VaultSyncTests/DiagnosticsControlledDownloadRuntimeTests.swiftios/VaultSync/Views/ControlledDiagnosticsView.swiftios/VaultSync/Services/DiagnosticsPairingController.swiftios/VaultSyncTests/DiagnosticsForegroundUploadRuntimeTests.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/DiagnosticsPinnedTransport.swiftios/VaultSync/Services/DiagnosticsCapabilityNamespaceProtocol.swiftios/VaultSyncTests/DiagnosticsUploadM5Tests.swiftios/VaultSync/Services/DiagnosticsResponseProtocol.swiftios/VaultSyncTests/DiagnosticsControlledDownloadRuntimeTests.swiftios/VaultSync/Views/ControlledDiagnosticsView.swiftios/VaultSync/Services/DiagnosticsPairingController.swiftios/VaultSyncTests/DiagnosticsForegroundUploadRuntimeTests.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/DiagnosticsPinnedTransport.swiftios/VaultSync/Services/DiagnosticsCapabilityNamespaceProtocol.swiftios/VaultSyncTests/DiagnosticsUploadM5Tests.swiftios/VaultSync/Services/DiagnosticsResponseProtocol.swiftios/VaultSyncTests/DiagnosticsControlledDownloadRuntimeTests.swiftios/VaultSync/Views/ControlledDiagnosticsView.swiftios/VaultSync/Services/DiagnosticsPairingController.swiftios/VaultSyncTests/DiagnosticsForegroundUploadRuntimeTests.swift
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.gonotify/diagnostics_download_syncthing_e2e_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.gonotify/diagnostics_download_syncthing_e2e_test.go
.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/ci.yml
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-07-15T09:11:23.730Z
Learning: VaultSync 2.0 remains NO-GO until the required helper publication, production rollout, rollback, real-device/PR gate, later download milestone, and causal roundtrip milestone are complete.
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-07-15T09:11:34.772Z
Learning: All Xcode results and derived data must remain outside the repository under `/tmp`.
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-07-15T09:11:34.772Z
Learning: Run the complete iOS plan, Release simulator build, design-token lint, string-key parity check, and sync-proof privacy lint before accepting the milestone.
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-07-15T09:11:34.772Z
Learning: Do not claim hardware keychain behavior, real APNs delivery, real background waking, TestFlight hardware installation, or real-device evidence when only simulator and isolated local Syncthing evidence exists.
Learnt from: CR
Repo: psimaker/vaultsync
Timestamp: 2026-07-15T09:11:34.772Z
Learning: Derive causal roundtrip only from this operation's upload and download legs after the PR and its review/CI gates complete.
📚 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/DiagnosticsPinnedTransport.swiftios/VaultSync/Services/DiagnosticsCapabilityNamespaceProtocol.swiftios/VaultSync/Services/DiagnosticsResponseProtocol.swiftios/VaultSync/Views/ControlledDiagnosticsView.swiftios/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/DiagnosticsUploadM5Tests.swiftios/VaultSyncTests/DiagnosticsControlledDownloadRuntimeTests.swiftios/VaultSyncTests/DiagnosticsForegroundUploadRuntimeTests.swift
🔇 Additional comments (24)
.github/workflows/ci.yml (1)
120-120: LGTM!Also applies to: 151-151, 165-165
ios/VaultSyncTests/DiagnosticsUploadM5Tests.swift (1)
349-355: LGTM!Also applies to: 366-366
notify/diagnostics_contract_model_test.go (1)
296-296: LGTM!Also applies to: 336-338
notify/diagnostics_download_syncthing_e2e_test.go (1)
1-121: LGTM!ios/VaultSync/Views/ControlledDiagnosticsView.swift (1)
86-94: LGTM!Also applies to: 103-103, 339-344, 548-568, 589-589, 598-598
ios/VaultSync/de.lproj/Localizable.strings (1)
917-930: LGTM!ios/VaultSync/en.lproj/Localizable.strings (1)
917-930: LGTM!ios/VaultSync/es.lproj/Localizable.strings (1)
917-930: LGTM!ios/VaultSync/zh-Hans.lproj/Localizable.strings (1)
917-930: LGTM!ios/VaultSync/Services/DiagnosticsCapabilityNamespaceProtocol.swift (1)
676-687: LGTM!ios/VaultSync/Services/DiagnosticsResponseProtocol.swift (3)
1-84: LGTM!
makeAuthorization,validateResponseArtifact, anddecodecorrectly chain digests across request → authorization → response, bound expiry to the tightest of the three, and select the signer (app vs. helper key) per message type. Field-label sets line up withexpectedLabelsand the golden-vector test.Also applies to: 86-118, 120-147
169-192: LGTM!
freshResponseApply's combined event-id/path/action/error/wall-clock gate matches the documented download-evidence contract, andsign/validateAuthorizationChain/validateFields/validateClock/commonFieldsEqualare consistent with the equivalent patterns inDiagnosticsNamespaceProtocol/DiagnosticsUploadProtocol.Also applies to: 194-306
149-167: 🩺 Stability & AvailabilityNo issue:
getEventsSincealways returns JSON
SyncBridgeService.getEventsSince(lastID:)returns"[]"when idle, stopped, or on marshal failure, so an ordinary “no events yet” poll won’t be misread as a bridge failure.> Likely an incorrect or invalid review comment.PRIVACY.md (1)
136-142: LGTM!The evidence-boundary wording (fresh baselines, one signed authorization, conflict-on-invalid-file, roundtrip-remains-unset, simulator/isolated-Syncthing-only download evidence) matches the implementation and test assertions in this cohort, and correctly avoids overclaiming physical-device evidence.
Also applies to: 262-283
ios/VaultSyncTests/DiagnosticsControlledDownloadRuntimeTests.swift (2)
9-95: LGTM!Exhaustive byte-tamper loop plus body/digest equality (not raw signature-byte equality) correctly follows the CryptoKit Ed25519 randomized-signature guidance from prior review learnings.
Source: Learnings
97-437: LGTM!Stale/tampered/generation-changed/cancelled/restarted scenarios each correctly preserve
uploadObservedwhile keepingdownloadObservedfalse, matching the documented partial-result contract.ios/VaultSyncTests/DiagnosticsForegroundUploadRuntimeTests.swift (4)
126-238: LGTM!The M6 happy-path wiring (response-path derivation,
respondclosure writing the artifact + matching event,.downloadObserved/poll-count assertions) is internally consistent withDiagnosticsResponseProtocol's field/label contract.
273-274: LGTM!Mechanical wiring of the new
events:parameter across the late/restart/raced/rate-limited/timeout/rejected scenarios, plus wideningwaitForTerminalUpload's non-terminal set to include.uploadObserved, is consistent and doesn't change any prior assertions' meaning.Also applies to: 319-320, 364-365, 392-431, 441-442, 481-482, 541-542, 632-634
694-699: LGTM!
LockedDownloadEventBox,iso8601WithNanoseconds, andmakeHelperResponseArtifactare consistent with existing Locked*/test-fixture conventions in this file, and the artifact fields matchDiagnosticsResponseProtocol'sexpectedLabels[.responseArtifact].Also applies to: 717-800
827-827: LGTM!
ForegroundUploadTransport's newrespond/authorizationsstate and@Sendableclosure typing are correctly actor-isolated for Swift 6 strict concurrency.Also applies to: 845-978
ios/VaultSync/Services/DiagnosticsPinnedTransport.swift (1)
47-47: LGTM!docs/architecture.md (1)
50-61: LGTM!Also applies to: 90-104, 124-134, 228-236
docs/m6-controlled-download-readiness.md (1)
1-89: LGTM!ios/VaultSync/Services/DiagnosticsPairingController.swift (1)
27-52: LGTM!Also applies to: 76-78, 392-434, 597-649, 651-731, 749-814, 906-936
An engine restart between the two bridge reads would tag new-engine events with the pre-restart generation and falsely pass the download continuity check. Reading events first makes the generation a valid witness: any restart before or during the event fetch fails the caller's boundary check instead.
What & why
Implement the owner-authorized M6 controlled-download leg of Decision 024 against the published, unchanged helper 2.0.2 runtime. Download evidence derives only inside the same explicit operation after its exact accepted upload: one signed type-6 response authorization over the pinned endpoint, then a fresh
ItemFinishedapply of the exact expected response path — newer than the post-upload cursor and wall-clock baselines inside an unchanged engine generation — followed by complete type-7 chain validation. Causal roundtrip remains 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
f41f597d3ceca73da102e5e447382dfae07d2e08.Component(s)
Testing
diagnostics-response-m6.jsongolden vectors, including full-chain validation and per-byte tamper rejectionTestDiagnosticsDownloadThroughTwoEphemeralSyncthingInstancesin the isolated no-network Linux container: exact request/attestation propagate app→helper through real Syncthing, the real helper response foundation creates the one signed response artifact, the exact bytes propagate helper→app and validate through the full D024 chain, helper restart replays idempotentlyTestDiagnosticsUploadThroughTwoEphemeralSyncthingInstancesre-run in the same isolated containercd notify && go test ./... -count=1on macOS, plusgo vetandgofmtCompatibility 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; the request, attestation, and response artifacts are synchronized opaque files whose retained copies never regain validity. Forward recovery starts with a fresh capability and never resumes an old proof.
Security and privacy
The download leg is foreground-only and bound to the same explicit operation, tuple, and TTL as its upload. It uses the pinned TLS-1.3/SPKI endpoint with a fixed path, one signed byte-exact authorization, local event observation only (no helper polling), rate/concurrency limits shared with the upload leg, 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.