Skip to main content
In most network conditions, HashDrop establishes a direct WebRTC data channel between peers. When that is not possible — for example, when both peers sit behind symmetric NAT or a firewall that blocks UDP — HashDrop offers a relay fallback that routes the file through the server. This page explains when relay mode is triggered, how it works, and what limits apply.

When relay mode is used

Relay mode is triggered when WebRTC ICE negotiation fails to produce a usable direct or STUN-assisted candidate pair. This typically happens when:
  • Both sender and receiver are on enterprise or mobile networks with symmetric NAT
  • A firewall blocks all UDP traffic and TURN-over-TCP is also unavailable
  • The TURN server credentials are missing or expired
In relay mode, the sender uploads the file to the HashDrop server via an HTTP POST request. The receiver downloads and claims it via a GET request. The signaling server’s Warp Code is reused as the relay key.

How relay mode works

  1. The sender’s client detects that a direct WebRTC connection cannot be established.
  2. The sender POSTs the file(s) to /api/relay/[code], tagged with the Warp Code.
  3. The server stores the file in the OS temporary directory with a 15-minute expiry.
  4. The receiver enters the same Warp Code. The client GETs /api/relay/[code], which returns the file and marks the entry as claimed.
  5. Once claimed, the relay entry is immediately deleted from disk.

Data structures

The relay store (src/app/api/relay/store.ts) uses two TypeScript interfaces to represent a stored transfer:
export interface RelayFile {
  name: string;
  mimeType: string;
  size: number;
  data: Buffer;
}

export interface RelayEntry {
  files: RelayFile[];
  expiresAt: number;
  claimed: boolean;
}
RelayEntry.claimed is set to true the moment the receiver successfully downloads the file. Any subsequent request for the same code returns a 404. expiresAt is set to Date.now() + TTL_MS at upload time.

TTL and cleanup

export const TTL_MS = 15 * 60 * 1000; // 15 minutes
Relay entries expire 15 minutes after upload. A sweep function (sweepRelayEntries) scans the relay directory and deletes any entry whose expiresAt timestamp has passed. Entries that cannot be parsed are also removed during sweeps.
The relay store writes files to the OS temporary directory: path.join(os.tmpdir(), 'hashdrop-relay-store'). On Linux this is typically /tmp/hashdrop-relay-store. Files in this location are not persisted across server restarts and may be cleared by the OS independently of HashDrop’s TTL sweep.

API endpoints

MethodPathDescription
POST/api/relay/[code]Upload one or more files for the given Warp Code. Sets expiresAt and claimed: false.
GET/api/relay/[code]Download the file(s) and atomically mark the entry as claimed. Deletes the entry after delivery.
DELETE/api/relay/[code]Explicitly remove a relay entry before it expires (e.g., sender cancels).
Relay mode offers no permanent storage. Files are held in the server’s temp directory for a maximum of 15 minutes and are deleted immediately after the receiver claims them. Do not use relay mode for files that need to be retrieved more than once or after the TTL has elapsed.

Build docs developers (and LLMs) love