尚未收集儲存庫資訊。
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗Walk around the room and still run the meeting.
One connection for both apps. When both are open on the computer, the phone has a single connection to it and shows a tab for each. Open the other app later and its tab comes alive by itself.
From then on, open it from the home screen: it connects by itself in about a second. It can remember several computers; tap ⋯ on a computer to forget it. On the computer, the remote's window lists the trusted phones, each with its own Forget.
It works on the same Wi-Fi, and also when the Wi-Fi keeps devices apart (common on guest and church networks) or the phone is on mobile data: then an encrypted relay carries the connection. Connecting needs the internet for a moment; for a place with no internet at all, the apps offer the same remote as a page served by the computer itself, under No internet here?
phone (this app) computer (Ma3madan Play / PPT)
│ │
│── encrypted offer ──► /api/ws: the computer's room ◄──── one socket, kept open
│◄── encrypted answer ── (a Cloudflare Durable Object) by whichever app holds the link
│ │
└════════ WebRTC data channels: direct over the Wi-Fi, or through a TURN relay ═════════┘
# (never sent to any server). The phone's first offer is encrypted with a key made from that code; the computer then gives this phone a key of its own over the encrypted connection, and makes a new QR code. So every phone can be forgotten on its own./api/presence; phones see the computers that share their public internet address, which is what devices on the same Wi-Fi have. Tapping one sends an offer that the computer must approve; both screens show a 4-digit code made from the two ends' certificates, so no one in between can pose as either./api/ws, a Durable Object reached over a WebSocket. Messages are passed on live, encrypted (AES-GCM) with the phone's key, so the server can neither read nor forge them, and nothing is stored. Only the computer that first opened a room can hold it. The socket costs nothing while idle (WebSocket Hibernation)./api/turn, short-lived credentials) carries the same encrypted channel. A heartbeat notices a lost connection within seconds, and the phone connects again by itself.| File | Role |
|---|---|
public/index.html · app.js | The app: pairing, connecting, and both remotes |
public/link.js | Pairing, keys, encryption and signaling, shared with the computer apps (src/link.js there) |
public/sw.js · manifest.webmanifest | Installable, and opens without internet |
worker/src/index.js | The signaling rooms (a Worker with a Durable Object, worker/wrangler.toml) |
functions/api/ws.js | The signaling socket, handed to the computer's room |
functions/api/turn.js | Short-lived TURN relay credentials |
functions/api/presence.js | Nearby computers |
functions/api/signal.js | The old polling signaling, kept for app versions before the shared link |
schema.sql · migrations/ | The D1 tables (nearby computers, old signaling) |
icon.svg | The app icon |
Every push to main deploys through GitHub Actions (.github/workflows/deploy.yml): the signaling worker first, then the Pages app. Pull requests get a preview copy. The repository needs two secrets, CLOUDFLARE_API_TOKEN (Workers Scripts, Pages and D1: Edit) and CLOUDFLARE_ACCOUNT_ID.
By hand:
npm run deploy # the worker, then the Pages app
npm run db:init # once: create the tables
npm run db:migrate # once: the 2026-10 columns
TURN relay. Create a TURN key in the Cloudflare dashboard (Realtime → TURN Server), then run npm run turn: it asks for the Key ID and the API token (hidden), stores them as the Pages project's secrets (never in the code) and republishes.
Without them the app still works, directly over the Wi-Fi only.
The app collects no personal data and no usage statistics. The signaling server only passes on encrypted messages, for the moment it takes to connect, and keeps none of them; the relay, when used, carries traffic it can't read. The keys stay on the phone and the computer.
Free to use for any person or organization, for any purpose (MIT License).