openvibers/openvibe.bot AGENTS.md

AGENTS.md

main 10.5 KB · 167 lines · markdown

1# AGENTS.md — OpenVibe.Bot
2
3## Project overview
4
5The open control panel for robots: pairs a robot's machines, keeps their state and gates every
6operator command. A device opens one outbound WebSocket to `/device`; a person drives it from
7`/control` or the REST API through the same gate — roles, per-robot limits, cooldowns and the turn
8budget — over a panel built from the robot's profile rather than per-robot code. Node.js/Express +
9`ws`, PostgreSQL (`ov_bot`), CommonJS, **no build step**.
10
11- Service id `bot`, port `4630`, origin `https://openvibe.bot`, unit `openvibe-bot.service`,
12 release root `/opt/openvibe.bot`.
13- Design: OpenVibe.Contracts `docs/adr/ADR-043-bot-devices-and-control.md`.
14- Boot applies `migrations/` with the owner role (`DATABASE_DIRECT_URL`, one connection, advisory
15 lock), seeds the shipped profiles, then serves on the pooled runtime role (`DATABASE_URL`) with
16 Valkey for shared per-process state. Several processes may start together.
17
18## What it owns
19
20- Robots, profiles, per-robot limits, `allow` lists, roles and operators; the curated kit catalogue
21 (`server/kits/*.json`, metadata only).
22- Devices and their credentials (hashed at rest, rotated, revoked, never readable back) and pairing
23 codes (one-time, short-lived; `BOT_PAIRING_AUTHORITY=network` lets Network mint them).
24- The `/device`, `/control` and `/watch` WebSockets, the command gate and the operator queue.
25- Routines (`server/routines.js`), owner webhooks (`server/webhooks.js`, signed, retried), community
26 robot profiles (`server/submissions.js`), the phone-as-robot device, the in-process simulator, the
27 server-side ONVIF camera connector, and the job dispatcher for paired Link machines
28 (`server/jobs/`).
29- The front page, `/robots`, `/models`, `/r/:id`, `/pair/:id`, `/panel/:id`, `/try`, `/docs`,
30 `/robots/submit`, legal pages, sitemap/llms/manifest, and the read-only embeddable panel.
31- The command audit (`command_audit`, 30 days) and the `bot.*` outbox events.
32
33Not owned: the robot hardware or its driver (Link plugins / the device), video ingest (OpenRestream),
34live streaming and VODs (Live/Media), identity, the money ledger, AI model execution.
35
36## Who calls it, and whom it calls
37
38- Callers: people in the browser; the OpenVibe.Actor agent (`svc:actor`, only for the robot's owner,
39 `GET/POST /api/v1/robots/:id/steps`), Live (a channel bound to a robot embeds `/panel/:id/embed`),
40 and services using a Network service token with `bot.*` capabilities.
41- Depends on: Network (user/service/node tokens, pairing, identity resolution, account export/
42 deletion events), OpenRestream (WHIP ingest and the robot's stream key; `openre.*` capabilities),
43 Billing (`billing.usage.record` for job usage), Search (`search.document.write`, model documents),
44 Live (the owner's channel for the on-stream switch), and `openvibe-contracts`/`openvibe-sdk`/
45 `openvibe-shared` pins.
46- A device credential is a secret: `Authorization` header only (the phone uses the subprotocol
47 `ov.device.<credential>` because a browser cannot set the header), never a query string.
48
49## Repository map
50
51- **Entry:** [server/index.js](server/index.js) — config, owner-role migration, profile seed, app,
52 Valkey, simulators, telemetry, jobs and subscriptions; graceful stop.
53- **App:** [server/app.js](server/app.js) — CSP (a special `HOME_CSP` for the front page),
54 `/api/v1`, `/auth`, `/shared`, `/device`, `/control`, `/watch` upgrades, pages, error handling.
55- **Config:** [server/config.js](server/config.js); database [server/db.js](server/db.js).
56- **Domain:** [server/domain/](server/domain/), [server/profiles/](server/profiles/),
57 [server/sim/](server/sim/), [server/onvif/](server/onvif/), [server/kits/](server/kits/).
58- **API:** [server/api/](server/api/) — `v1.js`, `auth.js`, `actor-limits.js`, `resource-control.js`,
59 `desk-leases.js`, `resource-index.js`.
60- **Realtime:** [server/realtime.js](server/realtime.js) — `/device`, `/control`, `/watch`, the gate.
61- **Web:** [server/web/](server/web/) — `home.js`, `routes.js`, `directory.js`, `docs.js`,
62 `layouts.js`, `session.js`, `qr.js`, `surface.js`.
63- **Browser:** [public/panel.js](public/panel.js), `public/drive.js`, `public/phone-device.js`,
64 `public/try-device.js`, `public/sim-world.js`, `public/sim-view.js`, `public/embed-frame.js`.
65- **Jobs/metering:** [server/jobs/](server/jobs/), [server/events/outbox.js](server/events/outbox.js).
66- **Deploy:** [deploy/systemd/openvibe-bot.service](deploy/systemd/openvibe-bot.service),
67 [deploy/nginx/openvibe.bot.conf](deploy/nginx/openvibe.bot.conf).
68
69## Commands
70
71```bash
72npm ci
73cp .env.example .env
74npm run dev # http://127.0.0.1:4630
75npm test # test/run.js, PGlite, in-process Network/Events stubs
76npm run test:pg # same suite on PostgreSQL (BOT_TEST_STORE=pg)
77npm test -- profiles pairing # filter by file name
78ov test # the harness's sharded run of what the change reaches
79node --check <file.js>
80```
81
82## Database and migrations
83
84PostgreSQL database `ov_bot` (host data role, ADR-035); PGlite in development/tests. Boot applies
85`migrations/NNNN_*.sql` through the **owner** connection and then serves through the pooled runtime
86role; the run is serialised by an advisory lock, so parallel starts are safe. Never edit an applied
87migration; add a new number. The command audit is pruned after 30 days; job usage waits in
88`run_usage_outbox` when Billing is not configured and is never dropped.
89
90## Auth and the command gate
91
92- User tokens (SSO session / `ov_token` cookie) and service tokens are both accepted on `/api/v1`;
93 a **node token is refused** on person and service routes. A person acts only as themself — naming
94 anyone else is refused, never ignored.
95- The **owner** can list a robot, add/remove operators, edit limits, set
96 the agent switch and flip streaming toggles. An operator drives within limits and cooldowns. An
97 open-queue robot gives a viewer a turn, and leaving promotes the next person at once.
98- `svc:actor` may read/drive **only** the owner's robot and only with `robots.agent_control` on;
99 otherwise `403 bot.agent_not_allowed`. Every step passes the same gate as the owner's own commands.
100- A machine runs only its owner's jobs: the dispatcher compares the job requester with the machine
101 owner and refuses anyone else with `bot.not_owner`, whatever the placer chose.
102- Every command, allowed or refused, is audited. Pairing codes are one-time; `halt` always passes;
103 Stop is one optional part of a layout, and the tray's own stop stays local.
104
105## Frontend loading model
106
107Server-rendered pages and plain forms, enhanced by no-build scripts (`public/panel.js` drives with
108touch/keyboard/gamepad). There is no SPA, bundler or transpiler. `openvibe-shared` supplies the
109Frame, showcase, arrange surface and serve handler (`/shared/*` from the pinned node_modules). The
110front page uses its own CSP (`HOME_CSP`) for the Frame's Network calls; every other page keeps
111`default-src 'self'`. Embedding takes both sides: `BOT_EMBED_ORIGINS` (bare https origins, no paths/
112wildcards) and the embedding page's `frame-src` must both allow the frame.
113
114## Deploy
115
116Unit [deploy/systemd/openvibe-bot.service](deploy/systemd/openvibe-bot.service) runs
117`/opt/openvibe.bot/server/index.js` on `127.0.0.1:4630`, env `/etc/openvibe/bot.env`; nginx
118[deploy/nginx/openvibe.bot.conf](deploy/nginx/openvibe.bot.conf) terminates TLS and upgrades
119`/device`, `/control` and `/watch` to the same port. The repo documents the unit and env, not an
120`ovhost deploy` command — use the host's own installer. Readiness `/api/ready`, liveness
121`/api/health`, both loopback-only. Env that refuses to boot when invalid: `BOT_PAIRING_AUTHORITY`
122(`bot` or `network`), `BOT_EMBED_ORIGINS` (https origins only; `http://localhost` outside production).
123
124## Tests
125
126`npm test` runs each `test/*.test.js` in its own process through `openvibe-shared/test-runner`, with
127PGlite and in-process Network/Events stubs. It covers the device and operator WebSocket gates, the
128REST routes, pairing paths, profiles and kits, the owner fence, the optional Stop, the outbox, routines,
129webhooks, jobs/metering, the embed/watch limits, the simulator and the ONVIF camera against a stub.
130`npm run test:pg` runs the same on PostgreSQL. `ov test` is the harness wrapper.
131
132## Conventions
133
134- CommonJS, 4-space indent, single quotes, semicolons; domain logic in `server/domain/` and
135 `server/`, HTTP in `server/api`/`server/web`, injectable config/db/clock/fetch/log for tests.
136- Robot profiles and layouts are data (`bot.robot-profile@1`, `ui.surface@1`, `ui.layout@1`), not
137 per-robot code; every control in a layout still goes through the same gate.
138- Secrets: device credentials, pairing codes, publish keys and WHIP URLs never reach a log, an
139 outbox event, an API response after their one showing, or a file.
140- Errors are problem+json with stable codes; egress only to configured hosts (ONVIF one host, no
141 redirects; webhooks public addresses only, no redirects).
142
143## Pitfalls
144
145- **Stop is one optional part, off by default.** It sends the robot's own `halt`; nothing latches and nothing
146 clears. The tray's own Stop stays local, and `halt` always passes the gate.
147- **Never widen the owner fence.** A person naming another subject, a service, a node token or
148 `svc:actor` without the owner's switch must be refused.
149- **Device credentials are hashed and read-once.** Do not add a readable copy or a query-string form.
150- **Webhooks are written in the transaction that emits the event**, signed and retried 30 s, 2, 10,
151 30, 120 min; never post inline and lose the row.
152- **ONVIF is one configured host, no embedded credentials, no redirects**, with secret refs only.
153- **The simulator is not a device**: no jobs, not counted online, no outbox presence.
154- **Embedding is opt-in** (`embed_public`), and `/api/v1` never exposes the flag.
155- **Do not put a capability in a layout** — layouts arrange, the gate authorizes.
156- **The front page CSP is separate**; a new Network/Frame asset must be added to `HOME_CSP` only, or
157 to the site-wide policy if it is used elsewhere.
158
159## Documentation
160
161- [README.md](README.md) — the product, devices, limits, security and deploy.
162- [docs/protocol.md](docs/protocol.md) — the device/control/watch WebSocket protocol.
163- [docs/live-controls-migration.md](docs/live-controls-migration.md) — where Live's old controls went.
164- [docs/controls-embed.md](docs/controls-embed.md) — embedding, tickets and CSP.
165- [docs/ai-offers.md](docs/ai-offers.md), [docs/machine-access-relay.md](docs/machine-access-relay.md).
166- ADR-043 is the binding contract; `openvibe-contracts` holds `bot.*` shapes and capabilities.
167