Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 31 additions & 19 deletions docs/adrs/adr-0080.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ backup through that same row, and a restore is almost always wanted *after*
the expiry window, once a bad write has been noticed. Dropping the row on
expiry would make every backup unreachable by token exactly when it is
needed. The ledger therefore grows with the number of leases ever acquired,
bounded only by the `lease prune` fast-follow (see Consequences), which
bounded by `drive lease prune` (#1678, landed — see Consequences), which
drops a row together with the backup it points at, never one without the
other. Concurrent access —
two overlapping `drive lease acquire`/write invocations against the same
Expand Down Expand Up @@ -750,26 +750,38 @@ under the lease regime.
codebase's usual best-effort logging posture** (§11), justified because a
forensic log that can silently drop the one record proving a write
happened is not a forensic log.
- **All three local artifacts grow unboundedly until pruned.** `audit.jsonl`
is append-only by design (§11), nothing here deletes an old backup, and
the ledger keeps every row so that `restore` can find a backup by token
after expiry (§4). A `lease prune` that drops a ledger row *and* the
backup it points at together (never one without the other), mirroring
`log prune`, is a known, deliberate
fast-follow — the same posture ADR-0071 §11 took for read-path
enforcement — not a silently dropped requirement.
- **Two of the three local artifacts grow unboundedly until pruned;
`audit.jsonl` grows unboundedly by design.** The ledger kept every row so
that `restore` could find a backup by token after expiry (§4), and
nothing deleted an old backup — bounded since #1678 (landed) by
`drive lease prune --older-than`/`--max-size`/`--dry-run`, which drops a
ledger row *and* the backup it points at together (never one without the
other: a byte backup is deleted from local disk, a Drive-copy backup is
moved to Drive Trash, and the row's removal is persisted immediately,
one row at a time — not batched across a run — so an interrupted prune
strands at most the row it was working on), mirroring `log prune`. A live
lease is never a removal candidate regardless of either bound, and every
removal attempt writes its own best-effort audit record (`"pruned"` or
`"prune-failed"`, ADR-0080 §11's usual fail-open posture) so a pruned
backup stays discoverable via `omni-dev log --audit`. `audit.jsonl`
itself stays append-only by design (§11) and is deliberately out of
`lease prune`'s scope *as a pruning target* — it is never rotated or
truncated by this command, the same posture ADR-0071 §11 took for
read-path enforcement — not a silently dropped requirement; that is
distinct from `lease prune` writing *to* it.
- **Tamper-evident, not tamper-proof** (§12): all three local artifacts are
owned by the same account an agent could compromise. The audit log's
value is as a checkable trail against Drive's own revision history, never
as a security boundary in its own right.
- **Sync obligation.** [docs/drive.md](../drive.md) documents `drive lease
acquire`/`restore`, the `--lease` requirement per verb, the exemption
list, the config surface (§13) and the headless opt-out; ADR-0077's
recovery-message language in the codebase (not this document) now points
at the lease's own backup by Drive file id and names `drive lease restore
<TOKEN>` (Phase 4, landed) alongside the Drive UI, since restore only
locates a native document's copy rather than applying it; [docs/log.md](../log.md)
documents `RecordKind::Audit`, its fields, `omni-dev log --audit`, and
`audit.jsonl`'s location
and exemption from `OMNI_DEV_LOG_DISABLE`/rotation/`prune`. Keep all three
in sync when this ADR's decisions change.
acquire`/`restore`/`prune` (#1678, landed), the `--lease` requirement per
verb, the exemption list, the config surface (§13) and the headless
opt-out; ADR-0077's recovery-message language in the codebase (not this
document) now points at the lease's own backup by Drive file id and names
`drive lease restore <TOKEN>` (Phase 4, landed) alongside the Drive UI,
since restore only locates a native document's copy rather than applying
it; [docs/log.md](../log.md) documents `RecordKind::Audit`, its fields,
`omni-dev log --audit`, and `audit.jsonl`'s location
and exemption from `OMNI_DEV_LOG_DISABLE`/rotation/`prune` (and, by the
same reasoning, from `drive lease prune`). Keep all three in sync when
this ADR's decisions change.
58 changes: 58 additions & 0 deletions docs/drive.md
Original file line number Diff line number Diff line change
Expand Up @@ -1209,6 +1209,64 @@ token in the ledger and in `audit.jsonl`, which records both tokens on a
restore (`lease_id` the fresh one, `restored_from_lease_id` the backup one
read from) — see [docs/log.md](log.md#audit-log).

### Prune

```bash
$ omni-dev drive lease prune --older-than 30d --dry-run
$ omni-dev drive lease prune --older-than 30d
```

`drive lease prune` bounds the ledger's and the backup directory/folder's
otherwise-unbounded growth ([ADR-0080](adrs/adr-0080.md) Consequences,
#1678) by dropping expired rows together with the backups they point at.
It mirrors [`omni-dev log prune`](log.md#omni-dev-log-prune)'s shape:

| Flag | Effect |
|------|--------|
| `--older-than <DUR>` | Drop non-live rows whose expiry is strictly before this relative window (`7d`, `24h`, `2w`). A row expiring exactly at the cutoff survives. |
| `--max-size <SIZE>` | After age pruning, additionally drop the oldest-expiring survivors until their local backup bytes total at most `<SIZE>` (`10mb`, `512kb`, or a bare byte count). A Drive-copy backup counts as zero local bytes, so it's only reachable through `--older-than`. |
| `--dry-run` | Report what would be removed without deleting/trashing any backup or modifying the ledger. |

At least one of `--older-than`/`--max-size` is required. A **live** lease
(unexpired and unreleased) is never a removal candidate regardless of
either bound — pruning can never invalidate a lease a write is still
relying on. `--max-size` always keeps at least the single
most-recently-expired row's backup, even if it alone exceeds the budget.

A row and the backup it points at are always dropped **together, never one
without the other**: a byte backup is deleted from local disk, a
Drive-copy backup is moved to Drive Trash (recoverable by hand for ~30
days via the Drive UI) — and the ledger row is dropped, with that removal
persisted to disk, only once its own backup has been cleared (or found
already gone). Persistence happens one row at a time, not batched across
the whole run, so an interrupted prune (a crash, a killed process) can
leave at most the one row it was working on inconsistent with its
already-cleared backup — never the rest of the run. The ledger lock
itself is likewise held only briefly per step (once to decide candidates,
then once per row to persist that row's own removal), not for the whole
run, so a large batch never blocks a concurrent `drive lease
acquire`/`restore`/write for longer than a single row's own local disk
I/O. A backup deletion/trash failure for one row (e.g. a transient Drive
error) is logged and skips just that row, leaving it for a future prune
run, rather than failing the whole command.

```bash
$ omni-dev drive lease prune --older-than 30d
Removed 12 lease(s); kept 4 (3 trashed Drive backup(s), 0 failure(s), freed 8241203 bytes of local backups).
```

Every removal attempt — successful or failed — writes its own best-effort
`audit.jsonl` record (`verdict: "pruned"` or `"prune-failed"`, the latter
carrying the underlying error), the same fail-open posture `drive lease
acquire`'s own audit trail uses, so `omni-dev log --audit` can always
answer "why is this backup gone" for a specific lease. This is distinct
from `audit.jsonl` itself being out of scope *as a pruning target*: the
file is append-only forensic history by design
([ADR-0080](adrs/adr-0080.md) §11) and `drive lease prune` never rotates
or deletes its content, the same exemption it has from
`OMNI_DEV_LOG_DISABLE` and `omni-dev log prune`'s own rotation — see
[docs/log.md](log.md#audit-log).

## Sheets

`drive sheets` reads and writes the *cells* of a Google Sheet through the
Expand Down
Loading
Loading