using DodoSSH.Client.Domain; using DodoSSH.Contracts; namespace DodoSSH.Client.Sync; /// /// Somewhere to record that a keychain item was created, changed or deleted. /// /// /// /// Hooked into rather than into the view models. That /// repository is the single generic funnel every kind's create, update and delete goes through, so one /// write site covers all of them and picks up a new kind for free. Hooking the view models instead would /// miss VaultKnownHostStore, which writes pins programmatically at connect time and never touches a /// screen — and those are exactly the writes an audit trail must not be blind to. /// /// /// Three rules govern the call, and they are properties of where it sits rather than of what it does: /// /// /// /// After the outbox queue, never before. A crash between the two loses an advisory line; the reverse /// records a change that never happened. /// /// /// Every exception swallowed by the implementation. A failing log write must never fail a save — the /// entire point of the outbox is that saving works offline and cannot be refused. /// /// /// Not in a transaction with the outbox, or a log failure rolls back a change the user made. /// /// /// /// Note the deliberate asymmetry with the outbox itself, because it reads as a discrepancy otherwise: the /// outbox coalesces two edits of one item into a single pending row, and this does not — two edits /// are two lines. The outbox describes what still has to be sent; this describes what somebody did. /// /// public interface IActivityLogSink { /// Records one write. /// Which vault it happened in. /// Which sort of item, as the wire contract names it. /// The item. /// What the item was called at the time. /// What was done. /// /// The names of the fields that differ, and never their values. Empty for a create and a delete, and /// also when the previous version could not be read — which is why an empty list must not be taken to /// mean nothing changed. /// /// /// Returns by contract, for the reason IConnectionLogSink does: the caller /// is a save the user is waiting on, and an encrypt-and-write on that path would put the cost of the log /// into every keystroke that reaches a Save button. /// void Record( Guid vaultId, SyncEntityType kind, Guid entityId, string label, ActivityOperation operation, IReadOnlyList changedFields); }