# Private file sharing end to end

Sharing a private file is a workflow, not a single API call: upload with the right policy, record the path, serve through minted URLs, and revoke by changing the policy. Agents that store the signed URL in the database build links that rot on expiry.

## Checkable procedure

1. Create a private bucket with an INSERT policy letting users upload to their own path prefix and a SELECT policy for the sharing rules (owner plus explicit shares).
2. Upload from the client (or via `createSignedUploadUrl` minted server-side for extra control). Store only the bucket and path in your database, never the signed URL.
3. When serving, mint `createSignedUrl(path, expiresIn)` at request time, server-side, after checking the requester may access the file. Minutes-long expiry for downloads.
4. For sharing with another user, add a share row (file id, grantee user id) and include it in the SELECT policy. Revoking the share row instantly revokes access, including outstanding signed URLs once they expire.
5. Never proxy file bytes through your app server unless you must transform them. Signed URLs let the client download straight from storage.

## Ordering constraints

Bucket and policies before the first upload. The share model before the sharing UI. If the policy cannot express a share, the UI will promise access it cannot enforce.

## Verification

Upload as user A, share with user B, confirm B can download via a minted URL and user C cannot. Revoke the share and confirm B's next mint fails. Confirm a minted URL 403s after expiry.