Building a USB-to-IPFS Dead Drop: Auto-Publishing Files to the Distributed Web
Hook
What if you could turn any USB stick into an instant publishing platform by simply plugging it into a box, with no software installation, no accounts, and no way to trace who uploaded what?
Context
The concept of a "dead drop" comes from Cold War espionage—a secret location where spies could leave information without meeting face-to-face. In 2010, artist Aram Bartholl adapted this concept for the digital age by embedding USB sticks into walls, creating physical dead drops where strangers could anonymously share files. But these physical drops had a fatal limitation: only one person could access the USB at a time, and you had to physically visit the location.
The ipfs-deaddrop project, created by the c-base hackerspace in Berlin, bridges this gap by combining the anonymity of physical dead drops with the reach of the decentralized web. When you plug a USB stick into an ipfs-deaddrop device, it automatically uploads everything to IPFS (InterPlanetary File System) and displays a QR code linking to the content. Anyone who scans that code can access the files from anywhere in the world, without knowing who uploaded them or where the original device is located. It's a physical-to-digital publishing pipeline that requires zero technical knowledge from the user and leaves no traditional audit trail.
Technical Insight
At its core, ipfs-deaddrop is a clever orchestration of three Linux subsystems: udev for hardware detection, systemd for process management, and the IPFS daemon for distributed storage. The architecture is deceptively simple but reveals thoughtful design decisions that make the user experience feel almost magical.
The entry point is a udev rule that watches for USB storage devices. When you plug in a USB stick, udev doesn't just mount it—it triggers a systemd service that runs the main Python script with elevated privileges. Here's what a typical udev rule looks like for this system:
ACTION=="add", SUBSYSTEM=="block", ENV{ID_FS_TYPE}=="vfat", TAG+="systemd", ENV{SYSTEMD_WANTS}="ipfs-deaddrop@%k.service"
This rule specifically targets FAT-formatted drives (ID_FS_TYPE=="vfat"), which is both a feature and a limitation. FAT is universally compatible across Windows, Mac, and Linux, meaning users don't need to think about filesystem formats. The %k parameter passes the kernel device name (like sdb1) to the systemd service, allowing multiple USB sticks to be processed simultaneously without conflicts.
The Python script itself follows a straightforward pipeline: mount → copy → upload → unmount → display. The interesting part is how it handles the IPFS upload. Rather than uploading individual files, the script adds the entire USB contents as a directory structure using ipfs add -r, which preserves the original folder hierarchy and generates a single content identifier (CID) for the root directory:
import subprocess
import os
def upload_to_ipfs(mount_point):
result = subprocess.run(
['ipfs', 'add', '-r', '-Q', mount_point],
capture_output=True,
text=True
)
root_hash = result.stdout.strip()
return root_hash
def generate_gateway_url(ipfs_hash):
return f"https://ipfs.io/ipfs/{ipfs_hash}"
The -Q flag is critical here—it tells IPFS to only output the final root hash, not the hash of every individual file, making the output parseable. This hash is content-addressed, meaning the same files will always produce the same hash, and any modification will produce a different hash. This makes the system inherently tamper-evident.
One of the most elegant aspects is the QR code generation. The system doesn't just display text—it generates a scannable QR code containing the IPFS gateway URL and displays it on a web interface. This transforms a potentially technical hash string into something anyone with a smartphone can use. The web interface updates in real-time using basic HTTP polling or WebSocket connections, so multiple people can watch as files are uploaded and immediately scan the resulting code.
The optional ipfs-ringpin integration adds another layer of resilience. When multiple ipfs-deaddrop installations are configured in a ring topology, they automatically discover and pin each other's uploads. This creates a distributed backup network where content persists even if the original uploading node goes offline. The ring uses IPFS's pubsub mechanism to broadcast new hashes:
def broadcast_hash(ipfs_hash):
subprocess.run([
'ipfs', 'pubsub', 'pub',
'deaddrop-ring',
ipfs_hash
])
def listen_for_hashes():
process = subprocess.Popen(
['ipfs', 'pubsub', 'sub', 'deaddrop-ring'],
stdout=subprocess.PIPE
)
for line in process.stdout:
remote_hash = line.decode().strip()
pin_content(remote_hash)
This creates a cooperative network where hackerspaces and makerspaces can support each other's content without central coordination. It's peer-to-peer infrastructure in the truest sense—no single point of failure, no centralized server to shut down.
Gotcha
The biggest gotcha is that this system has absolutely no content moderation. Whatever goes on that USB stick gets uploaded to IPFS and potentially pinned by every node in your ring network. There's no preview, no filtering, no allowlist of file types. If someone uploads illegal content, it's now distributed across multiple IPFS nodes, and removing it requires manually unpinning from every single node. IPFS's content addressing means you can't silently replace or modify content after the fact—the hash is the content's fingerprint.
The FAT filesystem restriction is more limiting than it first appears. While FAT is universal, it has a 4GB file size limit, no support for Unix permissions, and mangles long filenames. If someone tries to upload a large video file or a dataset that exceeds 4GB, it simply won't work, and the system provides no clear error message to the user. Additionally, the requirement for a working IPFS node means this isn't a plug-and-play solution. You need to configure IPFS, ensure it stays running, allocate sufficient storage for pinned content, and potentially configure firewall rules for peer discovery. For hackerspaces with existing infrastructure this is manageable, but it's not something you can deploy at a public library without dedicated technical support. The persistence question is also real: content only stays on IPFS as long as someone pins it. If your node goes offline and no one else has pinned your uploads, they'll eventually be garbage collected from the network.
Verdict
Use if: You're running a hackerspace, art installation, or community space where you want to enable truly anonymous, friction-free publishing to the decentralized web. This is perfect for environments where you trust your community enough to handle the content moderation responsibility socially rather than technically, and where you have the technical chops to maintain an IPFS node. It's also ideal if you want to make a statement about open access and censorship resistance—this is as much an artistic/political tool as a technical one. Skip if: You need any form of content control, accountability, or guaranteed long-term persistence. If you're deploying in a public space without community oversight, if you need to support large files or non-FAT filesystems, or if you don't have someone comfortable debugging Linux system services and IPFS networking issues, choose a more managed solution. This is a power tool for communities that understand both its capabilities and its risks.