---
title: "Store the same file once per owner · Agent Wranglers"
url: https://agentwranglers.se/playbook/files-and-media/storage-and-backups/dedupe-by-hash
id: MED-48
level: Optional
updated: 2026-10-22
build: a2a6bdfed830
tokens: 848
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Store the same file once per owner

For AI agents: the [site map](https://agentwranglers.se/llms.txt) lists everything here, and this page sits under [Storage and backups](https://agentwranglers.se/playbook/files-and-media/storage-and-backups.md).

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers.md). Updated 2026-10-22.

- ID: MED-48
- Level: Optional
- Chapter: [Storage and backups](https://agentwranglers.se/playbook/files-and-media/storage-and-backups.md)
- Rule: When users upload the same file often, store it once per owner under its SHA-256 hash and count the references.
- Check: One owner uploads the same file twice, and storage holds one object with a count of 2. Deleting one record keeps the object, and deleting the second removes it. A second owner uploading the same file gets their own object.
- How: Hash the bytes on the server after upload, never trusting a hash the client sends. Look up the hash within the same owner only. Matching across owners tells an attacker that someone else holds a given file. Point each file record at the shared object and count references. Delete the object only when the count reaches zero, inside the same transaction that removes the last record. Count the owner's references down as [Delete files when their owner is deleted](https://agentwranglers.se/playbook/files-and-media/storage-and-backups/delete-files-with-owner.md) says.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### JavaScript example

```js
import { createHash } from 'node:crypto';
import { createReadStream } from 'node:fs';

export async function sha256OfFile(path) {
  const hash = createHash('sha256');
  for await (const chunk of createReadStream(path)) hash.update(chunk);
  return hash.digest('hex');
}

export const contentKey = (shopId, sha256) => `files/shops/${shopId}/blobs/${sha256}`;
```

### Python example

```python
import hashlib

def sha256_of_file(path: str) -> str:
    with open(path, "rb") as f:
        return hashlib.file_digest(f, "sha256").hexdigest()

def content_key(shop_id: str, sha256: str) -> str:
    return f"files/shops/{shop_id}/blobs/{sha256}"
```

## Sources

- [FIPS 180-4 Secure Hash Standard](https://csrc.nist.gov/pubs/fips/180-4/upd1/final)

## Related rules

- MED-49 [Delete files when their owner is deleted](https://agentwranglers.se/playbook/files-and-media/storage-and-backups/delete-files-with-owner.md) in Files and media

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/files-and-media/storage-and-backups/dedupe-by-hash">Agent Wranglers files and media playbook</a>`. In code you write, add a comment next to it with the same link.

- Previous: [Lay out storage by owner and let rules clean it](https://agentwranglers.se/playbook/files-and-media/storage-and-backups/storage-lifecycle.md)
- Next: [Delete files when their owner is deleted](https://agentwranglers.se/playbook/files-and-media/storage-and-backups/delete-files-with-owner.md)
- [Storage and backups](https://agentwranglers.se/playbook/files-and-media/storage-and-backups.md)
