Adds the reverse direction: share FROM Photos/Files/Safari INTO a Biz Connect conversation. An app can only appear in the iOS share sheet as an app-extension target, so this is real native work, not a web change. Pieces: - mobile/ios-share/ShareViewController.swift: a UI-less Share Extension. It stages the shared items into the App Group container and opens bizconnect://share. It deliberately does NOT reimplement the chat picker — that lives in the app, which already has the chat list, search and upload progress. Appends to the manifest (never overwrites), so sharing twice before opening the app loses nothing. - mobile/scripts/add-share-extension.rb: injects the extension target into the Capacitor-generated Xcode project on every CI build (Codemagic checks out fresh), using the xcodeproj gem that ships with CocoaPods. Embeds it, sets the bundle id <app>.share, and MERGES the App Group into the app's entitlements rather than clobbering them (push's aps-environment must survive). Idempotent. - mobile/plugins/share-inbox: getPending()/clear() to read that manifest — the App Group container isn't one of Filesystem's known directories, so it needs a bridge. - home.html: on bizconnect://share (and every resume, and cold-launch), read the inbox and show a "Send to…" picker over the chat list; chosen files run the SAME upload + /api/messages send as an in-app attachment. Reuses convertFileSrc to read the staged bytes with no base64 marshalling. - ios-patch.sh registers the bizconnect URL scheme; codemagic.yaml fetches a profile for the .share bundle id too. One-time manual gate (CI cannot toggle App capabilities): the App Group group.com.bizgaze.connect must be created and enabled on both App IDs in the Apple portal — documented in mobile/IOS_SETUP.md. Without it the two processes can't see each other's files and sharing silently no-ops; everything else still works. Validated cross-file: pod-name/jsName/method wiring for all three plugins, App Group id identical in all 4 files, URL scheme consistent across extension/plist/web, entitlement-merge preserves push. Needs a new iOS build (new targets + plugins). 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.