import type { EventSink, AccessLogSink } from '@substrat-run/kernel'; import type { DirectoryBackupStore, ScopeBackupStore } from './backups.js'; export declare function createR2BackupStore(bucket: unknown): ScopeBackupStore; /** * The Cloudflare R2 implementation of `DirectoryBackupStore` (#40). * * Deliberately a separate factory over the SAME binding shape rather than a mode flag on * `createR2BackupStore`: the two stores answer different questions and only this one can * delete. A deployment may point them at one bucket or two — the prefixes keep them apart * either way, and pointing THIS one at a bucket in another account is the upgrade path * from "survives losing the directory" to "survives losing the account". */ export declare function createR2DirectoryBackupStore(bucket: unknown): DirectoryBackupStore; /** * The R2 implementation of the `EventSink` seam (#1334) — Tier 2 ingest, master-plan * §5.3's "exact history", landing as objects an engine can read. * * **NDJSON to R2, not Pipelines-to-Iceberg, for v1 — and that is a deliberate * staging choice rather than a shortcut.** §5.3 settles the architecture as * *"Iceberg is the contract, the query engine is replaceable"*, and the same * paragraph leaves R2 SQL's fitness explicitly open pending a benchmark. Writing * partitioned NDJSON to a bucket the platform already operates gets the events out * of the scope — which is what bounds an outbox that is never pruned — without * committing the ingest path to a product decision nobody has made yet. A * compaction step turns these objects into Iceberg later, and the seam means the * drain never learns which happened. * * The same line-format reasoning as the access-log sink applies: one event per * line, so a truncated object still parses up to its last newline, and `jq`, * DuckDB and any lake loader all read it unaided. */ export declare function createR2EventSink(bucket: unknown): EventSink; /** * The R2 implementation of the `AccessLogSink` seam (K-24, control-plane.md §4.4) — the * Tier 2 that makes the access log's retention window closable. * * **NDJSON, not JSON.** One row per line, appended-shaped: an operator greps it, a SIEM * ingests it line-by-line, and a truncated object still parses up to its last newline. * A single JSON array would have to be whole to be readable at all, which is the wrong * failure mode for evidence. * * **Vendor-neutral by construction**, which was the point (#36): this writes a documented * line format to an object store, so Splunk, Datadog, Drata and a human with `jq` all * consume the same file. A vendor-specific push connector would have coupled the platform's * retention policy to one company's roadmap. */ export declare function createR2AccessLogSink(bucket: unknown): AccessLogSink; /** * The stored-copy lifecycle rule (#557) — the retention leg #36 left unmade. * * Every prune here is **operator-chosen**: these functions run only when a deployment * has named a window (the CP worker's `SCOPE_BACKUP_RETENTION_DAYS` / * `ACCESS_LOG_RETENTION_DAYS`), never on a default. That is the #553 posture — the * platform never deletes evidence on a schedule a human did not choose — applied to the * copies themselves. Enforced in code rather than by an R2 bucket lifecycle rule so the * policy is visible in the repo, testable against the fake bucket, and identical on any * future non-R2 store. * * Both prunes are age-based and **conservative by construction**: an object whose age * cannot be determined is kept, and a batch is deleted only when its NEWEST row is out * of the window — never one that still holds in-window rows. The walk collects stale * keys first and deletes after, so deletion never races the pagination cursor. */ interface PruneOptions { /** * Copies strictly older than this many days are dropped. `0` is honored (drop * everything on the next pass) — an operator can ask for it, as with the reap windows. */ olderThanDays: number; /** Injected clock, for tests. */ now?: () => Date; } /** * Drop `scopes/…` reap copies (#493) older than the window. A copy's age is its * `capturedAt` — the key's last segment, same as the store's own fallback address — so a * hand-uploaded object without metadata still ages out rather than living forever by * omission. An unparsable timestamp keeps the copy: deleting what cannot be dated is not * retention. * * Note the interaction with #37 (erasure): per-subject payloads inside these dumps are * sealed under per-subject DEKs, so an old copy's PII exposure is bounded by key * destruction already — this rule is the cost/hygiene bound, not the privacy one. */ export declare function pruneScopeBackups(bucket: unknown, options: PruneOptions): Promise; /** * Drop `access-log/…` NDJSON batches (#553) whose NEWEST row is older than the window. * Age comes from the mirrored `to` metadata, falling back to the key's own `lastId` (a * ULID carries its timestamp), so a batch is datable from either half of what `ship` * wrote. Undatable ⇒ kept, as above. * * The rows themselves already left the directory DO — `pruneAccessLog` deleted them only * after this object durably existed — so this window is where the record's Tier 2 * lifetime finally ends, and choosing it is choosing how far back "which rows left, and * when" can be answered from the object store. The admin log's own `drainAccessLog` / * `pruneAccessLog` rows are never touched: the egress stays witnessed after its payload * expires. */ export declare function pruneAccessLogBatches(bucket: unknown, options: PruneOptions): Promise; export {}; //# sourceMappingURL=r2-backups.d.ts.map