Skip to content

Commit d2c6b73

Browse files
committed
docs(wiki): Lab-Mode page covering host + operator flows
Documents the host-initiated remote pair-flash session: trust model (opt-in, audit log, token rotation, no shell or arbitrary file surface, DPAPI vault for Wi-Fi credentials), how the host starts a session, how the operator drives it from another machine, the full typed command surface, server-side limits, and the troubleshooting table. Sidebar and home page link the new page.
1 parent bfbfe89 commit d2c6b73

3 files changed

Lines changed: 157 additions & 0 deletions

File tree

wiki/Home.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Console player 2
2626
- [[Quick Start]] - install from a release zip and run the setup script.
2727
- [[Setup and Flashing]] - what the script does and how to recover a Pico.
2828
- [[Troubleshooting]] - what to run when setup or discovery fails.
29+
- [[Lab Mode]] - opt-in remote-flash session for collaborative testing.
2930
- [[Reporting-Bugs]] - how to make a bundle and what to include in an issue.
3031
- [[Build]] - build the release zip from source.
3132
- [[Protocol]] - short runtime and setup protocol reference.

wiki/Lab-Mode.md

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# Lab Mode
2+
3+
Lab mode is an opt-in remote-flash session. The host starts a single
4+
subcommand, gets back a short URL, and shares it with the operator
5+
who is iterating on firmware from a different machine. The operator
6+
uploads UF2 builds, forces the Pico into BOOTSEL drive mode, flashes,
7+
runs the bridge's diagnostic ladder, and pulls the firmware diag ring
8+
-- all over a tunnel, with no shell or arbitrary file-read surface.
9+
10+
This page is for hosts (people who have the Pico physically plugged
11+
in) and operators (developers who want remote access for testing).
12+
13+
---
14+
15+
## Trust model up front
16+
17+
* The host runs `couchlink lab-mode` intentionally. The bridge is a
18+
normal foreground process; Ctrl+C ends the session and rotates the
19+
tokens so any URL that was shared stops working.
20+
* The accepted command surface is enumerated in code on both ends.
21+
The tunnel relay validates `kind` against an allowlist before
22+
queuing; the bridge validates again before dispatching. No
23+
arbitrary shell, no arbitrary file read, no process spawn.
24+
* Every command lands in
25+
`%LOCALAPPDATA%\ParsecCouchLink\data\logs\lab-mode.log` on the host
26+
as an audit trail.
27+
* Cleartext Wi-Fi credentials never traverse the tunnel. They live in
28+
a DPAPI-encrypted vault on the host, decrypted only when the
29+
operator triggers `wifi_apply_saved` and zeroized immediately after
30+
the CDC `SET_WIFI` call.
31+
32+
---
33+
34+
## Host: starting a session
35+
36+
```powershell
37+
# One-time: stash your 2.4 GHz Wi-Fi credentials locally so you don't
38+
# have to type them every iteration. DPAPI keeps the blob bound to
39+
# your Windows login.
40+
couchlink save-wifi --ssid 'home2g' # prompts for the password
41+
# (or pass --password if you really want to)
42+
# (--clear deletes the saved vault)
43+
44+
# Per session:
45+
couchlink lab-mode
46+
```
47+
48+
Lab mode prints two lines and goes silent:
49+
50+
```
51+
Lab session: https://couchlink.whyknot.dev/v/<token>
52+
Ctrl+C to end. Activity log: %LOCALAPPDATA%\ParsecCouchLink\data\logs\
53+
```
54+
55+
Send the URL to the operator out-of-band (DM, email, signal -- it
56+
doubles as the auth, so treat it like a password). Press Ctrl+C any
57+
time to end. The tokens rotate whenever the bridge restarts.
58+
59+
---
60+
61+
## Operator: driving a session
62+
63+
From a separate machine, in PowerShell:
64+
65+
```powershell
66+
iwr https://couchlink.whyknot.dev/client.ps1 -UseBasicParsing | iex
67+
Set-CouchlinkSession 'https://couchlink.whyknot.dev/v/<token>'
68+
Get-CouchlinkInfo
69+
```
70+
71+
`Get-CouchlinkInfo` lists `allowed_kinds` so you can see the live
72+
command set at any time.
73+
74+
### Iteration loop
75+
76+
```powershell
77+
Send-CouchlinkUf2 -Path .\couchlink-pico2w.uf2 # chunked + sha256
78+
Invoke-CouchlinkBootsel # CDC -> UDP -> picotool
79+
Wait-Couchlink -Ms 1500 # BOOTSEL drive mounts
80+
Invoke-CouchlinkFlash # flash the upload
81+
Wait-Couchlink -Ms 4000 # Pico reboots
82+
Get-CouchlinkPicoIdentity # CDC HELLO
83+
Invoke-CouchlinkWifiApplySaved # vault on the host
84+
Find-CouchlinkPico # UDP discovery
85+
Read-CouchlinkPicoLog | Out-Host # firmware diag ring
86+
```
87+
88+
For a full forensic snapshot:
89+
90+
```powershell
91+
Invoke-CouchlinkBundle -OutPath .\bundle.zip
92+
```
93+
94+
For situational awareness:
95+
96+
```powershell
97+
Watch-CouchlinkStream # live events
98+
Get-CouchlinkLabState # bridge state snapshot
99+
Get-CouchlinkStateJournal -Tail 50 # host's state-journal.log
100+
Get-CouchlinkBridgeLog -Tail 200 # today's bridge log
101+
Test-CouchlinkPing # relay + bridge RTT
102+
```
103+
104+
---
105+
106+
## Accepted commands
107+
108+
The relay rejects any other `kind` with a 400 before queueing. The
109+
bridge enforces the same list as a second gate.
110+
111+
| `kind` | Purpose |
112+
|-----------------------|--------------------------------------------------------------------|
113+
| `upload_uf2` | Upload a UF2 to the host's lab-mode slot, chunked. |
114+
| `flash` | Flash the most recently uploaded UF2 onto a mounted BOOTSEL drive. |
115+
| `force_bootsel` | Try CDC, then UDP, then `picotool reboot -u -f` to enter BOOTSEL. |
116+
| `doctor` | Run the bridge's 7-check diagnostic ladder. |
117+
| `bundle` | Build a support zip and stream it back via file_chunk / file_eof. |
118+
| `identify` | CDC HELLO. Returns fw triple + board + creds_present + wifi_joined.|
119+
| `discover` | UDP broadcast on 4242. Returns the first ACK. |
120+
| `ping` | Round-trip nonce echo for relay+bridge liveness. |
121+
| `state` | Bridge state snapshot (version, last Pico, vault presence). |
122+
| `pull_log` | UDP TYPE_GET_LOG against the latched Pico's diag ring. |
123+
| `read_state_journal` | Tail of `state-journal.log` (1..2000 lines). |
124+
| `read_bridge_log` | Tail of today's tracing log (1..2000 lines). |
125+
| `wifi_apply_saved` | Decrypt the host's DPAPI vault and CDC SET_WIFI in one shot. |
126+
| `wifi_clear` | CDC CLEAR_WIFI -- wipe Pico flash creds, re-enter setup mode. |
127+
| `sleep` | Bridge-side bounded sleep so scripts can sequence without timers. |
128+
129+
---
130+
131+
## Limits
132+
133+
* 240 commands per 60 s per session at the relay (sliding window).
134+
* 2 MiB hard cap per command body at the relay.
135+
* 10 session mints per 60 s per source IP at the relay.
136+
* 24 h idle TTL on a session whose bridge has disconnected.
137+
* Wi-Fi vault: SSID up to 32 bytes, password up to 63 bytes (matches
138+
the firmware's flash_creds_t).
139+
* `read_state_journal` / `read_bridge_log`: 2000-line hard cap.
140+
* `sleep`: 60 000 ms hard cap.
141+
142+
---
143+
144+
## Troubleshooting
145+
146+
| Symptom | Likely cause | Action |
147+
|-----------------------------------------------|-------------------------------------------------------------|------------------------------------------------------------------------------|
148+
| 404 on `Get-CouchlinkInfo` | View token expired (host restarted the bridge). | Ask the host for a fresh URL. |
149+
| `bridge_connected: False` persists | Bridge not running, or host network can't reach the relay. | Have the host restart `couchlink lab-mode`. Check their firewall / DNS. |
150+
| 400 with `kind not in lab-mode allowlist` | Unknown `kind` in the POST body. | Check the live list at `Get-CouchlinkInfo.allowed_kinds`. |
151+
| 413 on `Send-CouchlinkUf2` | Single chunk over 2 MiB. | Re-run with `-ChunkBytes 131072` (or smaller). |
152+
| 429 from the relay | More than 240 commands in 60 s on this session. | Back off; sequence with `Wait-Couchlink -Ms`. |
153+
| `bootsel_result.ok == false` for all methods | Pico USB is fully wedged. | Ask the host to physically power-cycle the Pico. |
154+
| `identify` reports `no setup-mode Pico found` | Pico is in run mode (XInput, not CDC). | Run `force_bootsel` then `flash`, or `wifi_clear` then power-cycle. |
155+
| `wifi_apply_saved` says `no saved Wi-Fi` | Host hasn't populated the vault yet. | Ask the host to run `couchlink save-wifi` on their own terminal. |

wiki/_Sidebar.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
- [Quick Start](Quick-Start)
33
- [Setup and Flashing](Setup-and-Flashing)
44
- [Troubleshooting](Troubleshooting)
5+
- [Lab Mode](Lab-Mode)
56
- [Build](Build)
67
- [Protocol](Protocol)
78
- [Changelog](Changelog)

0 commit comments

Comments
 (0)