Keep rotated log files for a set number of days
ci / fork-checks (pull_request) Successful in 2m28s
ci / build (pull_request) Successful in 3m47s

Personal-data catalog spec, default D1 (settled 2026-09-28): log files
were never deleted. inbuxa:LogSettings.keepForDays says how many days
rotated log files are kept; unset (null) keeps every file, as before,
and a new install sets 30 days.

It is a fork-owned setting, stored under T + l as audit retention is,
not a field on x:TracerLog: that object is also stored inside
x:Bootstrap with a field after it, so a new field would change
x:Bootstrap's stored format. Server-level, with the tracers'
permissions (sysTracerGet, sysTracerUpdate); changes are in the audit
log, before and after.

Log files are local, so every node deletes its own: hourly, and at once
when the setting changes on that node. Only regular files named
<prefix>.<something> in each enabled log tracer's directory, last
changed more than the limit ago, are removed; the file being written is
never that old, and nothing else in the directory is touched. Minimum
one day. The catalog classifies inbuxa:LogSettings and points the log
file's retention at it.

Tested: unit tests for the file rule (only this log's old files; the
current file, other files and directories stay) and a purge on disk;
the system suite, which reads, sets, refuses zero, restores null and
checks the audit records; fork checks.
This commit is contained in:
2026-09-28 08:23:36 -07:00
parent bdd97c5828
commit 1d5a49409f
21 changed files with 789 additions and 2 deletions
+7
View File
@@ -49,6 +49,7 @@ pub enum MethodObject {
DeletedAccount,
// inbuxa: AI call limits
AiLimits,
LogSettings,
// inbuxa: "Explain this" with the local model
Explanation,
// inbuxa: the audit log
@@ -89,6 +90,7 @@ impl MethodObject {
MethodObject::MaskedEmail => Capability::FastmailMaskedEmail,
MethodObject::DeletedAccount => Capability::Inbuxa,
MethodObject::AiLimits => Capability::Inbuxa,
MethodObject::LogSettings => Capability::Inbuxa,
MethodObject::Explanation => Capability::Inbuxa,
MethodObject::AuditEvent
| MethodObject::AuditSettings
@@ -276,6 +278,8 @@ impl MethodName {
(MethodFunction::Set, MethodObject::DeletedAccount) => "inbuxa:DeletedAccount/set",
(MethodFunction::Get, MethodObject::AiLimits) => "inbuxa:AiLimits/get",
(MethodFunction::Set, MethodObject::AiLimits) => "inbuxa:AiLimits/set",
(MethodFunction::Get, MethodObject::LogSettings) => "inbuxa:LogSettings/get",
(MethodFunction::Set, MethodObject::LogSettings) => "inbuxa:LogSettings/set",
(MethodFunction::Set, MethodObject::Explanation) => "inbuxa:Explanation/set",
(MethodFunction::Get, MethodObject::AuditEvent) => "inbuxa:AuditEvent/get",
(MethodFunction::Query, MethodObject::AuditEvent) => "inbuxa:AuditEvent/query",
@@ -424,6 +428,8 @@ impl MethodName {
"inbuxa:DeletedAccount/set" => (MethodObject::DeletedAccount, MethodFunction::Set),
"inbuxa:AiLimits/get" => (MethodObject::AiLimits, MethodFunction::Get),
"inbuxa:AiLimits/set" => (MethodObject::AiLimits, MethodFunction::Set),
"inbuxa:LogSettings/get" => (MethodObject::LogSettings, MethodFunction::Get),
"inbuxa:LogSettings/set" => (MethodObject::LogSettings, MethodFunction::Set),
"inbuxa:Explanation/set" => (MethodObject::Explanation, MethodFunction::Set),
"inbuxa:AuditEvent/get" => (MethodObject::AuditEvent, MethodFunction::Get),
"inbuxa:AuditEvent/query" => (MethodObject::AuditEvent, MethodFunction::Query),
@@ -494,6 +500,7 @@ impl Display for MethodObject {
MethodObject::MaskedEmail => "MaskedEmail",
MethodObject::DeletedAccount => "inbuxa:DeletedAccount",
MethodObject::AiLimits => "inbuxa:AiLimits",
MethodObject::LogSettings => "inbuxa:LogSettings",
MethodObject::Explanation => "inbuxa:Explanation",
MethodObject::AuditEvent => "inbuxa:AuditEvent",
MethodObject::AuditSettings => "inbuxa:AuditSettings",
+2
View File
@@ -116,6 +116,7 @@ pub enum GetRequestMethod {
MaskedEmail(Box<GetRequest<crate::object::fastmail_masked_email::FastmailMaskedEmail>>),
DeletedAccount(Box<GetRequest<crate::object::inbuxa_deleted_account::DeletedAccount>>),
AiLimits(Box<GetRequest<crate::object::inbuxa_ai_limits::AiLimits>>),
LogSettings(Box<GetRequest<crate::object::inbuxa_log_settings::LogSettings>>),
AuditEvent(Box<GetRequest<crate::object::inbuxa_audit::AuditEvent>>),
AuditSettings(Box<GetRequest<crate::object::inbuxa_audit::AuditSettings>>),
AccountLock(Box<GetRequest<crate::object::inbuxa_account_lock::AccountLock>>),
@@ -148,6 +149,7 @@ pub enum SetRequestMethod<'x> {
MaskedEmail(Box<SetRequest<'x, crate::object::fastmail_masked_email::FastmailMaskedEmail>>),
DeletedAccount(Box<SetRequest<'x, crate::object::inbuxa_deleted_account::DeletedAccount>>),
AiLimits(Box<SetRequest<'x, crate::object::inbuxa_ai_limits::AiLimits>>),
LogSettings(Box<SetRequest<'x, crate::object::inbuxa_log_settings::LogSettings>>),
Explanation(Box<SetRequest<'x, crate::object::inbuxa_explanation::Explanation>>),
AuditSettings(Box<SetRequest<'x, crate::object::inbuxa_audit::AuditSettings>>),
AuditExport(Box<SetRequest<'x, crate::object::inbuxa_audit::AuditExport>>),
+14
View File
@@ -169,6 +169,13 @@ impl<'de> Visitor<'de> for CallVisitor {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Get, MethodObject::LogSettings) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Get(GetRequestMethod::LogSettings(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Get, MethodObject::ProtocolPolicy) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Get(GetRequestMethod::ProtocolPolicy(value)),
Err(err) => RequestMethod::invalid(err),
@@ -350,6 +357,13 @@ impl<'de> Visitor<'de> for CallVisitor {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Set, MethodObject::LogSettings) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Set(SetRequestMethod::LogSettings(value)),
Err(err) => RequestMethod::invalid(err),
Ok(None) => {
return Err(de::Error::invalid_length(1, &self));
}
},
(MethodFunction::Set, MethodObject::Explanation) => match seq.next_element() {
Ok(Some(value)) => RequestMethod::Set(SetRequestMethod::Explanation(value)),
Err(err) => RequestMethod::invalid(err),