# Audit: Feedback Loop UX ## Scope Security, data integrity, and operational audit of the feedback loop UX feature (`p1-feedback-loop-ux`). ## Security ### Input Validation - [x] **user_id and entity_id are opaque integers.** No string parsing, no injection vectors. - [x] **creator_id in MuteCreator/UnmuteCreator is a u64.** No trust boundary crossed -- the caller provides the creator_id, and the method trusts it. This matches existing patterns (e.g., `signal_with_context` accepts user_id without validation). - [x] **No new network endpoints.** This feature adds only in-process Rust API methods. No HTTP routes, no deserialization of external input. - [x] **`require_writeable` guard** prevents writes on read-only follower nodes, maintaining the leader-follower invariant. ### Authorization - [x] **No authorization model in scope.** The feedback API trusts the caller to pass the correct user_id. This is consistent with all other `TidalDb` methods (signal, retrieve, search). Authorization is an application-layer concern. - [x] **No privilege escalation.** A user can only affect their own feedback state. The `user_id` parameter scopes all operations. ### Data Leakage - [x] **`feedback_state` only returns boolean flags.** No signal values, no embeddings, no metadata leaked through this API. - [x] **Skip counter is ephemeral.** Resets on restart. No durable record of how many times a user skipped an item (only the final hard-neg promotion is visible). ## Data Integrity ### Atomicity - [x] **Each `submit_feedback` call performs all side effects synchronously** before returning `Ok(())`. There is no partial-apply failure mode where a signal is written but the bitmap is not updated. - [x] **Exception: WAL write failure.** If `self.signal()` returns an error (e.g., backpressure), the method returns `Err` immediately. No bitmap updates have occurred yet because the signal write happens first in all action branches. This is correct: the method is fail-fast, and the caller can retry. ### Durability - [x] **Signal-writing actions are WAL-durable.** Like, Hide, Dislike, MuteCreator, NotInterested all call `self.signal()` which writes to the WAL. - [x] **Unhide writes a negative-weight signal** for durability (weight=-1.0 on "hide" signal type). This ensures the unhide survives restart via WAL replay. - [x] **Undo-only actions (Unlike, UndoDislike, UnmuteCreator, Unsave) are ephemeral.** This is by design and documented in the spec. After restart, the undo state is lost unless the application replays it. ### Consistency - [x] **DashMap provides sequential consistency** within a single process. A write on thread A followed by a read on thread B sees the write. - [x] **No stale reads possible** within the same process, because there is no caching layer between the bitmap writes and the query pipeline reads. - [x] **Cross-node replication is eventually consistent.** Signal writes propagate via WAL shipping. Bitmap state is rebuilt from WAL on followers. This is documented in the spec as a non-goal for immediate cross-node reflection. ## Operational Impact ### Memory - [x] **skip_counter: DashMap<(u64, u32), u8>.** Each entry is ~17 bytes (key: 12 bytes, value: 1 byte, overhead: ~4 bytes). At 1M unique user-item skip pairs, this is ~17MB. Acceptable for a single-node database. - [x] **No new background threads.** No new timers, no new channels, no new periodic tasks. ### Performance - [x] **No regression on hot paths.** `submit_feedback` does not add any overhead to `signal()`, `retrieve()`, or `search()`. It is a new entry point, not a wrapper around existing hot paths. - [x] **feedback_state is O(1).** 4 DashMap lookups, no iteration, no I/O. ### Monitoring - [x] **No new metrics.** The existing signal write metrics (WAL append latency, signal count) already cover the signal-writing actions. Feedback-specific metrics (e.g., hide rate, mute rate) can be added as a future feature. ## Verdict **APPROVED.** No security, data integrity, or operational concerns. The feature is safe for production use.