# v0.87.11 - Snapshot Security

> **Status:** Implemented
> **Scope:** 6 person-weeks
> **User promise:** *"Snapshots cannot cross ownership or schema boundaries by accident."*
> **Blocked by:** [v0.87.10](v0.87.10.md)
> **Program:** [Lifecycle security reimplementation](../plans/pg_trickle_lifecycle_security_reimplementation_plan.md)

## Theme

Give snapshots their own ownership and provenance contract. Internal snapshot
creation may use private infrastructure, but a caller-selected schema uses the
caller's privileges. Restore and drop require checks against both the snapshot
and the destination stream table.

Stream ownership transfer does not rewrite historical snapshot ownership. A
recreated relation cannot inherit trust from a stale name-only binding.

## Scope allocation

| Work package | Person-weeks |
|---|---:|
| Snapshot target resolution and ownership | 1.25 |
| Provenance-bound restore and drop | 1.25 |
| Upgrade, cleanup, and concurrency behavior | 1.00 |
| Security, rollback, provenance, and upgrade tests | 2.50 |
| **Total** | **6.00** |

## Items

### LSEC-13: Caller-checked snapshot creation

Default internal snapshots are created through private infrastructure and then
owned by the caller. A caller-supplied schema requires caller `USAGE` and
`CREATE`. Store creator OID, relation OID, stream OID, and immutable provenance
needed to reject name reuse.

### LSEC-14: Restore and drop authorization

Restore requires ownership of the destination stream table and `SELECT` on the
snapshot relation. Snapshot drop requires snapshot ownership or superuser.
Resolve both objects canonically before private mutation.

### LSEC-15: Transfer and stale-binding behavior

Changing stream ownership leaves existing snapshots with their documented
owners. Renamed, dropped, or recreated snapshot relations fail with a specific
provenance error. Cleanup is idempotent and cannot remove an unrelated object
that reused a name.

## Required tests

- Default and custom-schema creation cover allowed and denied `USAGE`, `CREATE`,
  ownership, quoted identifiers, and schema shadowing.
- Restore covers destination owner versus non-owner, snapshot `SELECT` grant and
  revocation, RLS-visible data, mismatched source, and owner transfer.
- Drop covers snapshot owner, stream owner who does not own the snapshot,
  superuser, stale OID, renamed relation, and name reuse.
- Failure injection between relation creation, ownership transfer, provenance
  insert, restore, and cleanup proves transaction rollback.
- Concurrent create, restore, drop, rename, and stream-owner transfer use
  deterministic synchronization and leave no orphaned private rows.
- Fresh-install and v0.87.10 upgrade tests verify provenance constraints,
  ownership, ACL retention, and cleanup of legacy metadata.

## Exit criteria

- [x] Caller-selected schemas never receive definer-equivalent authority.
- [x] Every snapshot relation has an explicit owner and immutable provenance.
- [x] Restore checks both destination ownership and snapshot access.
- [x] Transfer and name reuse cannot redirect snapshot operations.
- [x] Failed and concurrent operations leave no orphaned or partial state.
- [x] Security, rollback, concurrency, upgrade, and full repository CI gates pass.
