WebRTC DataChannels
The pipe that carries file bytes between browsers after ICE succeeds. Ordered, reliable channels are used for Share.
🔐 Peer-to-peer · end-to-end encrypted
Files transfer browser-to-browser. Nothing is stored on this server. No account required. No size limit beyond your connection and device memory.
Open on another device
Point your phone camera at the white square. This opens the public Share page—not a temporary room.
https://share.88-222-244-146.nip.io/Tap to choose files Click to browse or drag files or folders here
A share link appears next Pick files or folders — a share link will appear for the receiver
Folders are read and zipped in your browser only. The server only helps with the connection handshake — it never sees file contents.
💡 Tip: drag-and-drop a folder onto the box above if you prefer not to use Choose folder.
Your files are not stored or uploaded to our server. Folder ZIP runs in the sender’s browser; transfer stays peer-to-peer (P2P). The server only brokers the WebRTC handshake (and optional TURN relay of encrypted bytes) — it never keeps Share file contents.
Folder selected: ZIP packaging is required and will run in this browser when transfer starts. File contents stay on your device until they go directly to the receiver — not to this server.
WebRTC always uses DTLS. AES-256-GCM adds file encryption on top. Without a passphrase, a one-time key is put in the link fragment (not sent to this server).
📱 Scan only the white square above (or enlarge it)
Point your camera at the code — tap outside or ✕ to close
Full link
one · max 10 million
Link expires in —
Receivers completed: 0 / 1 · Status: waiting
⏳ Waiting for receiver to open the link…
0%
🔒 P2P file/folder stream — file bytes stay peer-to-peer. Nothing is stored on the server; only a small signaling handshake goes through Tunnel.
Stream your camera and microphone peer-to-peer to one viewer. Video and audio stay between browsers — this server only helps with the handshake (and optional TURN relay). Nothing is recorded here.
Optional. When enabled, the viewer must enter this passphrase before the live stream starts. The passphrase is never put in the link.
When on, the viewer can save video and audio on their own device.
When on, the viewer can share their camera and mic with you. You can turn this on anytime during the stream.
Press Start to allow camera and microphone access, then share the link with one viewer.
Camera off
Viewer camera is off
🔒 P2P stream — video and audio stay peer-to-peer. Nothing is stored on the server; only a small signaling handshake goes through Tunnel.
Waiting for viewer…
📱 Scan to open the live stream
💡 Keep this tab open while streaming. Closing it ends the live feed for the viewer. Your browser will ask for camera and microphone permission.
ℹ️ One viewer at a time. The stream uses WebRTC DTLS encryption; it is not uploaded to this server.
Use the P2P CLI on a Linux or Mac host to send files to a browser. Same WebRTC flow as this page — nothing is uploaded to this server. Keep the terminal process running until the download finishes.
Required for the native WebRTC module (node-datachannel).
node -v
# v18.20.0 or newer
Portable source ZIP (no node_modules). Install dependencies on the destination host so native binaries match its OS and CPU.
⬇️ Download tunnel-share-linux.zip
From a Linux or Mac terminal (use -O so the file is saved as a .zip):
wget -O tunnel-share-linux.zip 'https://share.88-222-244-146.nip.io/download/share-client.php'
# or:
curl -fsSL -o tunnel-share-linux.zip 'https://share.88-222-244-146.nip.io/download/share-client.php'
unzip tunnel-share-linux.zip -d tunnel-share
cd tunnel-share
./install.sh
# Manual alternative:
npm ci --omit=dev
The package includes defaults.json pointing at this server
((https://share.88-222-244-146.nip.io).
Override with --api if needed. Folders are zipped in the CLI
(same as the browser) before peer-to-peer transfer — the server never sees contents.
./share.sh ./report.pdf
# Share a folder (zipped locally with paths preserved):
./share.sh ./photos
# Direct Node alternative:
node bin/tunnel-share.js ./report.pdf
# Override the packaged server:
node bin/tunnel-share.js ./report.pdf --api https://share.88-222-244-146.nip.io
Copy the printed link into a browser. Default links include an AES key in the
#k=… fragment. Press Ctrl+C to revoke the room.
# Passphrase mode (receiver enters the same passphrase)
./share.sh ./secret.zip --password 'your-passphrase'
# Folder share (ZIP in CLI, browser can extract)
./share.sh ./photos
# Allow up to 3 sequential receivers
./share.sh ./demo.bin --max-receivers 3
# Quiet mode — print only the share URL
./share.sh ./file.bin -q
How Share works under the hood, what this server does (and does not) see, and why you can trust a peer-to-peer transfer for large private files.
After both browsers connect, file bytes travel over WebRTC—not through a normal HTTP upload to this website. The server helps with the handshake (and sometimes NAT), then gets out of the way.
sequenceDiagram
participant S as Sender
participant W as Server
participant R as Receiver
S->>W: Create room and share link
R->>W: Open link
S->>W: Signaling offer and ICE
R->>W: Signaling answer and ICE
Note over S,R: WebRTC DataChannel with DTLS
S->>R: File bytes peer to peer
Note over S,R: Optional TURN if NAT blocks direct path
R->>R: Assemble in OPFS or Blob and save
Note over S,R: Close tabs and room disappearsUseful for confidence: page load and signaling are expected; your file contents are not stored in Share’s database or disk as an upload.
flowchart LR
subgraph serverRole [On this server]
Page["Share page and config"]
Signal["WebSocket signaling"]
Turn["TURN NAT relay"]
end
Sender --> Page
Receiver --> Page
Sender --> Signal
Receiver --> Signal
Sender -->|"WebRTC file bytes"| Receiver
Sender -.-> Turn
Turn -.-> ReceiverNo. Share does not upload your files into cloud storage on this host. The server delivers the page, helps peers find each other over a WebSocket (signaling), and may run a TURN relay if a direct peer connection cannot be made. Even with TURN, bytes are not kept as a Share “download archive”—they are relayed for the session, then gone.
That prompt is from your browser (for example Chrome), not an upload form to this server. Allow only grants this webpage running on your device permission to read the folder you picked so it can zip it locally. The site name in the dialog is the page origin; it does not mean Hostinger or this VPS receives the files. After you allow, Share zips in your browser and sends peer-to-peer. You can also drag-and-drop a folder to skip the Choose folder picker.
Yes. Drag a folder onto the drop zone (or use Choose folder in Chromium). Your browser may ask permission to read that folder on your device — that is local access for this page’s JavaScript, not an upload. Share zips the folder in the sender browser (paths preserved), then sends it peer-to-peer. This server only helps peers find each other. The receiver can choose a destination folder to extract into, or download the ZIP instead.
The live connection is between the two browsers. Closing either side tears down the WebRTC session, so the transfer stops. Leave sender and receiver tabs open until progress reaches 100% and the file is saved.
Signaling (WebSocket) is only for setup and control messages. Once the DataChannel is open, file bytes keep flowing even if the signaling socket briefly drops (common on mobile networks). The UI reconnects signaling in the background.
Share links that include an AES key in #k=… make a denser QR.
Use Enlarge QR for a fullscreen white target, or
Download QR and open the PNG fullscreen. Aim only at the white square—
buttons sit outside that tile on purpose.
The file was assembled in the browser first. Some desktop browsers fail if temporary storage is cleaned too early. Use Tap to save on the receiver page— that starts a durable local save (and can open the system Save dialog on Chromium). Mobile browsers usually handle the same flow more reliably.
For large files, Share can split the payload into segments (you choose the size in MB), send up to four WebRTC streams in parallel, checkpoint progress, and resume after a drop. The receiver writes segments into browser storage (OPFS when available), verifies checksums, then merges into one file. The overall progress bar stays; stream rows show each parallel path.
It sets how large each piece is (16–512 MB, default 100). Smaller pieces resume more finely after a network blip; larger pieces mean fewer segments. The sender chooses this; it is sent to the receiver in the transfer plan.
WebRTC always protects the transport with DTLS. You can also enable AES-256-GCM so
file bytes are encrypted before they hit the DataChannel—either with a one-time key
in the link fragment (#k=…) or a shared passphrase the receiver types in.
The sender can see public IP, approximate location, and browser details used for the session. Precise GPS is optional and only shared if the receiver taps that option. When both sides have usable coordinates (IP geolocation or optional GPS), the sender panel also shows an estimated distance in miles and kilometers with place names and lat/lon codes — this is approximate and omitted if either location is unavailable.
No. Live Camera uses the same peer-to-peer WebRTC path as file Share: your browser captures camera and microphone, then streams encrypted media (DTLS) directly to the viewer. This server only helps with signaling (and optional TURN relay). It does not record, store, or archive the video or audio. Closing either tab ends the stream.
Yes. Open the API tab for the full guide.
Fetch GET /api/v1/share/config.php, connect to the WebSocket in
wsUrl, join a room, exchange WebRTC signaling, then send file bytes
on a DataChannel. Machine-readable OpenAPI:
/openapi/share.openapi.json.
File bytes are not uploaded to this server.
The pipe that carries file bytes between browsers after ICE succeeds. Ordered, reliable channels are used for Share.
Signaling only: room join, offers, answers, ICE candidates, and a few control messages—not the file contents path.
STUN discovers how to reach you through NAT. TURN relays encrypted media when a direct peer path is blocked.
On the receiver, large accelerated files land in Origin Private File System (or memory Blobs), then you save locally—no server merge step.
SHA-256 covers each accelerated segment and the full file so both sides can confirm integrity before you trust the download.
Compared with typical “upload then download from the cloud” share links:
Use these public endpoints and protocols to send or receive files from your own app. HTTP only bootstraps config. Rooms and WebRTC signaling use a JSON WebSocket. File bytes travel peer-to-peer over a WebRTC DataChannel — this server does not store uploads.
HTTP bootstraps config. The WebSocket creates and joins rooms and relays SDP/ICE. File bytes never go through a REST upload — they travel on a WebRTC DataChannel.
sequenceDiagram
participant App as ThirdPartyApp
participant Http as ShareHTTP
participant Ws as SignalingWS
participant Peer as RemotePeer
App->>Http: GET share config
App->>Ws: join room role sender or receiver
App->>Ws: offer answer ice
Ws->>Peer: relay signaling
App->>Peer: WebRTC DataChannel file bytesDocumented in OpenAPI 3.1. Paths include the .php filename.
/api/v1/share/config.php
Bootstrap: wsUrl, publicUrl, iceServers, maxChunkSize, room defaults
{"ok":true,"enabled":true,"wsUrl":"wss://…/ws","publicUrl":"https://…/share.php","iceServers":[…],"maxChunkSize":16384,"roomIdLength":8,"roomTtlMs":3600000}
/api/v1/share/shorten.php
Create a short /s/{code} link for a same-host Share URL (stores full target including #k= until TTL)
{"url":"https://…/share.php?room=Ab3Xk9Qm#k=…","room":"Ab3Xk9Qm","ttlMs":3600000}
{"ok":true,"code":"aB3xK9mQ","shortUrl":"https://…/s/aB3xK9mQ","expiresAt":"…","roomId":"Ab3Xk9Qm"}
/s/{code}
Resolve short link → Share URL (HTML/JS redirect when #k= is present)
/api/v1/share/shorten-revoke.php
Delete short links for a room after revoke
{"room":"Ab3Xk9Qm"}
/api/v1/share/openapi.php
OpenAPI 3.1 JSON (CORS). Same document as the static file, with this host injected as servers[0].
/openapi/share.openapi.json
Static OpenAPI source of truth (crawlable under /openapi/).
/download/share-client.php
Optional portable Node CLI ZIP for Linux/macOS hosts (same protocol as the browser).
Connect to wsUrl from config (typically wss://…/ws).
Send JSON text frames. The first sender join creates the room.
join — { type, room, role: "sender"|"receiver", maxReceivers?, fingerprint? } — sender creates the room if missingoffer — { type, sdp, to? } — WebRTC SDP offer relayanswer — { type, sdp, to? } — WebRTC SDP answer relayice — { type, candidate, to? } — ICE candidate relayleave — { type } — leave the current roomset-max — { type, maxReceivers } — sender updates max receivers (1–10000000)transfer-complete — { type } — sender marks a receiver transfer finishedreset-count — { type } — sender resets completed receiver countrevoke-room — { type } — sender destroys the roomcancel-transfer — { type } — cancel in-flight transferreceiver-info — { type, info } — optional receiver metadata for the sender UIrequest-offer — { type } — receiver asks sender to (re)send an offerjoined — Ack with peerId, role, completed/max, expiresAt, roomTtlMs, full/active flagsroom-stats — Updated completed/max/active/full counterspeer-joined — Remote peer connectedpeer-left — Remote peer disconnectedpeer-info — Relay of receiver-info to senderself-info — Sender-only: public IP + IP geolocation for distance estimatesoffer / answer / ice — Relayed WebRTC signaling from the other peerrequest-offer — Relayed request for a fresh offerroom-full / room-revoked / room-expired — Room lifecycle errorsreset-ok / transfer-cancelled — Control acknowledgementserror — { type, message } — invalid message, rate limit, etc.
After ICE succeeds, open an ordered reliable DataChannel. Text control messages use
a prefix + optional JSON; file payload is binary (chunked to maxChunkSize).
ENC_CONFIG:{json} — Sender announces encryption mode (dtls or aes-gcm + passphrase/fragment key params)ENC_READY:{json} — Receiver confirms encryption handshakeENC_FAIL:{json} — Receiver rejects passphrase / fragment keyMETA:{json} — Per-file metadata: name, size, type, optional sha256binary chunks — Raw ArrayBuffer / Blob pieces up to maxChunkSize (encrypted if AES mode)FILE_DONE — Current file finishedDONE:N — All N files finished from senderRECV_DONE / RECV_DONE:{json} — Receiver acknowledgement + optional integrity summaryFILE_PLAN:{json} — Accelerated mode: segment plan (size, sha256, segmentSize, parallel)SEG_META:{json} / SEG_DONE / SEG_ACK: — Accelerated per-segment framing over parallel channelsRESUME:{json} — Accelerated resume checkpoint from receiverFetch bootstrap config:
curl -sS 'https://share.88-222-244-146.nip.io/api/v1/share/config.php'
Minimal browser/Node join (signaling only — add WebRTC next):
const cfg = await fetch('/api/v1/share/config.php').then((r) => r.json());
if (!cfg.ok || !cfg.enabled) throw new Error(cfg.error || 'Share disabled');
const room = crypto.randomUUID().replace(/-/g, '').slice(0, cfg.roomIdLength);
const ws = new WebSocket(cfg.wsUrl);
ws.onopen = () => {
ws.send(JSON.stringify({
type: 'join',
room,
role: 'sender', // or 'receiver'
maxReceivers: 1,
}));
};
ws.onmessage = (ev) => {
const msg = JSON.parse(ev.data);
console.log(msg.type, msg);
// After peer-joined: create RTCPeerConnection({ iceServers: cfg.iceServers }),
// open a DataChannel, exchange offer/answer/ice over this WebSocket,
// then send ENC_CONFIG / META / binary chunks / FILE_DONE / DONE:N.
};
const shareLink = cfg.publicUrl + '?room=' + encodeURIComponent(room);
Prefer a ready-made sender? Download the Linux/macOS CLI package or use the Browser tab on this page.
🔄 Connecting to sender…
0%
🔒 P2P file/folder stream — file bytes stay peer-to-peer. Nothing is stored on the server; only a small signaling handshake goes through Tunnel.
💡 Keep this tab open until the download finishes. Closing either side ends the transfer.
ℹ️ The sender can see your public IP, approximate location, and browser details. Precise GPS is optional.
🔄 Connecting to sender…
Waiting for sender…
Start your camera to appear here
🔒 P2P stream — video and audio stay peer-to-peer. Nothing is stored on the server; only a small signaling handshake goes through Tunnel.
Waiting for connection…
💡 Keep this tab open to watch the stream. Closing either side ends the live feed.
ℹ️ Video and audio are peer-to-peer (WebRTC DTLS). Recording on this device is only available when the sender allows it.
Growing with internet users — real peer-to-peer transfers, no cloud uploads.
./install.sh once. It checks Node.js and installs WebRTC dependencies for that machine../share.sh ./file or ./share.sh ./folder (or use Node directly). Folders are zipped in the CLI. The CLI creates an encrypted temporary room and prints a browser link.