Skip to content

fix(sync): fail closed before conflict retention can discard unique bytes #167

Description

@psimaker

Parent and release gate

Sub-issue of #150. This implementation blocks the replacement VaultSync 2.0.2 candidate. Candidate eb83ade738c1da90cc9c798823ddd7bfd43b6466 remains NO-GO.

Problem

With MaxConflicts=10, the pinned Syncthing source can physically remove unique conflict bytes. Deterministic characterization also demonstrates conflict-on-conflict deletion, pruning after a failed rename, and replacement risk at a generated-name collision. MaxConflicts=-1, a higher limit, stock versioning, and a Syncthing upgrade do not address every path.

Policy

  • A finite MaxConflicts is a hard stop, never an authorization to delete.
  • 0 stops at the first conflict; -1 remains unlimited.
  • Conflict-on-conflict, incomplete inventory, destination collision, remote deletion or replacement of an existing conflict copy, and I/O failure stop before mutation.
  • Existing folder configuration is not rewritten.
  • Recovery requires an explicit user-confirmed Keep Both action and Resume.
  • No automatic delete, overwrite, repair, re-pair, reindex, config rewrite, or folder repoint is permitted.

Implementation scope

  • Upstream-near fork patch under go/patches/, never the generated Syncthing tree.
  • Atomic no-replace conflict preservation.
  • Stable path-free safety error through the bridge.
  • Critical localized iOS issue and fail-closed folder state.
  • Idempotent pause-once behavior that respects a deliberate Resume.
  • Unique atomic no-replace Keep Both recovery.
  • Decision record and update to the existing manual-conflict doctrine.

Acceptance criteria

  • An issue-numbered true-pull RED fails on the unchanged candidate before fix code is added.
  • More than ten unique sentinels, conflict-on-conflict, same-home restart, and explicit recovery preserve every hash and end with converged identical inventories.
  • New, genuinely offered-and-accepted pending, existing, persisted, overridden, and upgraded folders are covered.
  • Rename failure, bounded capacity, real ENOSPC, capacity growth, crash/restart, and destination collisions never delete or replace bytes.
  • Incoming remote retention deletes/replaces from an unpatched peer are fail-closed.
  • Folder and background status never report false success.
  • No user-derived path, vault name, device ID, or sentinel content is logged.
  • Required Go, race, iOS, lifecycle, M4/M5/M6, physical-device, and soak matrices are recorded against the exact post-merge candidate and freshly built XCFramework.

Out of scope

No Syncthing upgrade, #152/STUN patch, automatic quarantine, StoreKit, signing, entitlement, bundle-ID, product-ID, relay-protocol, tag, release, upload, or deployment change.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:conflictsConflict detection and resolutionarea:syncthingEmbedded Syncthing and upstream behaviorbugSomething isn't workingdocumentationImprovements or additions to documentationgoPull requests that update go codepriority:P0Confirmed critical safety issuerisk:data-lossRisk of losing or replacing user bytesrisk:false-successRisk of reporting success without evidencerisk:releaseRelease-blocking evidence or documentation risk

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions