Skip to main content

ferro_audit/
lib.rs

1//! # ferro-audit
2//!
3//! Append-only structured before/after audit log for the Ferro framework.
4//!
5//! Audit entries record *what happened* — for forensic investigation,
6//! regulatory evidence, and state replay. They are the historical twin
7//! of [`ferro-events`]: events are "something happened, react now";
8//! audit entries are "something happened, here is the evidence forever".
9//!
10//! ## Example
11//!
12//! ```rust,ignore
13//! use ferro_audit::{AuditEntry, AuditActor, AuditTarget};
14//! use serde_json::json;
15//!
16//! AuditEntry::record("inventory.stock.adjust")
17//!     .actor(AuditActor::User(user_id.to_string()))
18//!     .target(AuditTarget::new("inventory.unit", unit_id.to_string()))
19//!     .before(json!({ "quantity": old }))
20//!     .after(json!({ "quantity": new }))
21//!     .reason("order_committed")
22//!     .write(&conn)
23//!     .await?;
24//! ```
25//!
26//! ## Replay
27//!
28//! `AuditEntry::history_for_target(&target, &conn).await?` returns the
29//! sequence of entries ordered ascending by `created_at`. Passing that
30//! sequence to [`reconstruct_state`] folds each entry's `after` JSON into
31//! a running object — the *replay* primitive.
32//!
33//! The fold is a **shallow object merge**: newer keys overwrite older
34//! keys at the top level only. Nested objects and arrays are replaced
35//! wholesale, not deep-merged. A consumer needing deep-merge runs its
36//! own fold over the `Vec<AuditEntry>`.
37//!
38//! ## Schema and Migration
39//!
40//! ferro-audit ships a SeaORM migration as [`CreateAuditLogTable`].
41//! Register it in your consumer-side `Migrator`:
42//!
43//! ```rust,ignore
44//! impl MigratorTrait for Migrator {
45//!     fn migrations() -> Vec<Box<dyn MigrationTrait>> {
46//!         vec![
47//!             Box::new(ferro_audit::CreateAuditLogTable),
48//!             // ... your app migrations
49//!         ]
50//!     }
51//! }
52//! ```
53
54mod actor;
55mod entity;
56mod entry;
57mod error;
58mod migration;
59mod prune;
60mod query;
61mod replay;
62mod target;
63
64pub use actor::AuditActor;
65pub use entry::AuditEntry;
66pub use error::AuditError;
67pub use migration::Migration as CreateAuditLogTable;
68pub use prune::prune_older_than;
69pub use query::{history_for_target, recent, recent_by_actor};
70pub use replay::reconstruct_state;
71pub use target::AuditTarget;
72
73// Entity re-export for SeaORM-native consumer queries (D-25 — consumers
74// needing pagination / custom filters drop down to sea-orm directly).
75pub use entity::Entity as AuditLogEntity;