{"id":13622665,"url":"https://github.com/gfodor/p2pcf","last_synced_at":"2026-01-16T15:31:59.763Z","repository":{"id":50538354,"uuid":"518319075","full_name":"gfodor/p2pcf","owner":"gfodor","description":"Low cost, low effort P2P WebRTC serverless signalling using Cloudflare Workers","archived":false,"fork":false,"pushed_at":"2023-11-23T00:13:10.000Z","size":1687,"stargazers_count":1024,"open_issues_count":8,"forks_count":63,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-08-09T12:52:33.279Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gfodor.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2022-07-27T05:19:02.000Z","updated_at":"2025-08-01T11:17:55.000Z","dependencies_parsed_at":"2024-01-14T06:04:12.488Z","dependency_job_id":"bbde3990-f1ca-4932-ac31-6d97181ec08a","html_url":"https://github.com/gfodor/p2pcf","commit_stats":{"total_commits":159,"total_committers":3,"mean_commits":53.0,"dds":"0.037735849056603765","last_synced_commit":"44369f025493f5ed2e60c0962a50ccc70b2e3e4a"},"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"purl":"pkg:github/gfodor/p2pcf","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gfodor%2Fp2pcf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gfodor%2Fp2pcf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gfodor%2Fp2pcf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gfodor%2Fp2pcf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gfodor","download_url":"https://codeload.github.com/gfodor/p2pcf/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gfodor%2Fp2pcf/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28479409,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-16T11:59:17.896Z","status":"ssl_error","status_checked_at":"2026-01-16T11:55:55.838Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T21:01:22.470Z","updated_at":"2026-01-16T15:31:59.733Z","avatar_url":"https://github.com/gfodor.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"![image](https://user-images.githubusercontent.com/220020/181821538-4ace5e9d-1fa0-4146-881b-368bcb253f6c.png)\n\n\n# P2PCF\n\nP2PCF enables free (or cheap) serverless WebRTC signalling using a [Cloudflare worker](https://workers.cloudflare.com/) and a [Cloudflare R2](https://www.cloudflare.com/products/r2/) bucket. The API is inspired by [P2PT](https://github.com/subins2000/p2pt), but instead of using WebTorrent trackers, which may go down, a custom Cloudflare worker is provided whose level of I/O aims to be free for most use-cases, and otherwise very cheap.\n\nThe point is to allow people to deploy WebRTC-enabled applications without having to manage or worry (much) about a signalling server. Out of the box the library will \"just work\" using a free public worker (run by the author) that is subject to quota. However, [setting up your own worker](https://github.com/gfodor/p2pcf/blob/master/INSTALL.md) is easy and takes just a few minutes. Once it is deployed, signalling will work without any further maintenance.\n\nP2PCF also has some additional features:\n\n- Room-based keying for easy connection management + acquisition\n- Minimal initial signalling (1 or 2 signalling messages) using the technique [put together](https://twitter.com/evan_brass/status/1549078627282722816) by [@evan_brass](https://twitter.com/evan_brass)\n- Subsequent signalling over DataChannels\n- Efficient chunking + delivery of DataChannel messages that exceed the ~16k limit\n- Peers handed back from the API are [tiny-simple-peer](https://github.com/gfodor/tiny-simple-peer) instances which provides a simple API for interacting with the underlying PeerConnections (adding + removing media tracks, etc.)\n\n# Example + Usage\n\nOut of the box, P2PCF will use a free public worker.\n\nOnce you're ready, you should [set up your own worker on Cloudflare](https://github.com/gfodor/p2pcf/blob/master/INSTALL.md): https://github.com/gfodor/p2pcf/blob/master/INSTALL.md\n\nA basic chat + video sharing example demonstrates the library at https://gfodor.github.io/p2pcf-demo ([source](https://github.com/gfodor/p2pcf/blob/master/examples/basic-video-chat/index.js))\n\nBasic usage:\n\n```js\nimport P2PCF from 'p2pcf'\n\nconst client_id = 'MyUsername'\nconst room_id = 'MyRoom'\n\nconst p2pcf = new P2PCF(client_id, room_id, {\n  // Worker URL (optional) - if left out, will use a public worker\n  workerUrl: '\u003cyour worker url\u003e',\n\n  // STUN ICE servers (optional)\n  // If left out, will use public STUN from Google + Twilio\n  stunIceServers: { ... },\n  \n  // TURN ICE servers (optional)\n  // If left out, will use openrelay public TURN servers from metered.ca\n  turnIceServers: { ... },\n  \n  // Network change poll interval (milliseconds, optional, default: 15000, 15 seconds)\n  // Interval to poll STUN for network changes + reconnect\n  networkChangePollIntervalMs: ...,\n  \n  // State expiration interval (milliseconds, optional, default: 120000, 2 minutes)\n  // Timeout interval for peers during polling\n  stateExpirationIntervalMs: ...,\n  \n  // State heartbeat interval (milliseconds, optional, default: 30000, 30 seconds)\n  // Time before expiration to heartbeat\n  stateHeartbeatWindowMs: ...,\n  \n  // Fast polling duration (milliseconds, optional, default: 10000, 10 seconds)\n  // How long we run fast polling after a state transition\n  fastPollingDurationMs: ...,\n\n  // Fast polling rate (milliseconds, optional, default: 1500)\n  // Polling rate during state transitions\n  fastPollingRateMs: ...,\n\n  // Slow polling rate (milliseconds, optional, default: 5000, 1.5 seconds)\n  // Polling rate when there has been no recent activity\n  slowPollingRateMs: ...,\n\n  // Idle polling delay (milliseconds, optional, default: never)\n  // How long to wait for activity before switching to idle polling rate\n  idlePollingAfterMs: ...,\n\n  // Idle polling rate (milliseconds, optional, default: Infinity)\n  // Polling rate when there has been no activity for idlePollingAfterMs milliseconds\n  // Infinity will cause polling to stop, which is useful for idle clients left open.\n  idlePollingAfterMs: ...,\n\n  // Options to pass to RTCPeerConnection constructor (optional)\n  rtcPeerConnectionOptions: {},\n\n  // Proprietary constraints to pass to RTCPeerConnection constructor (optional)\n  rtcPeerConnectionProprietaryConstraints: {},\n\n  // SDP transform function (optional)\n  sdpTransform: sdp =\u003e sdp\n});\n\n// Start polling\np2pcf.start()\n\np2pcf.on('peerconnect', peer =\u003e {\n  // New peer connected\n  \n  // Peer is an instance of simple-peer (https://github.com/feross/simple-peer)\n  //\n  // The peer has two custom fields:\n  // - id (a per session unique id)\n  // - client_id (which was passed to their P2PCF constructor)\n  \n  console.log(\"New peer:\", peer.id, peer.client_id)\n  \n  peer.on('track', (track, stream) =\u003e {\n    // New media track + stream from peer\n  })\n  \n  // Add a media stream to the peer to start sending it\n  peer.addStream(new MediaStream(...))\n})\n\np2pcf.on('peerclose', peer =\u003e {\n  // Peer has disconnected\n})\n\np2pcf.on('msg', (peer, data) =\u003e {\n  // Received data from peer (data is an ArrayBuffer)\n})\n\n// Broadcast a message via data channel to all peers\np2pcf.broadcast(new ArrayBuffer(...))\n\n// To send a message via data channel to just one peer:\np2pcf.send(peer, new ArrayBuffer(...))\n\n// To stop polling + shut down (not necessary to call this typically, page transition suffices.)\np2pcf.destroy()\n```\n\n`stunIceServers` and `turnIceServers` are optional, but if provided, should be in the format of the [`iceServers` option](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/RTCPeerConnection#parameters) passed to `RTCPeerConnection`.\n\nWhen a new peer joins, it can take up to `slowPollingRateMs` before negotiation will begin. If you want peers to connect more quickly, you can adjust `slowPollingRateMs` but it will result in increased worker requests and R2 reads.\n\nNote that peers who are both on symmetric NATs (or one symmetric NAT + one port restricted NAT) will use TURN. If you do not specify a TURN server then the TURN server provided by [Open Relay](https://www.metered.ca/tools/openrelay/) will be used. It's estimated 8% of visitors require TURN.\n\n# Estimated Cloudflare Usage\n\nThe worker provides signalling via HTTP polling (with backoff when the room is idle), and each request to the server performs a small number of reads from R2. Each join of a peer to the room will do at least 1 write to R2 and up to N + 1 writes (one for each peer, and a metadata update) in the worst-case where all peers are behind symmetric NATs and need to perform bi-directional hole punching to establish their initial DataChannel. Subsequent renegoations are performed over the DataChannel and so do not incur any R2 writes. Clients also heartbeat to maintain livliness every 90 seconds, which incurs an additional write each time.\n\nR2's [free tier](https://developers.cloudflare.com/r2/platform/pricing/) offers 1M writes per month and 10M reads per month. Cloudflare workers offer ~3M [free requests](https://developers.cloudflare.com/workers/platform/pricing/) per month. In general, these free tiers should support any modest WebRTC application's signalling needs without the need to rely upon public signalling servers.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgfodor%2Fp2pcf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgfodor%2Fp2pcf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgfodor%2Fp2pcf/lists"}