The download button was a small corner overlay, which landed on top of the native
control bar's speaker icon. That was not the design asked for either: it should be
a single control in the CENTRE that turns into a play button once downloaded.
The tile is now the masked poster plus one centred button:
not downloaded → download icon (the element has no src at all, so nothing is
fetched until it is tapped)
downloading → live % inside that same button, in place
downloaded → play icon; tapping plays the LOCAL file
The native control bar is switched on only when playback starts, so there is
nothing for the control to collide with. Progress reports into the button rather
than the floating chip, so a video download no longer shows two indicators.
Also gives the tile a min-height so the thread doesn't jump while the poster loads.
Web/PWA is untouched — it has nowhere to download to, so it keeps streaming with
native controls.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Remote Access Platform — Alpha
A self-hostable remote support platform for IT teams: technicians log in to a web
console, see their team's machines, and start a screen-share + remote-control
session to any online machine after the end user grants consent. Built to the
spec in PRD-remote-access-platform.md.
This alpha implements the PRD's P0 requirements: authenticated console, MFA, RBAC, machine enrollment, per-session consent, WebRTC screen streaming + remote input, and an immutable audit log.
remote-access-app/
├── server/ Backend: HTTP API + WebSocket signaling + SQLite
│ ├── server.js Auth, MFA, machines, audit API + signaling broker
│ ├── auth.js scrypt passwords, TOTP MFA, tokens (no external auth deps)
│ ├── db.js Schema via Node's built-in node:sqlite
│ ├── public/ Web console (index.html) + remote viewer (viewer.html)
│ └── test/e2e.js 26-check end-to-end test of the whole backend flow
└── agent/ Native host agent (Electron)
├── main.js Consent window, screen source, OS input injection
├── input/inject.js Mouse/keyboard injection via nut-js (Win32 SendInput)
└── renderer/ Agent UI + WebRTC screen capture
Quick start
1. Server (any OS, Node 22.5+)
cd server
npm install # only dependency is `ws`
npm start # serves http://localhost:8090
Open http://localhost:8090, click Register team, then set up MFA
(add the shown secret to Google Authenticator / Authy / 1Password and enter a code).
Log in, and enroll a machine — you'll get an AGENT_ENROLL_TOKEN.
2. Agent (on the Windows/macOS PC to be controlled)
cd agent
npm install # installs Electron + nut-js (input injection)
set SERVER_URL=http://<server-ip>:8090 # Windows
set AGENT_ENROLL_TOKEN=<token from console>
npm start
The agent window comes online; the machine shows green in the console. Click Connect in the console — the agent shows a consent prompt. On Allow, the technician sees the live screen and can control it. A red banner stays on the host screen for the whole session, and every step is written to the audit log.
Set a machine to unattended at enrollment to skip the consent prompt (for servers / headless machines), per the PRD's unattended-access policy.
What's tested (in this sandbox)
cd server && npm test # 26/26 checks pass
cd agent && npm run test:input # 5/5 input-mapping checks pass
The e2e test drives the real backend: register → enable MFA → login (password + TOTP) → enroll machine → agent connects → technician requests session → consent → SDP/ICE relay → session end → audit verification → consent-denial path.
What requires real hardware (not testable in a headless sandbox)
- OS input injection runs through
nut-js(Win32SendInputon Windows,CGEventon macOS). Without it installed,inject.jsdegrades to a safe logging no-op so the agent still runs. Verify on a real desktop. - Screen capture uses Electron's
desktopCapturer/getDisplayMedia.
Architecture notes
- Media is peer-to-peer. The server only brokers signaling (SDP/ICE) and consent; screen frames and input never pass through it. Channels are DTLS-encrypted by WebRTC.
- NAT traversal uses a public STUN server. ~10–15% of connections behind symmetric NATs will need a TURN relay (coturn) — the next infra item.
- Auth uses scrypt password hashing and RFC-6238 TOTP, implemented on Node's
built-in
crypto— nobcrypt/jsonwebtoken/speakeasydependencies. - Storage is
node:sqlite(built into Node 22.5+), so the backend has a single runtime dependency (ws).
Gaps before production (from PRD §5)
- TURN relay for non-P2P connections (coturn).
- macOS/Linux agents + code-signed installers; packaged Windows binary.
- File transfer, clipboard sync, multi-monitor (PRD P1).
- SSO, session recording, SOC 2 (PRD P1/T8).
- Harden signaling: rate limiting, per-session authz checks, CSRF on cookie API.