SigningLog retention/prune reaper (unbounded growth from #28 usage caps) #37
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Follow-up deferred from #28 / #34 (per-rule windowed usage caps).
Problem
recordSigning()appends oneSigningLogrow for every allowed consequential signing (sign_event/encrypt/decrypt). The table is append-only and nothing ever deletes from it, so it grows unbounded for the life of the bunker. On a busy operator-IdP instance this is the highest-write table in the schema.Caps count live against it (
checkIfPubkeyAllowedstep 4:COUNT(SigningLog WHERE keyUserId/method/kind AND createdAt > since)), so only rows inside the active window ever affect a verdict — older rows are dead weight that only slow the count and bloat the DB.Proposed reaper
A periodic prune (daemon timer, or a maintenance RPC) that deletes
SigningLogrows older than the longest active finite window across allPolicyRules:The existing
@@index([keyUserId, method, createdAt])supports acreatedAt-bounded delete.⚠️ Correctness constraint — do NOT prune rows a lifetime cap counts
A rule with
windowSeconds = nullis a lifetime cap: step 4 counts that(method, kind)all-time with nocreatedAtfloor (acl/index.ts:145-146). If the reaper deletes old rows that a lifetime-capped rule would have counted, the lifetime count silently under-reports and the cap lifts itself — a security regression, not just a perf miss.So the prune boundary is not simply "oldest finite window." The reaper must:
windowSecondsamong active rules → candidate cutoff.(keyUserId, method, kind)that is matched by an active rule withmaxUsageCount != null AND windowSeconds == null(lifetime cap) — those rows are load-bearing forever.(method, kind)tuples.If any active lifetime cap exists, the simplest correct v1 is to skip pruning the tuples it covers entirely and only reap rows for tuples governed solely by finite-window caps.
Notes / open questions
(method, kind)tuples that are uncapped (no matchingmaxUsageCount) are never counted at all — those are always safe to prune tonow() - shortest-windowor even immediately, but keeping the logic simple (single global cutoff + lifetime exclusion) is fine for v1.signingLogRetentionDays) defaulting to the cap-driven minimum.Relevant code:
src/daemon/lib/acl/index.ts(recordSigning, step-4 count),prisma/schema.prismamodel SigningLog.