Skip to main content
Introducing packages.sweber.dev
Documentation menuRetention and privacy

Retention and privacy

Retention periods, archives, erasure and access requests.

Retention periods

import { applyRetention, toNdjson } from "@weber-development/logarithm-retention"

await applyRetention(store, tenantIds, {
  keep: "13m",                     // 13 × 30 days
  tenants: { "bank-ag": "10y" },   // longer for customers who need it
  archive: async (events, { tenantId }) => {
    await bucket.put(`audit/${tenantId}/${events[0].occurredAt}.ndjson`, toNdjson(events))
  },
})

Durations: 36h, 90d, 12w, 13m (30 days each), 7y (365 days each). With archive, events are handed over oldest first in batches of 1000 before they are deleted. dryRun: true only counts. Run it daily.

Erasure requests

import { eraseActor } from "@weber-development/logarithm-retention"

const result = await eraseActor(store, "user_123", { key: process.env.AUDIT_KEY! })
// { actor: { id: "erased_3f9a…", type: "erased", name: "Deleted user" }, rewritten: 41, mentionedIn: ["01J…"] }

The person's id, name and email are replaced in every event they performed. The pseudonym is derived with an HMAC, so all their events still group together and the log stays readable, without revealing who it was. Events that mention the person as a target are listed in mentionedIn for you to review, because those may be needed as evidence.

With tamper evidence, erased events still verify.

Access requests

const data = await exportActorData(store, "user_123", { tenantId: "acme" })
// { performed: AuditEvent[], concerning: AuditEvent[] }

performed holds what the person did, concerning what others did to them. Hand it over as JSON or render it.

These functions help you answer requests under the GDPR (Art. 15, 17) and the Swiss FADP (Art. 25, 32). Whether and how far you must erase or disclose is a legal question; get advice.