Skip to content

Remote Development Environment ​

The board always hangs off the company VM 10.8.0.6; every other machine uses its serial ports, ADB and flashing over WireGuard. Zero changes to the board — the workflow is identical from whichever machine you sit at.

Overview ​

Star topology: every node peers only with the cloud, and cross-node traffic is relayed through the cloud's ip_forward. Onboarding a new device touches just two places: itself and the cloud.

Machine Inventory ​

The single source of truth is machines.tsv (versioned; the authoritative copy lives on the cloud at /srv/mesh/). Whenever you switch machines, check your identity first:

bash
mesh-whoami        # 按本机 WireGuard IP 自动匹配身份
idIPRole
cloud10.8.0.1WG relay · authoritative inventory host · nginx hosting · archive
home-vm10.8.0.4home build VM · always-on Agent
company-vm10.8.0.6board host: ser2net / adb-server / board lock
mac10.8.0.10interactive · AI Agent · on-the-go build & flash
phone10.8.0.100Termius SSH · remote viewing
home-host10.8.0.2home-vm hypervisor (usually offline)

Same-name trap

vms and vmt share the hostname olicca-vm — go by IP, not hostname.

WireGuard: Onboarding a New Device ​

The full chain, using an iPhone as the example (measured 2026-09):

Config discipline: client configs live in ~/.wireguard-mesh/<设备>.conf (0600, the private key never goes into git); render the QR code as a PNG with the qrcode library — don't use terminal ASCII (it gets cut off).

Daily Workflow Cheat Sheet ​

NeedChannelKey command
shell / push-pull filesadb client → remote adb serveradb -H 10.8.0.6 shell
MCU log / consoleser2net raw TCPsocket://10.8.0.6:400x
Flashingpush artifacts to the remote, run the flash command therersync + ssh
On-board web UIrun the forward locally on vmthttp://10.8.0.6:18080
Anything elsepasswordless SSH (ed25519)ssh olicca@10.8.0.6

Remote ADB ​

bash
adb -H 10.8.0.6 devices                                  # 显式走远端
export ADB_SERVER_SOCKET=tcp:10.8.0.6:5037               # 脚本里免 -H

The remote's local adb shell and a remote adb -H share one server and can run concurrently; the adb versions on both ends should be close (28.0.2 on both right now).

Remote Serial (ser2net) ​

PortBoardFunctionBaud rate
4000S100PMCU-COM921600
4001S100PMain-COM921600
4002RV1126BAP (Linux)1500000
4003RV1126BMCU (RT-Thread)1500000
python
import serial
s = serial.serial_for_url("socket://10.8.0.6:4002", timeout=2)   # raw TCP,波特率在 ser2net 端固定

Don't use rfc2217://

ser2net 4.3.4's telnet stack freezes after an abnormal disconnect (it accepts TCP but never sends the negotiation bytes). The raw TCP mode stayed stable across 15 fast connect/disconnect cycles. The only cost is that you can't change the baud rate remotely — and ours is fixed anyway.

30-Second Health Check ​

Remote Flashing ​

On a high-latency link (RTT 130ms), don't map the flashing USB back to your local machine. Do this instead:

Board Mutex Lock ​

The dev board is a singleton resource — always take the lock before flashing or running an exclusive monitor:

Locks live in /tmp/boards/*.lock/ (owner / task / timestamp), and /tmp is wiped on reboot, so deadlocks heal themselves.

Async Task Dispatch (Level 2) ​

You drop a task file into a shared directory and the unattended Agent on the home VM picks it up and runs it:

Direct Local Mode (Board Plugged into the Mac) ​

Without WireGuard, adb connects directly; the four CH340/341 serial ports (/dev/cu.wchusbserial*) change names on re-enumeration, so identify them by baud rate + prompt:

BoardFunctionBaud ratePrompt
RV1126BAP1500000root@ATK-DLRV1126B-MCU720:/#
RV1126BMCU1500000msh />
S100PAP921600ubuntu login: (root, empty password)
S100PMCU921600no shell, boot log only

Getting the S100P into fastboot: send \x03 one byte at a time at ~100ms intervals on the AP serial port to break the zero-delay autoboot (sending every 10ms floods the U-Boot input buffer) → Hobot$ → fastboot 0.

Data and Collaboration Ecosystem ​

The cloud isn't just a relay — it hosts the whole collaboration stack:

FacilityPort / entryNotes
omp-webhttp://10.8.0.1:8080 (WG intranet only, no auth)browse sessions from any device + web chat + task UI
Task Centerhttp://10.8.0.1:8090REST + work-task CLI + deadline reminders over Feishu
Session daily reportsystemd timer at 09:30scans all sessions → kimi-k3 writes it → Feishu DM
nginx80 (public)static homepage + prebuilts toolchain downloads

omp session sync uses one syncthing folder per project: the folder_id is the same on all three ends while each end maps its own path, and on the cloud ignoreDelete makes it an append-only archive. Single source of truth for the mapping: projects.tsv.

Pitfall Archive ​

SymptomRoot causeFix
CH340 plugged in but no /dev/ttyUSB0Ubuntu brltty grabs CH340/CP210xapt purge brltty + replug
ser2net accepts TCP but sends no datathe 4.3.4 RFC2217 stack freezes after an abnormal disconnectswitch to raw TCP (socket://)
ser2net process alive but the port isn't listeningboot race: the USB serial port isn't enumerated yet when it starts, so it silently disables itselfudev rule that restarts it automatically
adb-server systemd stuck in activatingan old adb -a nodaemon still holds port 5037pkill -f "adb -a nodaemon"
Remote adb suddenly dies completelyadb version drift between the two endsalign the versions (28.0.2 right now)
adb -H ... forward on the Mac reports more than oneadb 1.0.41's -s doesn't work with forwardrun the forward locally on vmt
All syncthing folders on vms suspenddisk at 100% trips minHomeFreePctjournal vacuum + free up disk

Reproduction and Sources ​

  • All remote configuration is captured as idempotent scripts under setup/; run setup-resource-host.sh once on a new resource host to reproduce it
  • For operating details and command-by-command notes, see the remote-dev skill and its four reference documents (initialization pitfalls / design choices / topology evolution / multi-project design)
Something unclear here, or want to talk about similar work?Message me →