This document details all features for PairUX v1 (MVP) and outlines future enhancements.
Purpose: Convert visitors to downloads
Components:
- Hero section with value proposition
- Animated demo/screenshot carousel
- Key benefits (3-4 bullet points)
- CTA: Download button with OS detection
- Social proof section (testimonials placeholder)
- Footer with links
Requirements:
- Mobile responsive
- Fast LCP (<2.5s)
- Clear call-to-action above fold
Purpose: Detailed feature showcase
Sections:
- Screen sharing capabilities
- Remote control with consent
- Multi-cursor collaboration
- Cross-platform support
- Security & privacy
- Comparison table vs competitors
Purpose: Explain the user journey
Content:
- Download & install
- Start a session
- Share the link
- Collaborate together
Visual: Step-by-step illustrations or animated GIFs
Purpose: Get users to install the app
Features:
-
OS detection (auto-recommend)
-
Manual OS selection tabs
-
Package manager commands:
# macOS brew install --cask pairux # Windows winget install PairUX.PairUX # Ubuntu/Debian sudo apt install pairux # Fedora/RHEL sudo dnf install pairux # Arch Linux yay -S pairux-bin
-
Direct download links (DMG, MSI, AppImage)
-
System requirements
-
SHA256 checksums
Purpose: Self-service support
Sections:
- Getting started guide
- Troubleshooting
- FAQ
- Privacy policy
- Terms of service
- Meta titles and descriptions per page
- OpenGraph tags for social sharing
- Twitter Card tags
- Structured data (JSON-LD)
- Sitemap.xml
- Robots.txt
- Canonical URLs
| Method | Host | Viewer | Notes |
|---|---|---|---|
| Email/Password | ✅ | ✅ | Primary method |
| Magic Link | ✅ | ✅ | Passwordless option |
| OAuth (Google) | ✅ | ✅ | Social login |
| OAuth (GitHub) | ✅ | ✅ | Developer-friendly |
| Guest Join | ❌ | ✅ | View-only by default |
Host Authentication (Desktop):
stateDiagram-v2
[*] --> AppLaunch
AppLaunch --> CheckAuth: Check stored session
CheckAuth --> LoggedIn: Valid session
CheckAuth --> LoginScreen: No session
LoginScreen --> SupabaseAuth: User submits credentials
SupabaseAuth --> LoggedIn: Success
SupabaseAuth --> LoginScreen: Failure
LoggedIn --> Dashboard
Viewer Authentication (Web):
stateDiagram-v2
[*] --> JoinLink
JoinLink --> CheckSession: Has session?
CheckSession --> ViewerReady: Yes
CheckSession --> AuthChoice: No
AuthChoice --> GuestJoin: Join as guest
AuthChoice --> Login: Sign in
GuestJoin --> ViewerReady
Login --> ViewerReady
ViewerReady --> SessionJoin
- Supabase JWT tokens
- Stored securely:
- Desktop: Electron safeStorage
- Web: httpOnly cookies
- Auto-refresh before expiry
- Logout clears all tokens
stateDiagram-v2
[*] --> Created: Host creates session
Created --> Active: First participant joins
Active --> Active: Participants join/leave
Active --> Paused: Host pauses sharing
Paused --> Active: Host resumes
Active --> Ended: Host ends session
Paused --> Ended: Host ends session
Ended --> [*]
| State | Description | Host Actions | Viewer Actions |
|---|---|---|---|
created |
Session exists, no sharing | Start sharing | Cannot join yet |
active |
Screen being shared | Pause, End, Grant control | View, Request control |
paused |
Sharing paused | Resume, End | Wait screen shown |
ended |
Session terminated | N/A | Redirected to end screen |
Format: https://pairux.com/join/{session_id}
Properties:
- Session ID: UUID v4
- Optional password protection (v2)
- Expiry: When session ends
- Single-use option (v2)
- 1 host + 1 viewer per session
- Future: Multi-viewer support
Supported Sources:
- Entire screen (single or multi-monitor)
- Individual application window
- Browser tab (via Electron desktopCapturer)
Selection UI:
┌─────────────────────────────────────────┐
│ Select what to share │
├─────────────────────────────────────────┤
│ 🖥️ Screens │
│ ┌─────────┐ ┌─────────┐ │
│ │ Screen 1│ │ Screen 2│ │
│ └─────────┘ └─────────┘ │
│ │
│ 📱 Windows │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ VS Code │ │ Chrome │ │ Slack │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │
│ [Cancel] [Share Selected] │
└─────────────────────────────────────────┘
| Setting | Default | Options |
|---|---|---|
| Resolution | Native | Native, 1080p, 720p |
| Frame rate | 30 fps | 15, 30, 60 fps |
| Cursor | Included | Included, Hidden |
| Audio | Off | Off, System audio |
Adaptive Bitrate:
- Monitors network conditions
- Adjusts quality automatically
- Target: 2-8 Mbps for 1080p30
Quality Presets:
| Preset | Resolution | Bitrate | Use Case |
|---|---|---|---|
| Low | 720p | 1-2 Mbps | Poor connection |
| Medium | 1080p | 2-4 Mbps | Standard |
| High | 1080p | 4-8 Mbps | Good connection |
Purpose: Optional live streaming to external platforms (YouTube, Twitch, Facebook, custom RTMP servers)
Features:
- Stream to one or more RTMP destinations simultaneously
- Continue WebRTC session while streaming (parallel output)
- Standard RTMP settings for broad compatibility
RTMP Destination Configuration:
interface RTMPDestination {
id: string;
name: string; // e.g., "YouTube Live", "Twitch"
enabled: boolean;
url: string; // RTMP ingest URL
streamKey: string; // Stream key (stored securely)
}
interface RTMPSettings {
destinations: RTMPDestination[];
encoder: RTMPEncoderSettings;
}
interface RTMPEncoderSettings {
// Video
videoCodec: 'h264'; // H.264 (AVC) for maximum compatibility
videoBitrate: number; // Default: 4500 kbps (4500-6000 for 1080p)
resolution: '1080p' | '720p' | '480p';
framerate: 30 | 60; // Default: 30 fps
keyframeInterval: number; // Default: 2 seconds (GOP)
// Audio
audioCodec: 'aac'; // AAC for maximum compatibility
audioBitrate: number; // Default: 160 kbps (128-320)
audioSampleRate: 44100 | 48000;
audioChannels: 1 | 2; // Default: 2 (stereo)
}Default RTMP Settings (optimized for most platforms):
| Setting | Default | Notes |
|---|---|---|
| Video Codec | H.264 (x264) | Baseline/Main profile for compatibility |
| Video Bitrate | 4500 kbps | 2500-6000 recommended range |
| Resolution | 1080p | Match source or downscale |
| Framerate | 30 fps | 60 fps for gaming content |
| Keyframe Interval | 2 seconds | Required by most platforms |
| Audio Codec | AAC | LC profile |
| Audio Bitrate | 160 kbps | 128-320 range |
| Audio Sample Rate | 48000 Hz | Standard for streaming |
Popular Platform Presets:
| Platform | Ingest URL Pattern | Max Bitrate | Notes |
|---|---|---|---|
| YouTube | rtmp://a.rtmp.youtube.com/live2 | 51 Mbps | Supports 4K |
| Twitch | rtmp://live.twitch.tv/app | 6000 kbps | 1080p60 max |
| rtmps://live-api-s.facebook.com:443/rtmp | 4000 kbps | Use RTMPS | |
| Custom | User-provided | Varies | Full control |
UI - RTMP Settings Panel:
┌─────────────────────────────────────────────────────┐
│ Live Streaming Settings │
├─────────────────────────────────────────────────────┤
│ │
│ Destinations [+ Add New] │
│ ┌─────────────────────────────────────────────┐ │
│ │ ☑️ YouTube Live │ │
│ │ rtmp://a.rtmp.youtube.com/live2 │ │
│ │ Stream Key: ●●●●●●●●●●●● [Edit] [🗑️] │ │
│ └─────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────┐ │
│ │ ☐ Twitch │ │
│ │ rtmp://live.twitch.tv/app │ │
│ │ Stream Key: ●●●●●●●●●●●● [Edit] [🗑️] │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ Encoder Settings │
│ ├─ Resolution: [1080p ▼] │
│ ├─ Framerate: [30 fps ▼] │
│ ├─ Video Bitrate: [4500 kbps ] │
│ ├─ Audio Bitrate: [160 kbps ▼] │
│ └─ Keyframe Int: [2 seconds ▼] │
│ │
│ [Reset to Defaults] │
└─────────────────────────────────────────────────────┘
Active Session - RTMP Controls:
┌─────────────────────────────────────────────────────┐
│ Live Streaming [⚙️ Settings] │
├─────────────────────────────────────────────────────┤
│ ☑️ YouTube Live 🔴 LIVE 00:45:23 │
│ ☐ Twitch ⚫ Ready │
├─────────────────────────────────────────────────────┤
│ [Start All] [Stop All] Bitrate: 4.5M│
└─────────────────────────────────────────────────────┘
Security:
- Stream keys stored in system keychain (Electron safeStorage)
- Keys never logged or transmitted except to RTMP server
- Option to require re-entry of stream key each session
Error Handling:
| Error | User Message | Action |
|---|---|---|
| Connection failed | "Cannot connect to streaming server" | Retry with backoff |
| Auth failed | "Invalid stream key" | Prompt to re-enter |
| Bandwidth exceeded | "Network too slow for live stream" | Suggest lower bitrate |
| Server disconnected | "Stream interrupted, reconnecting..." | Auto-reconnect (3 attempts) |
stateDiagram-v2
[*] --> ViewOnly: Viewer joins
ViewOnly --> Requested: Viewer requests control
Requested --> ViewOnly: Host denies
Requested --> Granted: Host approves
Granted --> ViewOnly: Host revokes
Granted --> ViewOnly: Viewer releases
Granted --> ViewOnly: Emergency hotkey
| State | Viewer Can | Host Can | Visual Indicator |
|---|---|---|---|
view-only |
View only | Full control | None |
requested |
View only | Approve/Deny | Request notification |
granted |
Mouse + Keyboard | Full control | Control active badge |
revoked |
View only | Full control | Control ended notice |
Mouse Events:
mousemove- Cursor position (x, y relative to screen)mousedown- Button press (left, right, middle)mouseup- Button releaseclick- Click eventdblclick- Double clickscroll- Scroll delta (x, y)
Keyboard Events:
keydown- Key press with modifierskeyup- Key release- Key codes follow Web KeyboardEvent standard
Event Message Format:
interface InputEvent {
type: 'mouse' | 'keyboard';
action: string;
timestamp: number;
data: MouseEventData | KeyboardEventData;
}
interface MouseEventData {
x: number; // 0-1 relative position
y: number; // 0-1 relative position
button?: number;
deltaX?: number;
deltaY?: number;
}
interface KeyboardEventData {
key: string;
code: string;
modifiers: {
ctrl: boolean;
alt: boolean;
shift: boolean;
meta: boolean;
};
}Hotkey: Ctrl+Shift+Escape (configurable)
Behavior:
- Immediately revokes all remote control
- Shows confirmation toast
- Cannot be overridden by remote input
- Works even if app is not focused (global hotkey)
Design: The host and viewer share the host's one real system pointer.
How: Every remote movement, click, scroll, and drag is injected directly into the host operating system. When the viewer is idle, the host immediately uses the same pointer normally. There is no separate cursor, pointer borrowing, restoration, or compositor integration.
Conflict resolution: Input that arrives at the same moment is ordered by the operating system. The emergency revoke hotkey always releases held input and stops injection.
See docs/REMOTE-CONTROL.md and @profullstack/remote-input.
stateDiagram-v2
[*] --> Disconnected
Disconnected --> Connecting: Join session
Connecting --> Connected: WebRTC established
Connecting --> Failed: Timeout/Error
Connected --> Reconnecting: Connection lost
Reconnecting --> Connected: Recovered
Reconnecting --> Disconnected: Max retries
Connected --> Disconnected: Leave session
Failed --> Disconnected: User dismisses
Triggers:
- ICE connection state:
disconnectedorfailed - No data received for 10 seconds
Strategy:
- Attempt ICE restart
- If fails, create new peer connection
- Re-establish via signaling
- Max 3 attempts, 5 second intervals
- Show reconnecting UI during process
| Quality | Latency | Packet Loss | Icon |
|---|---|---|---|
| Excellent | <50ms | <1% | 🟢 |
| Good | 50-150ms | 1-3% | 🟡 |
| Poor | 150-300ms | 3-10% | 🟠 |
| Bad | >300ms | >10% | 🔴 |
Main Window:
┌─────────────────────────────────────────────────────┐
│ PairUX [_] [□] [X] │
├─────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────┐ │
│ │ │ │
│ │ No active session │ │
│ │ │ │
│ │ [Start New Session] │ │
│ │ │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ Recent Sessions │
│ • Session with John - 2 hours ago │
│ • Session with Jane - Yesterday │
│ │
├─────────────────────────────────────────────────────┤
│ 👤 user@email.com [Settings] │
└─────────────────────────────────────────────────────┘
Active Session:
┌─────────────────────────────────────────────────────┐
│ PairUX - Session Active [_] [□] [X] │
├─────────────────────────────────────────────────────┤
│ Share Link: https://pairux.com/join/abc123 │
│ [Copy Link] │
├─────────────────────────────────────────────────────┤
│ Sharing: Screen 1 [Change] │
│ Quality: 1080p @ 30fps 🟢 Excellent │
├─────────────────────────────────────────────────────┤
│ Participants (1) │
│ ┌─────────────────────────────────────────────┐ │
│ │ 👤 John Doe │ │
│ │ Status: Viewing │ │
│ │ [Grant Control] [Remove] │ │
│ └─────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────┤
│ [Pause Sharing] [End Session] │
└─────────────────────────────────────────────────────┘
Viewer Interface:
┌─────────────────────────────────────────────────────┐
│ PairUX Viewer 🟢 Connected [Settings] │
├─────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────┐ │
│ │ │ │
│ │ │ │
│ │ [Remote Screen Display] │ │
│ │ │ │
│ │ │ │
│ │ 🔵 Host │ │
│ └─────────────────────────────────────────────┘ │
│ │
├─────────────────────────────────────────────────────┤
│ [Request Control] [Fullscreen] [Leave] │
└─────────────────────────────────────────────────────┘
Tray Menu:
- Show/Hide window
- Session status
- Quick end session
- Settings
- Quit
Tray Icon States:
- Idle: Default icon
- Session active: Animated/colored icon
- Control granted: Warning icon
General:
- Start on login
- Start minimized
- Check for updates
Sharing:
- Default quality preset
- Include cursor
- Include system audio
RTMP Streaming:
- Manage destinations (add/edit/remove)
- Stream keys (securely stored)
- Encoder settings (bitrate, resolution, fps)
- Keyframe interval
- Auto-start streaming with session
Control:
- Emergency revoke hotkey
- Require approval for control
- Auto-revoke on inactivity
Privacy:
- Clear session history
- Analytics opt-out
Display:
- Fit to window / Original size
Input:
- Keyboard layout
- Mouse sensitivity
- RTMP live streaming to external platforms (YouTube, Twitch, etc.)
- Multiple simultaneous RTMP destinations
- Stream key management with secure storage
- Multi-viewer support (up to 5)
- Session recording
- Chat/annotations
- Password-protected sessions
- Scheduled sessions
- RTMP recording to file
- File transfer
- Clipboard sync
- Voice chat
- Session templates
- Team workspaces
- SRT/RIST streaming protocols
- SSO integration
- Admin dashboard
- Audit logs
- Custom branding
- On-premise deployment
- Custom RTMP ingest server