openvibers/openvibe.link AGENTS.md

AGENTS.md

main 11.8 KB · 154 lines · markdown

1# AGENTS.md — OpenVibe.Link
2
3## Project Overview
4
5OpenVibe.Link ("OpenVibe Node") connects a person's computers, servers, Raspberry Pis, phones and robots to the
6OpenVibe network. It pairs once, keeps **one outbound WebSocket** to OpenVibe.Bot (credential in the `Authorization`
7header only), opens **no ports**, and lets OpenVibe.Bot (robots) and OpenVibe.Actor (computer control for agents) use
8the device at the level the owner sets. It is a **client**, not a service: no database, no server, no deploy of its own.
9
10- **Kind:** client · **Version:** 0.59.0 · **License:** AGPL-3.0 · **Runtime:** Go 1.24 (one static binary per
11 platform), Python 3 plugins, an ESP32 Arduino library, an Android app (gomobile + Kotlin/Compose)
12- **Read first:** [README.md](README.md), [STATUS.json](STATUS.json), [docs/protocol.md](docs/protocol.md),
13 [docs/plugins.md](docs/plugins.md), [docs/computer.md](docs/computer.md), [docs/panel.md](docs/panel.md),
14 [docs/worker.md](docs/worker.md), [docs/access-and-agents.md](docs/access-and-agents.md),
15 [ADR-043](docs/ADR-043-bot-devices-and-control.md).
16
17**Network context:** pairing and the device credential are OpenVibe.Bot's (or Network's pairing flow); Network owns the
18account, roles, signed access grants (`access request/approve/revoke`) and the machines hub; Actor sends computer
19commands through Bot; Codes' job runner sends jobs to the worker; Connect carries port shares; Exchange lists a local
20model; Locker/Send move sealed files and text between the person's machines; Live receives WHIP video; Git serves the
21clone/git-connect relay. Link never becomes an authority for any of them.
22
23## Commands
24
25```bash
26go vet ./... && go test -race ./... # the e2e tests run the Python dry-run plugin
27pip install pytest pillow numpy # plugin test deps
28for p in sdk dryrun adeept_adr036 cozmo relay ros2 computer; do python3 -m pytest -q plugins/$p/tests; done
29scripts/dist.sh v0.0.0-dev # every binary, plugin bundle and SHA256SUMS in dist/
30scripts/package.sh # distro packages
31go build ./cmd/openvibe-link # one binary
32openvibe-link # guided first run: pair, check the link, report the first job
33openvibe-link status | stop | resume | quit | approvals | tray | panel
34openvibe-link memory scan|upload|sync | skills sync | exchange serve --model <id> --public-url <https>
35openvibe-link share <port> | share add | party host <party-id>
36openvibe-link access request|list|approve|deny|revoke ... | openvibe-link mcp [--print-config]
37openvibe-link clone '<link>' | git connect '<link>'
38openvibe-link profile verify community.<name> [--key-id <id>]
39```
40
41Install: `curl -fsSL https://openvibe.link/install | sh` (and `--robot/--driver`, `--network/--pairing/--code`,
42`--computer`, `--ai`, `--worker`, `--plan-logins`). CI builds Windows x64/ARM64, macOS arm64/Intel, Linux x64/ARM64
43(`.deb`, `.rpm`, tarball, install script) and the Android APK; releases carry SHA256SUMS only (no code signing yet).
44
45## Architecture at a Glance
46
47- **Core Go binary** ([cmd/openvibe-link/](cmd/openvibe-link/main.go), [internal/](internal/)): pairing, the outbound
48 WebSocket, heartbeats/deadman, the safety rules (deadlines, owner limits clamped before any driver sees a value, a
49 persisted stop, the local kill switch), the plugin supervisor, WHIP video (pion), the system service
50 (systemd/launchd/Windows).
51- **Plugins** ([plugins/](plugins/), separate processes, JSON lines over stdin/stdout): `dryrun`,
52 `adeept_adr036` (Adeept 4WD), `cozmo` (PyCozmo), `relay` (existing Live hardware clients), `ros2` (rclpy or
53 rosbridge: cmd_vel, battery, scan/range, odometry, camera), `computer` (terminal/files/screen/input/browser), plus the
54 community LOOI driver contract. A plugin stops its own actuators when stdin closes, at a command deadline and after
55 1 s without a heartbeat — the core dying never leaves motors running.
56- **ESP32 library** ([esp32/](esp32/README.md), `OpenVibeLink`): pairing, control link, drive/actuator commands,
57 telemetry, heartbeat, deadman and stop; no jobs or video.
58- **Computer plugin** ([docs/computer.md](docs/computer.md), [internal/agent/](internal/agent/)): `--computer
59 observe|ask|trusted|full`; runs as the person's account; approvals in the tray / notification / `openvibe-link
60 approve`; browser over DevTools `--remote-debugging-pipe` (no port); exact-origin task checks; nonce-fenced page
61 text; a live screen on openvibe.bot for its owner only (≤30 min); `openvibe-link stop` ends it at once.
62- **Worker** ([docs/worker.md](docs/worker.md), [internal/agent/jobs.go](internal/agent/jobs.go)): `--worker` runs
63 each job as an unprivileged user in a sandbox; Codes' runner can start a task from any browser; `--plan-logins` names
64 existing login files without reading/printing them.
65- **Network roles:** `codes.worker`, `desks.host`, `ai.host`, `bot.robot` are read at check-in and kept, but the local
66 configuration always wins — a role never installs anything or turns on a feature turned off here.
67- **Panel** ([docs/panel.md](docs/panel.md), [internal/panel/](internal/panel/)): `127.0.0.1`-only, a fresh random port
68 and 32-byte token per run (`#t=` fragment), every `/panel/api/*` needs `X-Link-Panel`, Origin must be the panel's own,
69 no CORS, only three files served. A restart invalidates the old address/token.
70- **Sharing/relay:** `share`/`share add` (Connect), `party host`, `clone`/`git connect` (Git, outbound), Locker-sealed
71 Send between machines, the access-grant relay (six scopes), `mcp` (stdio + local control socket) for agent CLIs,
72 memory/skills sync (dry run, one marked block per file, opt-in per tool).
73- **Android** ([android/](android/), [docs/android.md](docs/android.md)): v0 pairing/connection in Kotlin/Compose;
74 tokens + pairing AES-GCM-sealed with a non-exportable Keystore key; approvals (fingerprint for money, passwords,
75 deletions and a new recipient), read-only Dock activity, **Send to OpenVibe**; no AccessibilityService and no screen
76 capture.
77
78## Safety, Egress and Security
79
80- **The kill switch is the promise:** `openvibe-link stop` latches (persisted stop); `resume` clears it. Every
81 actuator path checks deadlines and clamped owner limits before a driver sees a value; the deadman/heartbeat stops
82 motion when the control link is lost.
83- **No open ports:** the core connects out; the panel is loopback-only with a per-run token; the browser is driven over
84 a pipe; `git connect`, clone, shares and the worker all connect out.
85- **Browsing egress** ([docs/link-egress.md](docs/link-egress.md)): public web destinations on 80/443 only, DNS
86 resolved and pinned on the device, private/local/metadata/IPv6-translation addresses refused; per-owner limits,
87 opt-out and a monthly cap; Network checks every stream; no cookie or ordinary share key grants it; a missing Network
88 **never** means direct fallback; on expiry/revocation/opt-out/battery/metered it closes within 5 s.
89- **Access grants** (`openvibe-link access`): the target verifies Network's signed grant (signature, audience, sender,
90 scope, expiry, live revocation) and its own level; the owner may narrow but never widen; a missing/expired/revoked
91 grant fails closed. Relayed ops go through Bot's device relay; Link Send's LAN hand-over checks the same grant.
92- **Community plugins/profiles:** a `community.<name>` profile is reviewed and signed by Bot (Ed25519 over canonical
93 JSON); `profile verify` checks it against Bot's published key (pin with `--key-id`), and pairing with a robot on a
94 community profile runs the same check and warns. Install a community plugin only when it passes.
95- **Secrets:** the device credential appears only in the `Authorization` header; Android seals it with the Keystore;
96 never log a token, a login file's content or a sealed envelope. The panel's token is in the fragment and must stay
97 there.
98- **Local configuration wins** over a network role; a role is a description, not a remote installer.
99
100## Deployment and Data
101
102None of its own: people install it on their machines/robots (install script or release binary), it pairs through
103OpenVibe.Bot/Network and runs as a system service. There is no database; local state lives in the Link's state
104directory (config, stop latch, panel.json, usage/reservations, memory/skills settings). Production is a distribution
105matter, not a service deploy.
106
107## Key Pitfalls
108
109- **Never add an inbound listener** (other than the loopback panel) or a shared secret/key. Pairing is a one-time code
110 and the credential goes in the `Authorization` header only.
111- **Never let a network role or a remote command turn on a local feature**; the local config is the upper bound.
112 Approvals at `ask` must be answerable (tray/notification/CLI) and deny by default.
113- **A plugin that keeps moving when its stdin closes is a safety bug.** Every driver must stop on stdin close, deadline
114 and 1 s without a heartbeat; keep the persisted stop checked on every path.
115- **Computer/browser:** exact-origin checks for task origins, no direct DNS/QUIC/non-proxied WebRTC UDP in the shared
116 browser, nonce-fenced page text, and no port. Do not "simplify" the DevTools pipe into a TCP debug port.
117- **Egress failure is closed:** a Network/Bot outage must never fall back to direct browsing or a relay that bypasses
118 the owner's cap; the saved offline choice ("use OpenVibe's address or wait") applies only when the Link connection
119 cannot be made.
120- **Panel:** loopback only, token on every API call, Origin checked, no CORS, no directory listing; a restart must
121 rotate the port and token.
122- **Worker:** run jobs as an unprivileged user in the sandbox; never read or print a login file's content; a job that
123 a person did not install a role for must not run.
124- **Community profiles:** verify the Ed25519 signature and plugin name before running a community driver; a failed
125 check is a warning/refusal, not a shrug.
126- **Android v0** has no automation: no AccessibilityService, no screen capture. Do not add one without the owner's
127 decision and a security review.
128- **Releases:** CI builds every platform; the release has SHA256SUMS only (no signing/notarization, no auto-update).
129 Do not claim a store/signed build.
130- `settings`/memory/skills sync must stay opt-in per tool, dry-run first, one marked block per file, with an undo; never
131 overwrite a tool's own content.
132
133## Testing
134
135`go vet ./... && go test -race ./...` covers the core, pairing, safety, protocol, worker, panel, access grants, Git
136relay, memory/skills and the e2e flow against [internal/fakebot](internal/fakebot) and
137[internal/fakenetwork](internal/fakenetwork). Plugin tests are Python (`pytest` per plugin; the e2e Go tests run the
138dry-run plugin). `scripts/dist.sh` proves every binary/plugin bundle builds. Add a test with every safety, grant or
139egress change; a skipped test is not a pass.
140
141## Documentation
142
143- [README.md](README.md) — install, what is in here, development.
144- [docs/protocol.md](docs/protocol.md) — the wire format with Bot; [docs/plugins.md](docs/plugins.md) — driver protocol,
145 bundled drivers, community plugins; [docs/install.md](docs/install.md); [docs/computer.md](docs/computer.md);
146 [docs/panel.md](docs/panel.md); [docs/worker.md](docs/worker.md); [docs/access-and-agents.md](docs/access-and-agents.md);
147 [docs/connect.md](docs/connect.md); [docs/party-desktop.md](docs/party-desktop.md); [docs/ai.md](docs/ai.md);
148 [docs/link-egress.md](docs/link-egress.md); [docs/stream.md](docs/stream.md); [docs/voice.md](docs/voice.md);
149 [docs/memory.md](docs/memory.md); [docs/skills.md](docs/skills.md); [docs/android.md](docs/android.md);
150 [docs/hardware-adeept.md](docs/hardware-adeept.md), [docs/hardware-cozmo.md](docs/hardware-cozmo.md),
151 [docs/hardware-looi.md](docs/hardware-looi.md); [ADR-043](docs/ADR-043-bot-devices-and-control.md).
152- [SECURITY.md](SECURITY.md), [STATUS.json](STATUS.json), [esp32/README.md](esp32/README.md),
153 [plugins/](plugins/) READMEs.
154