Audit writer: open a record-less log, share the log-name rule, keep the lock file

M3a review findings 1, 2, 4 and part of 5. One empty log file made brokerd
panic at startup (files[len - 2]); it now opens as an empty log. brokerd's
name check tested one month digit, so a file bxctl ignored could become
brokerd's latest file; both now use proto::is_audit_log_name. Writer no
longer unlinks audit/.lock, which opened a two-writer window. No unwrap in
short_check.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-22 21:05:13 -07:00
co-authored by Claude Opus 5.5
parent 22a26aad45
commit eed0a221ca
6 changed files with 182 additions and 70 deletions
+13
View File
@@ -91,3 +91,16 @@ pub struct AuditRecord {
pub prev: Hash32,
pub event: AuditEvent,
}
/// True if `name` is an audit log file: `YYYY-MM-DD.jsonl`, ASCII digits with `-` at positions 4
/// and 7. `brokerd` and `bxctl audit verify` both decide with this, so they read the same files.
pub fn is_audit_log_name(name: &str) -> bool {
let Some(date) = name.strip_suffix(".jsonl") else {
return false;
};
date.len() == 10
&& date.bytes().enumerate().all(|(i, b)| match i {
4 | 7 => b == b'-',
_ => b.is_ascii_digit(),
})
}
+3 -1
View File
@@ -10,7 +10,9 @@ pub mod ids;
pub mod log;
pub mod wire;
pub use audit::{ApprovalAnswer, AuditEvent, AuditRecord, DecisionRecord, ResultStatus};
pub use audit::{
ApprovalAnswer, AuditEvent, AuditRecord, DecisionRecord, ResultStatus, is_audit_log_name,
};
pub use chain::{ChainFailure, ChainReport, ChainVerifier, Location, TornTail};
pub use class::DataClass;
pub use frame::{FrameError, MAX_FRAME, read_frame, write_frame};