pub struct AuditChain { /* private fields */ }Expand description
The chain itself. Open it once and append to it; it is Send + Sync and
holds its own lock, because one server writes one chain from many request
threads and two appends that interleaved would each claim the same prev.
Implementations§
Source§impl AuditChain
impl AuditChain
Sourcepub fn open(path: impl Into<PathBuf>) -> Result<AuditChain, AuditError>
pub fn open(path: impl Into<PathBuf>) -> Result<AuditChain, AuditError>
Open — or start — the chain at path.
The existing file is read and verified at open, not merely counted. A server that appended to a chain it had not checked would extend somebody’s forgery with genuine entries and make the break look like it happened later than it did.
Sourcepub fn open_framed(path: impl Into<PathBuf>) -> Result<AuditChain, AuditError>
pub fn open_framed(path: impl Into<PathBuf>) -> Result<AuditChain, AuditError>
Open the chain as the REPLICATED trail — every append is an
Append transaction before it is a line in a file, so the twin holds
the trail and gunnar control twin replay brings it back.
This is the D6 fix (lane T11, 2026-09-21). Before it, log/ was a
per-box path: the twin never held the audit trail and a restore came up
with none, reporting success. The restore happens on the worst day,
which is the day somebody has to reconstruct what happened.
It refuses rather than falling back. The chain must sit at
crate::txlog::AUDIT_REL under a data root the sink knows, or this
returns AuditError::Unframed naming why. A chain that quietly
wrote unframed lines would reproduce the exact bug — a trail the
restore drops without saying so. The caller’s job is to say it out
loud and open an ordinary chain with AuditChain::open.
Sourcepub fn is_framed(&self) -> bool
pub fn is_framed(&self) -> bool
Does every append of this chain also become a transaction? See
AuditChain::open_framed.
Sourcepub fn append(
&self,
event: AuditEvent<'_>,
at_unix_ms: i64,
) -> Result<AuditEntry, AuditError>
pub fn append( &self, event: AuditEvent<'_>, at_unix_ms: i64, ) -> Result<AuditEntry, AuditError>
Append one decision. Returns the entry that was written (or, while the log is
released, queued — Recorded::Queued), whose hash is the new tip an operator can
record. A box that follows its primary answers AuditError::NotOriginated.
Sourcepub fn record(
&self,
event: AuditEvent<'_>,
at_unix_ms: i64,
) -> Result<Recorded, AuditError>
pub fn record( &self, event: AuditEvent<'_>, at_unix_ms: i64, ) -> Result<Recorded, AuditError>
Record one decision — Recorded says what became of it.
G + G2 (release-3 test2 mock 2026-09-29): a chain opened framed on a box that was
then told BecomeTwin at runtime, and a primary whose log was released for its own
growth, answered every decision with an error (txlog: this box is a twin …, txlog: released …) that the gate logged at ERROR — 13 and 4 lines per mock — and the grow’s
decisions were lost. A follower now originates nothing and says so without an error; a
released log holds the trail in memory, in order, and writes it after the remount.
Sourcepub fn queued(&self) -> usize
pub fn queued(&self) -> usize
How many entries wait for the log to be taken back (Recorded::Queued).
Sourcepub fn flush(&self) -> Result<usize, AuditError>
pub fn flush(&self) -> Result<usize, AuditError>
Write what waits — every held entry, in order, once the log is taken back (the remount after a growth). Answers how many were written; a log still released writes none and keeps them.
Sourcepub fn entries(&self) -> Result<Vec<AuditEntry>, AuditError>
pub fn entries(&self) -> Result<Vec<AuditEntry>, AuditError>
Every entry, oldest first.
Sourcepub fn verify(&self) -> Result<usize, AuditError>
pub fn verify(&self) -> Result<usize, AuditError>
Check the chain end to end. Ok(n) is “n entries, and they hold”.
This is the function the finding asks for: an operator (or
cargo test) can ask the file whether anything in it has been changed
or removed, and get an answer that names the entry rather than a
shrug.