cat / goq
clone

turns a linux box into a private steam streaming appliance: plug in, tap your key, play from another computer

chore: clean up for public visibility

dami etoile committed 2343f99
- Rewrite README to reflect built app, not planning hypotheses
- Rewrite AGENTS.md — remove planning mode, mark assumptions validated
- Rewrite SETUP.md — add macOS, update to Titan key flow + H.264 protocol
- Add MIT LICENSE file
- Remove stale lib.rs (incomplete duplicate of main.rs with missing commands)
- Rename Cargo package from 'app' to 'keyhome', set repository URL
- Mark OpenSpec tasks 4.1-4.3 as done (streaming, rendering, input all work)
- 17/18 tasks done; only 4.4 (latency measurement) remains
commit
2343f99080afe7fe2e3953997eb0acc393c1f1cc
parent
4324c72ea49206a9db2661e90c0e907b489af3f1
author
dami etoile <dami.etoile@gmail.com>
8 files changed, +214 -142
MAGENTS.md37+++++++---
ALICENSE21+++++
MREADME.md115+++++++++++++++++-------------
Mdocs/SETUP.md94+++++++++++++++++++-----
Mopenspec/changes/validate-tauri-yubikey-iroh-mvp/tasks.md6+-
Msrc-tauri/Cargo.lock46++++++------
Msrc-tauri/Cargo.toml10+--
Dsrc-tauri/src/lib.rs27-------
M AGENTS.md +25 -12
@@ -1,25 +1,38 @@
11 # Keyhome agent guide22 3-This repo is in planning/spec mode until Dami explicitly approves MVP implementation.3+Keyhome is a Tauri v2 remote desktop app with FIDO2 key authentication over Iroh P2P. The app is built and functional — spikes are done, the core flow works end to end.4+5+## Current focus6+7+The project is past the spike/validation phase. The main work now is **performance optimization** — improving frame rate (FPS), reducing latency, and tuning the H.264 encoding pipeline.8+9+## Key files10+11+- `src-tauri/src/commands.rs` — all backend logic (FIDO2 key derivation, Iroh networking, screen capture, H.264 encoding, input injection)12+- `src/ui/index.html` — the entire frontend (single-file UI)13+14+## Tech notes15+16+- **Rust edition 2024** — requires Rust 1.85+.17+- **Iroh 1.0 API** — uses the latest Iroh endpoint, protocol, and ticket APIs. Older Iroh 0.x code will not compile.18+- Always run `source ~/.cargo/env` before any `cargo` command.19+- On Linux with NVIDIA, set `WEBKIT_DISABLE_DMABUF_RENDERER=1` before running or building.420 521 ## Working rules622 723 - Use OpenSpec for capability planning before coding.8-- Treat `openspec/` artifacts as the source of truth for requirements and MVP scope.24+- Treat `openspec/` artifacts as the source of truth for requirements and scope.925 - Do not build a browser-only version; the core architecture is native Tauri + Rust backend.10-- Keep the first MVP focused on viability evidence, not polish.11-- Prefer small, verifiable Rust spikes over broad scaffolding.12-13-## MVP evidence standard26+- Prefer small, verifiable Rust spikes over broad scaffolding when exploring new capabilities.1427 15-A claim is not accepted unless backed by a runnable spike, a crate/API proof, or a documented blocker with exact error output.28+## Validated assumptions1629 17-The highest-risk assumptions are:30+The highest-risk assumptions have all been validated via spikes:1831 19-1. Native YubiKey flows usable from a Tauri backend.20-2. Safe storage or derivation of Iroh identity/addressing material from a YubiKey.21-3. Iroh session setup and authenticated protocol binding.22-4. Remote desktop stream/input latency and OS permissions.32+1. ~~Native YubiKey flows usable from a Tauri backend.~~ ✅ Validated (spikes 002, 003 — ctap-hid-fido2 communicates with Google Titan v2)33+2. ~~Safe storage or derivation of Iroh identity/addressing material from a YubiKey.~~ ✅ Validated (spike 004 — Titan hmac-secret → Iroh SecretKey → working endpoint, 6.5ms RTT)34+3. ~~Iroh session setup and authenticated protocol binding.~~ ✅ Validated (spikes 001, 007 — Iroh endpoints, ALPN routing, and key-derived identity all work)35+4. ~~Remote desktop stream/input latency and OS permissions.~~ ✅ Validated (spikes 005, 006 — frame streaming and input injection over Iroh both functional)2336 2437 ## OpenSpec workflow2538
A LICENSE +21 -0
M README.md +66 -49
@@ -1,62 +1,79 @@
1-# Keyhome1+# keyhome22 3-Keyhome is a native remote-control client concept: open the app, plug in a YubiKey, tap or enter PIN when required, and connect to the paired home machine over an end-to-end encrypted Iroh tunnel.3+A native remote desktop app secured by FIDO2 hardware keys. Plug in a Google Titan or YubiKey, tap it, and connect to your home machine over an end-to-end encrypted Iroh tunnel. No passwords, no addresses shared — the key derives the Iroh identity.44 5-The point of this repo is to test viability before building a polished product. The MVP should prove four things:5+## Status66 7-1. A Tauri Rust backend can reliably detect and challenge a YubiKey across Linux/macOS/Windows.8-2. The YubiKey can carry or derive the identity material needed to authenticate the user and find the remote machine.9-3. A native Iroh node can dial the host agent and establish an authenticated encrypted session.10-4. Remote desktop control can be streamed with acceptable latency after hardware-backed auth.7+Early prototype. Core functionality works (identity derivation, streaming, input). Performance optimization in progress. Current frame rate is ~1–2 fps. The main bottleneck is openh264 software encoding on the host and lack of hardware decode on the client when WebCodecs is unavailable.118 12-## Architecture hypothesis9+## How it works10+11+You plug in a FIDO2 security key (Google Titan, YubiKey) and tap it. The key produces a `hmac-secret` that becomes the 32-byte seed for an Iroh `SecretKey`. Both the host and client derive the same Iroh node ID from the same key — no addresses are exchanged, no passwords are stored.12+13+1. **One-time registration.** Tap the key → `hmac-secret` → 32 bytes → OS keyring.14+2. **Host starts.** Read from keyring → Iroh `SecretKey` → `Endpoint`.15+3. **Client connects.** Tap the key → derive the host's node ID → dial via relay.16+17+The connection is end-to-end encrypted by Iroh. The key never leaves the hardware token; only the derived secret is persisted in the OS keyring.18+19+## Architecture1320 1421 ```text15-+------------------ Keyhome Tauri Client ------------------+16-| UI webview |17-| - connection state |18-| - video/input surface |19-| - setup and diagnostics |20-| |21-| Tauri IPC |22-| |23-| Rust backend |24-| - YubiKey detection/challenge/PIN flow |25-| - token-resident peer/address config |26-| - native Iroh endpoint |27-| - remote-control protocol client |28-+----------------------------+-----------------------------+29- |30- | E2EE Iroh protocol31- v32-+----------------------- Host Agent ------------------------+33-| - paired node identity |34-| - screen capture / encode |35-| - keyboard + pointer injection |36-| - user consent / local safety controls |37-+-----------------------------------------------------------+22++--------------------------- keyhome (Tauri v2) ---------------------------+23+| |24+| Frontend (vanilla JS, no npm) |25+| - connection state, video canvas, input capture |26+| |27+| Tauri IPC |28+| |29+| Rust backend |30+| - FIDO2 CTAP via ctap-hid-fido2 - Screen capture via xcap |31+| - Iroh Endpoint (P2P, relay, E2EE) - H.264 encoding via openh264 |32+| - OS keyring for identity - Input injection via enigo |33+| |34++----------------------------------+--------------------------------------+35+ |36+ | Iroh (E2EE, relay-assisted)37+ v38++------------------------------ Host -------------------------------------+39+| - Iroh Endpoint (identity from keyring) |40+| - xcap screen capture → openh264 H.264 encode |41+| - enigo mouse/keyboard injection |42++-------------------------------------------------------------------------+3843 ```3944 40-## Planning artifacts45+| Layer | Crate / Tool |46+|---|---|47+| App shell | Tauri v2 |48+| Backend | Rust |49+| P2P transport | Iroh (relay-assisted, end-to-end encrypted) |50+| Hardware auth | FIDO2 CTAP via `ctap-hid-fido2` |51+| Screen capture | `xcap` |52+| Video encoding | `openh264` (software) |53+| Input injection | `enigo` |54+| Identity storage | OS keyring |55+| Frontend | Vanilla JS (no npm, no build step) |56+57+## Spikes58+59+Seven spikes were completed as viability evidence. They live in `spikes/`.4160 42-- `openspec/project.md` — project conventions and scope boundaries.43-- `openspec/specs/yubikey-identity/spec.md` — hardware identity and token data requirements.44-- `openspec/specs/iroh-session/spec.md` — peer discovery, authenticated dialing, and encrypted channel requirements.45-- `openspec/specs/remote-control/spec.md` — desktop streaming and input behavior requirements.46-- `openspec/changes/validate-tauri-yubikey-iroh-mvp/` — first MVP viability-testing plan.61+| # | Spike | What it proved |62+|---|---|---|63+| 1 | `001-iroh-native-ping` | Iroh connectivity between two native endpoints |64+| 2 | `002-yubikey-hmac-detection` | YubiKey HMAC-Secret detection and invocation |65+| 3 | `003-fido2-hid-enumeration` | FIDO2 HID device enumeration via `ctap-hid-fido2` |66+| 4 | `004-hmac-iroh-derivation` | HMAC-Secret → Iroh `SecretKey` derivation |67+| 5 | `005-frame-stream` | Frame streaming over Iroh |68+| 6 | `006-input-injection` | Mouse/keyboard injection via `enigo` |69+| 7 | `007-titan-no-copy` | Titan key derives identity without copying secrets off-device |4770 48-## Non-goals for the first MVP71+## Requirements4972 50-- General-purpose account systems.51-- Browser-only implementation.52-- Cloud relay ownership beyond what Iroh itself requires.53-- Polished installer/update channels.54-- Production-grade cross-platform remote-control permissions UX.73+- **OS:** Linux or macOS74+- **Hardware:** A FIDO2 security key (Google Titan, YubiKey)75+- **System dependencies:** See [docs/SETUP.md](docs/SETUP.md)5576 56-## Initial technology candidates77+## License5778 58-- App shell: Tauri 2.59-- Backend: Rust.60-- Network: Iroh native Rust stack.61-- Hardware auth: YubiKey challenge-response/PIV/OpenPGP/CTAP investigation; exact mode is part of the viability work.62-- Host agent: Rust service/binary, initially local or same-LAN before hardening NAT traversal and unattended access.79+MIT
M docs/SETUP.md +73 -21
@@ -1,6 +1,19 @@
11 # Keyhome Setup22 3-## Prerequisites (Ubuntu/Debian)3+## Prerequisites4+5+### Rust (required on all platforms)6+7+Keyhome uses Rust edition 2024 and requires **Rust 1.85 or later**.8+9+```bash10+curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh11+source "$HOME/.cargo/env"12+```13+14+> Always run `source ~/.cargo/env` before any `cargo` command in a new shell.15+16+### Linux (Ubuntu/Debian)417 518 ```bash619 # Tauri v2 system dependencies
@@ -10,14 +23,32 @@ sudo apt install -y libwebkit2gtk-4.1-dev build-essential curl wget file \
1023 # FIDO2 + screen capture dependencies1124 sudo apt install -y libudev-dev libusb-1.0-0-dev pkg-config1225 13-# xcap wayland support (even on X11, xcap pulls these in)26+# xcap Wayland support (needed even on X11)1427 sudo apt install -y libwayland-dev libpipewire-0.3-dev libgbm-dev28+```1529 16-# Rust toolchain17-curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh18-source "$HOME/.cargo/env"30+If you are on Linux with an NVIDIA GPU, set this environment variable before running or building:31+32+```bash33+export WEBKIT_DISABLE_DMABUF_RENDERER=134+```35+36+### macOS37+38+Install the Tauri CLI (v2):39+40+```bash41+cargo install tauri-cli --version "^2"42+```43+44+Grant the following permissions in **System Settings > Privacy & Security**:45+46+- **Accessibility** — required for input injection (mouse/keyboard control)47+- **Screen Recording** — required for screen capture48+49+### Clone1950 20-# Clone51+```bash2152 git clone https://github.com/FelineStateMachine/keyhome.git2253 cd keyhome2354 ```
@@ -32,32 +63,53 @@ cargo tauri dev
3263 cargo tauri build --debug3364 ```3465 66+On Linux with NVIDIA, prefix with the env var:67+68+```bash69+WEBKIT_DISABLE_DMABUF_RENDERER=1 cargo tauri dev70+```71+3572 ## Usage3673 37-### As Host (Server)38-1. Click "Start host" — the app creates an Iroh endpoint and waits for connections39-2. Copy the address JSON (click "Copy address")40-3. Share the address with the client machine74+Keyhome uses a FIDO2 security key (Google Titan) to derive Iroh peer identities. There is no address copy/paste — the key handles identity for you.75+76+### 1. Register (one-time per machine)77+78+Click **Register** and tap your Titan key. This creates a resident (discoverable) credential stored on the key itself. You only do this once per machine.79+80+### 2. Host81+82+Click **Start host**, then either:83+84+- **Tap your Titan key** — the app derives the Iroh identity from the key and starts listening, or85+- **Use saved keyring** — if you have previously hosted, the app can reuse the stored identity from the system keyring without requiring a tap.86+87+The host is now reachable over Iroh at an endpoint ID derived from the Titan key.88+89+### 3. Connect90+91+Click **Connect** and tap your Titan key. The app derives the same host endpoint ID from the key and dials it over Iroh. The remote screen appears in the viewer canvas. Input (mouse and keyboard) is forwarded to the host.4192 42-### As Client43-1. Paste the host's address JSON into the "Connect to Host" input44-2. Click "Connect" — the app connects over Iroh and streams screen frames45-3. The remote screen appears in the viewer canvas93+> Both machines must use the same Titan key (or a key that produces the same hmac-secret output). The key derivation replaces manual address sharing entirely.4694 4795 ## Architecture4896 49-- **Transport**: Iroh (peer-to-peer, relay-assisted)50-- **Screen capture**: xcap (X11/PipeWire)97+- **Transport**: Iroh 1.0 (peer-to-peer, relay-assisted)98+- **Screen capture**: xcap (X11/PipeWire on Linux, CoreGraphics on macOS)5199 - **FIDO2**: ctap-hid-fido2 (CTAP 2.0/2.1 over HID)52-- **Frame encoding**: JPEG (MVP — upgrade to H.264 later)53-- **Protocol**: BiStream with frame headers `[width:u32][height:u32][jpeg_len:u32][jpeg_data]`100+- **Frame encoding**: H.264 via openh264101+- **Input injection**: enigo (cross-platform mouse/keyboard)102+- **Protocol**: BiStream with frame header `[width:u32][height:u32][h264_len:u32][keyframe:u8][h264_data]`103+- **Identity**: Iroh SecretKey derived from Titan hmac-secret extension; no address copy/paste54104 55105 ## Spikes (evidence)56106 57107 | Spike | Status | What it proves |58108 |-------|--------|----------------|59109 | 001-iroh-native-ping | ✅ PASS | Iroh endpoints + ALPN routing work in native Rust |60-| 002-yubikey-hmac | ⚠️ PARTIAL | challenge_response crate works but only for YubiKey (not Titan) |61-| 003-fido2-hid | ✅ PASS | ctap-hid-fido2 communicates with Google Titan v2 |62-| 004-derive-iroh-identity | ✅ PASS | Titan hmac-secret → Iroh SecretKey → working endpoint (6.5ms RTT) |110+| 002-yubikey-hmac-detection | ⚠️ PARTIAL | challenge_response crate works but only for YubiKey (not Titan) |111+| 003-fido2-hid-enumeration | ✅ PASS | ctap-hid-fido2 communicates with Google Titan v2 |112+| 004-hmac-iroh-derivation | ✅ PASS | Titan hmac-secret → Iroh SecretKey → working endpoint (6.5ms RTT) |63113 | 005-frame-stream | ✅ PASS | Screen capture → JPEG → Iroh stream → client receives frames |114+| 006-input-injection | ✅ PASS | enigo input injection works over Iroh bidirectional stream |115+| 007-titan-no-copy | ✅ PASS | Both sides derive the same Iroh identity from Titan — no address sharing needed |
M openspec/changes/validate-tauri-yubikey-iroh-mvp/tasks.md +3 -3
M src-tauri/Cargo.lock +23 -23
M src-tauri/Cargo.toml +3 -7
D src-tauri/src/lib.rs +0 -27