SigningLog retention/pruning — the usage-cap log grows unbounded #35

Open
opened 2026-06-20 19:52:43 +00:00 by padreug · 0 comments
Owner

Summary

#28 (PR #34) added SigningLog — a durable, append-only row per allowed consequential signing (sign_event/encrypt/decrypt) — as the source of truth usage caps count against. It has no retention/pruning, so on a busy key it grows without bound.

Why it's deferred from #28

Caps only ever count signings within the trailing windowSeconds of a rule, so the only rows that can ever matter are those newer than the longest active window across all policies. Everything older is dead weight (kept only for incidental audit value). #28 shipped the enforcement; pruning is orthogonal and can land separately without changing behavior.

Proposed work

  1. Prune reaper. Periodically delete SigningLog rows older than max(windowSeconds across all PolicyRule) + margin (and a hard floor, e.g. keep ≥ 24h regardless). A setInterval in the daemon, or opportunistic prune on write (probabilistic, to avoid a delete per sign).
    • Careful with lifetime caps (windowSeconds = NULL): those count all-time, so a key with any lifetime-capped rule must not have its log pruned for that (method, kind). Either exclude keyUsers/methods covered by a lifetime cap from pruning, or keep an aggregate counter for lifetime caps specifically.
  2. Index already covers the query (@@index([keyUserId, method, createdAt])), so a createdAt < cutoff delete is cheap.
  3. Consider an audit-retention knob (env) if we want to keep the log longer than caps require for forensics.

Notes

  • Until this lands, SigningLog growth is bounded only by traffic. For current low-volume bunkers that's fine; revisit before any high-throughput signer.
  • The Request ephemeral-row auto-delete pattern (authorize.ts:79, setTimeout delete) is a precedent for opportunistic cleanup, though a batched reaper is better here.
  • Cross-refs: #28 (PR #34), #25.
## Summary #28 (PR #34) added `SigningLog` — a durable, append-only row per allowed consequential signing (`sign_event`/encrypt/decrypt) — as the source of truth usage caps count against. It has **no retention/pruning**, so on a busy key it grows without bound. ## Why it's deferred from #28 Caps only ever count signings within the **trailing `windowSeconds`** of a rule, so the only rows that can ever matter are those newer than the **longest active window** across all policies. Everything older is dead weight (kept only for incidental audit value). #28 shipped the enforcement; pruning is orthogonal and can land separately without changing behavior. ## Proposed work 1. **Prune reaper.** Periodically delete `SigningLog` rows older than `max(windowSeconds across all PolicyRule) + margin` (and a hard floor, e.g. keep ≥ 24h regardless). A `setInterval` in the daemon, or opportunistic prune on write (probabilistic, to avoid a delete per sign). - Careful with **lifetime caps** (`windowSeconds = NULL`): those count all-time, so a key with any lifetime-capped rule must **not** have its log pruned for that `(method, kind)`. Either exclude keyUsers/methods covered by a lifetime cap from pruning, or keep an aggregate counter for lifetime caps specifically. 2. **Index already covers the query** (`@@index([keyUserId, method, createdAt])`), so a `createdAt < cutoff` delete is cheap. 3. Consider an audit-retention knob (env) if we want to keep the log longer than caps require for forensics. ## Notes - Until this lands, `SigningLog` growth is bounded only by traffic. For current low-volume bunkers that's fine; revisit before any high-throughput signer. - The `Request` ephemeral-row auto-delete pattern (`authorize.ts:79`, `setTimeout` delete) is a precedent for opportunistic cleanup, though a batched reaper is better here. - Cross-refs: #28 (PR #34), #25.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
aiolabs/nsecbunkerd#35
No description provided.