Private screen sharing for a Mac over a Tailscale tailnet.
Tailview runs a tiny Mac host that captures the screen, streams it to a browser with WebRTC, and forwards touch, click, keyboard, scroll, pan, and zoom input back to macOS. The default flow is meant for an iPhone on the same private tailnet: open the Tailview URL, see the Mac screen, control it.
- Streams the Mac screen to the web viewer over WebRTC.
- Uses Tailscale for private access instead of exposing the session publicly.
- Supports remote pointer, keyboard, scroll, pinch zoom, and two-finger pan.
- Auto-opens the mobile keyboard when the remote click focuses a Mac text input.
- Adapts bitrate dynamically while keeping full resolution and 60 FPS targets.
Requirements:
- macOS
- Node.js 24 or newer
- pnpm 11.5.2 via Corepack
- Tailscale on the Mac and viewer device
- macOS Screen Recording and Accessibility permissions for the desktop host
Setup:
corepack enable
pnpm install --frozen-lockfile
pnpm run validatepnpm run validate runs Biome, TypeScript, Vitest, the production builds, and the repository audit.
TAILVIEW_PORT=4177 TAILVIEW_VIEWER_URL=https://main.tail0f70a4.ts.net/ pnpm --filter @tailview/desktop start
pnpm --filter @tailview/web devThen open the Tailscale HTTPS URL on the phone.
Useful checks:
pnpm run validate
pnpm run web:smokesequenceDiagram
participant Mac as "Mac host"
participant Viewer as "Phone browser"
participant Tailscale as "Tailnet"
Mac->>Mac: "Capture screen"
Viewer->>Mac: "Open private HTTPS URL"
Mac-->>Viewer: "WebRTC video stream"
Viewer-->>Mac: "Pointer, keyboard, scroll, zoom"
Mac->>Mac: "Inject native macOS input"
The web viewer is intentionally static and small. The Mac host owns capture, WebRTC signaling, metrics, and native input injection.
pnpm run devstarts workspace dev tasks.pnpm run buildbuilds all packages and apps.pnpm run testruns Vitest.pnpm run typecheckruns TypeScript checks.pnpm run checkruns Biome.pnpm run validateruns the full local validation path.pnpm run web:smokeruns the browser touch smoke test.
- Tailscale is the privacy boundary; the app does not need a public relay for the direct personal-use path.
- WebRTC carries live video because browsers and macOS already have the right low-latency media stack.
- Control messages stay separate from video so input latency can be tuned independently.
- The viewer stays dependency-light because mobile browser behavior is the product surface.
- macOS native input is compiled from Swift so the host can avoid a large automation dependency.
| Item | Limit |
|---|---|
| Host platform | macOS |
| Viewer platform | Modern browser |
| Network model | Same Tailscale tailnet |
| Input permissions | macOS Accessibility |
| Screen permissions | macOS Screen Recording |
MIT