An encrypted file can still expose which customer requested it, when they requested it, and whether two applications share the same object. Teams often discover those leaks after they have published content identifiers or promised deletion they cannot verify. For a 15–30 person engineering organisation, I would keep sensitive user data off public decentralized storage by default, because encryption solves less of the privacy problem than the storage decision creates.
Encryption does not hide the access patterns your architecture publishes
With IPFS, a CID identifies content; it is not an access-control credential. Encrypting a file before adding it through Kubo can protect its contents from someone who obtains only the ciphertext, but the CID, retrieval requests, gateway logs, and surrounding application records can still reveal activity. If a tenant ID, document title, or email address appears in a path or metadata field, encryption of the file body cannot undo that disclosure.
The problem becomes easier to miss when an application treats a gateway as a transparent transport. A public gateway may see requesting IP addresses and CIDs, while an application-operated gateway can see account IDs or authorization headers if the team sends them there. A third-party pinning service may also learn which objects the organisation keeps available. None of those parties needs the decryption key to infer that the same object was requested repeatedly.
Content addressing creates a second trap: identical plaintext processed in the same way can produce the same identifier. Encrypting before upload can reduce that correlation, but only if the encryption design produces distinct ciphertext where unlinkability is required. AES-256-GCM with a fresh, unique nonce for each encryption is a defensible component; reusing a nonce with the same key is unsafe because it can compromise confidentiality and integrity. The team still needs to decide whether deliberate deduplication is worth making two uploads linkable.
I would use Decentralized Storage and Data Networks for Developers to inventory retrieval paths, but I would challenge any design review that stops at “the payload is encrypted”: it leaves request metadata and key distribution outside the threat model. Draw the path from browser or service to gateway, provider, pinning service, and key service; then mark where each party can observe an account, IP address, CID, timestamp, or object size. That drawing is more useful to a tech lead than a blanket claim of end-to-end privacy because it names the observers who can make correlations.
For a pilot, make correlation measurable with synthetic requests rather than collecting more production IP addresses. The following Python 3.11 script reads JSON Lines with cid and client_ip fields and reports how many distinct clients requested each CID. Run it on synthetic gateway records with python3 audit.py sample.jsonl; a shared CID in the output is a prompt to inspect what an observer could infer, not evidence of a breach.
import json
import sys
from collections import defaultdict
seen = defaultdict(set)
with open(sys.argv[1], encoding="utf-8") as records:
for line in records:
row = json.loads(line)
if row.get("cid") and row.get("client_ip"):
seen[row["cid"]].add(row["client_ip"])
for cid, clients in sorted(seen.items(), key=lambda item: -len(item[1]))[:10]:
print(cid, len(clients))
A deletion promise is false until every copy and key has an owner
Deleting an application database row does not delete a pinned IPFS block, because the database and the peers holding the block have separate retention controls. Unpinning through Kubo removes one operator’s commitment to retain data; it does not command other peers, gateways, or backups to erase copies they already acquired. Filecoin storage deals add another retention relationship to account for, rather than a universal delete button.
Cryptographic erasure can make retained ciphertext unusable to someone who lacks a key, but it cannot retract plaintext already downloaded or keys already copied. That distinction matters when a product requirement says “delete the document”: the team must specify whether it promises removal from systems it controls, loss of decryptability for future readers, removal from third parties, or all three. Those are different claims because the organisation has different authority over each copy.
I would not place sensitive user documents on a public IPFS retrieval path and promise prompt global deletion, because neither an unpin operation nor key revocation can verify what independent peers retained. If distribution across independent operators is a product requirement, I would narrow the promise in product language and obtain a legal review before upload. Otherwise, choosing a storage design with controllable replication makes the deletion requirement easier to test.
Start with a proposed 30-day maximum for application-controlled backup retention, then adjust that value against contractual obligations and recovery needs; it is a policy setting, not a property of IPFS. Give each copy class an owner: origin, pinning provider, gateway cache, backup, client download, and observability pipeline. Record the deletion API or manual procedure for each class, along with the evidence it returns. An inventory without those procedures cannot substantiate a deletion claim because it describes locations without a way to change them.
Key rotation also needs a testable boundary. Use 15 minutes as an initial revocation objective to tune for newly authorized retrievals, and measure the interval from disabling a grant to the last successful fresh key request. AWS KMS or HashiCorp Vault can help manage key access, but neither can take back a data-encryption key that an authorized client already stored. A tabletop exercise should include that client, because excluding it would make the revocation result look stronger than the actual guarantee.
S3 wins by default for sensitive records; IPFS wins only when independent distribution is the requirement
The useful comparison is Amazon S3 with SSE-KMS against IPFS with Kubo and an independent pinning arrangement, not “centralized versus decentralized” in the abstract. S3 wins for sensitive application records when the organisation needs one accountable storage operator, enforceable bucket policies, lifecycle controls, and a deletion process it can inspect. Its costs are provider dependence, cloud charges, and the work of configuring IAM, KMS grants, replication, and logs correctly.
IPFS wins when independent parties must fetch and verify the same published content without trusting one organisation’s endpoint, because CIDs let each party check the bytes it received. Its costs are managing availability across pinning operators, securing keys outside the network, reviewing gateway exposure, and accepting that independently retained copies are outside the uploader’s direct control. Those costs are justified for a public release artifact more readily than for a customer’s private draft.
AWS publishes an S3 Standard durability design target of 99.999999999%, commonly called eleven nines; that vendor-published figure concerns object durability, not confidentiality, correct IAM configuration, or guaranteed deletion everywhere. Conversely, a CID’s cryptographic verification helps detect changed content, but it says nothing about who was entitled to fetch the content. Putting those properties in separate decision-record columns prevents a durability claim from being mistaken for a privacy claim.
I would treat Decentralized Storage Networks for Secure App Data as a starting point for testing security assumptions, not as evidence that a decentralized location makes app data safer. For an organisation of this size, splitting ownership among an API team, a key-service team, gateway operators, and pinning providers creates more boundaries to audit; that is a reason to demand a distribution requirement before accepting the split.
Use a concrete decision rule: if independent verification and multi-operator publication are mandatory, publish encrypted or deliberately public artifacts through IPFS after reviewing metadata exposure. If the requirement is private storage for authenticated users, begin with S3, restrictive IAM policies, SSE-KMS, and an explicit lifecycle configuration; add decentralized distribution only for a subset whose threat model benefits from it. The rule is opinionated because an extra network does not remove the need for conventional authorization and key management.
Privacy failures surface in drills before they surface in uptime charts
A storage proof of concept usually demonstrates a successful upload and retrieval, which tests availability but not what an unauthorized observer learns. Run a drill in which one engineer acts as a gateway operator, one as a former authorized client, and one as the application operator. Give each only the logs, CIDs, and credentials their role would possess. Ask what each can infer about tenant activity and what each can still retrieve after access is revoked.
Set an initial 24-hour takedown target for application-controlled copies, with the understanding that this is a value to tune after rehearsals rather than a promise about independent peers. Record measured time from the deletion request to removal from the origin, cache, and controlled pins separately. Keep p95 retrieval latency as a separate measured metric: fast reads do not compensate for a failed takedown, and slow reads do not by themselves prove a privacy failure.
Include OAuth 2.0 token expiry, KMS grant removal, CDN cache invalidation, and Kubo unpinning in the drill if the design uses them. Test a client that cached ciphertext and its key before revocation, because that client may retain readable data even when every server-side access check now rejects it. Also test a client that cached only ciphertext; if it can still obtain a key, the revocation path has not achieved its stated objective.
Do not put raw gateway access logs into a long-lived analytics store merely to study the pilot, because that would create another sensitive record of who requested which CID. Use synthetic traffic for correlation tests, restrict production log access, and define retention for the logs themselves. The security review is complete only when the team can say which observations are necessary to operate the service and which observations it has chosen not to keep.
The first decision should be a deletion rehearsal, not a storage migration
Pick one representative private object and document every place its ciphertext, CID, access log, and key can persist. Then revoke access, request deletion, and time what your team can actually remove. If an independent copy survives, change the product promise or the storage design before shipping. That small rehearsal gives the tech lead a defensible boundary between data the organisation controls and data it merely hopes will disappear.


