Hardens the scan-policy and CVE-suppression replication channel as the
foundation of a multi-PR fleet sync hardening track. No new endpoints,
no new tables, no schema changes; receivers still tolerate legacy
payloads (absent pushedAt and controlIdentity) for rollout safety.
Wire protocol:
- Sender stamps every push with a strictly-increasing pushedAt and a
placeholder controlIdentity. Receiver rejects strictly-older pushedAt
with 409 STALE_SYNC_PUSH so the next write retries.
- pushedAt comparison plus row replacement plus watermark write run in a
single SQLite transaction; a partial-write window cannot leave the
watermark behind the row state.
Concurrency and limits:
- Per-node mutex on the sender so concurrent control writes serialize
per remote and never apply older state on top of newer.
- Sender-side row cap at MAX_SYNC_ROWS=5000 with a 6-hour throttled
truncation alert so flapping configs cannot flood the operator.
- Route-level body limit raised to 5MB on POST /api/fleet/sync/:resource
only; the global 100KB cap is unchanged. Oversize bodies return a
structured 413 SYNC_PAYLOAD_TOO_LARGE.
Determinism and hygiene:
- getMatchingPolicy gains an id-ASC tiebreaker so two replicas resolve
the same winner when policies tie on scope class.
- Inline comment documents why getMatchingPolicy filters node_id at SQL
yet still relies on JS identity matching for replicated rows.
- Comment on the receive endpoint documents why no requirePaid is
enforced (control's tier authorizes; replica trusts the bearer).
- STALE_SYNC_PUSH 409s no longer record a node failure; they are
expected protocol outcomes, not health issues.
Public surface additions:
- DatabaseService.transaction(fn): generic SAVEPOINT-friendly wrapper.
- DatabaseService.getLocalScanPolicies / getLocalCveSuppressions: SQL
filter on replicated_from_control = 0.
- StaleSyncPushError: typed sentinel the route translates to 409.
- fleetSyncConstants: shared MAX_SYNC_ROWS, body limit, state-key and
error-code maps so the wire protocol has one source of truth.
Tests:
- 24 new vitest cases across fleet-sync-service, fleet-sync-routes, and
database-matching-policy covering monotonic pushedAt, per-node
serialization, row truncation and throttle, stale-push suppression,
receiver back-compat, oversize-body 413, deterministic matching.
- Full backend suite: 1757 pass / 5 skipped.