openvibers/openvibe.connect AGENTS.md

AGENTS.md

main 10.4 KB · 170 lines · markdown

1# AGENTS.md — OpenVibe.Connect
2
3## Project overview
4
5"Your computer, on the web, privately." Connect gives one port on a person's computer its own HTTPS
6address, `https://<name>--<you>.openvibe.link`, visible only to the people the owner picks, signed in
7with OpenVibe. A small Link client opens one outgoing WebSocket (the tunnel), so no router setting or
8open port is needed; Connect terminates TLS, runs the gate and proxies to the app through the tunnel.
9Node.js/Express + `ws`, PostgreSQL, CommonJS, **no build step**.
10
11- Canonical origin `https://connect.openvibe.link`; the old `*.openvibe.website` addresses redirect.
12 Share hosts are `<label>--<user>.openvibe.link` (a regex vhost).
13- Port `5240`, unit `openvibe-connect.service`, release root `/opt/openvibe.connect`.
14- Design: ADR-057 in OpenVibe.Contracts; CON-4/CON-16 are the Link and MCP decisions.
15
16## What it owns
17
18- Shares (a port, a name, an audience rule), their aliases and their lifecycle; the `shr_` id and the
19 one-time `ovc_` connect key.
20- The gate (who may open a share: owner, named people, signed-in, public), the tunnel registry, the
21 edge proxy (HTTP, WebSocket, SSE, 100 MB bodies) and the inspector.
22- A computer as an **OpenVibe Link** endpoint (`/api/v1/link/*`, node tokens, `openvibe-link share`),
23 the `link.egress` relay and the party desktop grant.
24- Project webhooks that wait for a computer (`server/hooks.js`), API keys for programs (`ovk_`/…, with
25 quotas) and the MCP OAuth 2.1 authorization server for shares that are MCP servers.
26- Pages, `/ov-connect.js` (the client, served as-is), `/openapi.json`, `/llms.txt`, agent descriptor,
27 `robots.txt`, `sitemap.xml`, `/release.json`, health/ready, `/metrics` (loopback only).
28- Its own PostgreSQL database (ADR-035; migrations applied at boot) and the `connect.*` events.
29
30Not owned: the tunnel's app (the person's own server), the Link machine/plugins (OpenVibe.Link),
31identity (Network), the sites Space builds and hosts (Space).
32
33## Who calls it, and whom it calls
34
35- Callers: people in the browser; the Link client on a person's computer (`/tunnel`); OpenVibe Link's
36 share commands; MCP clients (Claude and others) through the OAuth flow; other services through the
37 API with a Network service token and `connect.*`.
38- Depends on: **Network** (SSO with PKCE, JWKS, node tokens for Link machines,
39 `identity.subject.resolve`), **Events** (outbox; account export/deletion), and the person's
40 computer over the tunnel. There is no Media/AI/Live dependency in the serving path.
41- What the app behind a share sees: `Host: localhost:<port>`, `X-Forwarded-Host`, `X-Forwarded-Proto:
42 https`, `X-OpenVibe-User` and `X-OpenVibe-Subject` (set by the edge, never forwardable by a visitor),
43 the visitor's cookies for that share, WebSockets/SSE, bodies up to 100 MB.
44
45## Repository map
46
47- **Entry:** [server/index.js](server/index.js) — `createApp()`, HTTP listen, `/tunnel` upgrade;
48 lifecycle closes tunnels, proxy, gate, keys and telemetry.
49- **App:** [server/app.js](server/app.js) — the share-host branch comes **first** (gate → tunnel →
50 proxy), then `/`, `/shares/*`, `/grant`, `/api/v1/link/*`, `/api/v1/*`, `/ov-connect.js`, `/auth`,
51 `/internal/events`, machine endpoints, `/tunnel`.
52- **Config/db:** [server/config.js](server/config.js), [server/db.js](server/db.js).
53- **Shares/gate/tunnel:** [server/shares.js](server/shares.js), [server/edge/gate.js](server/edge/gate.js),
54 [server/edge/tunnels.js](server/edge/tunnels.js), [server/edge/proxy.js](server/edge/proxy.js),
55 [server/edge/inspector.js](server/edge/inspector.js), [server/edge/frames.js](server/edge/frames.js).
56- **Link:** [server/auth/link-auth.js](server/auth/link-auth.js), [server/machines.js](server/machines.js),
57 [server/link-egress.js](server/link-egress.js), [server/http/link-api.js](server/http/link-api.js),
58 [server/party-grants.js](server/party-grants.js).
59- **Programs:** [server/api-keys.js](server/api-keys.js), [server/oauth.js](server/oauth.js),
60 [server/http/oauth-routes.js](server/http/oauth-routes.js), [server/hooks.js](server/hooks.js),
61 [server/http/hooks.js](server/http/hooks.js).
62- **Pages/render:** [server/http/pages.js](server/http/pages.js), [server/views/index.js](server/views/index.js),
63 [server/render/layout.js](server/render/layout.js), [public/js/connect.js](public/js/connect.js).
64- **Client:** [public/ov-connect.js](public/ov-connect.js) — a single file, no install.
65- **Deploy:** [deploy/systemd/openvibe-connect.service](deploy/systemd/openvibe-connect.service),
66 [deploy/nginx/connect.openvibe.website.conf](deploy/nginx/connect.openvibe.website.conf),
67 [docs/link-share-rollout.md](docs/link-share-rollout.md).
68
69## Commands
70
71```bash
72npm install
73npm run dev # NODE_ENV=development node server/index.js; PGlite under data/pglite
74npm start # node server/index.js, http://127.0.0.1:5240
75npm test # test/run.js, temp PGlite, mock Network
76npm run test:pg # same suite on PostgreSQL (CONNECT_TEST_STORE=pg)
77npm test -- hooks # filter by file name; --strict makes a skip fail
78ov test # the harness's sharded run of what the change reaches
79```
80
81End-to-end from a computer: `node ov-connect.js --key ovc_… --port 8790`, or
82`openvibe-link share --share shr_…` on a Link machine.
83
84## Database and migrations
85
86Connect's own PostgreSQL database (ADR-035), PGlite in development/tests. Async through
87`openvibe-sdk/db`; migrations `migrations/NNNN_*.sql` are applied at boot — never edit an applied one,
88add a new number. Only hashes of API keys, tunnel tokens and OAuth tokens are stored; a webhook body
89is sealed at rest with a key derived from the service session secret and is never readable from the
90database by a reader. Rows that a share/computer no longer needs are removed at expiry.
91
92## Auth
93
94- **People** sign in through Network SSO (PKCE, HttpOnly cookie). A share's rule decides whether a
95 signed-in person may open it; the edge then sets `X-OpenVibe-User`/`X-OpenVibe-Subject`. The
96 visitor's own `Authorization` is consumed at the edge, never forwarded.
97- **Link machines** prove themselves twice at every connection: a Network node token
98 (`link:lnk_…`, audience `openvibe.link`, 5 minutes) and a one-use tunnel token from Connect (60 s)
99 for that share. A computer removed on Network stops serving within about five minutes because the
100 node token is refreshed and checked.
101- **MCP clients** use OAuth 2.1: `401` with RFC 9728 resource metadata, RFC 8414 server metadata,
102 RFC 7591 dynamic registration (public clients + PKCE only; https or loopback redirect URIs),
103 RFC 8707 `resource`, a 60-second one-use code and rotating `ovat_`/`ovrt_` tokens bound to the
104 share. Unknown clients/redirects get a page, never a redirect.
105- **API keys** for programs are shown once, stored hashed and carry quotas; a policy change, the owner
106 signing the app out or closing the share stops every token.
107
108## Frontend loading model
109
110Server-rendered pages and plain forms; the only page script is [public/js/connect.js](public/js/connect.js).
111`public/ov-connect.js` is the downloadable client and is served as-is. There is no SPA, bundler or
112transpiler; `openvibe-shared` supplies the Frame, legal/SEO helpers and `/shared/*` from the pinned
113node_modules.
114
115## Deploy
116
117Unit [deploy/systemd/openvibe-connect.service](deploy/systemd/openvibe-connect.service) runs
118`/opt/openvibe.connect/server/index.js` on `127.0.0.1:5240`, env `/etc/openvibe/connect.env` (0600).
119The vhost is the **combined** file
120[deploy/nginx/connect.openvibe.website.conf](deploy/nginx/connect.openvibe.website.conf), installed
121with `sudo ov-vhost-install …` and reloaded by that installer — it keeps both Website servers, the
122exact Link control host and the `<label>--<user>.openvibe.link` regex, with HTTP/1.1 Upgrade,
123streaming and no buffering. The repository does not document an `ovhost deploy connect` command; use
124the host's normal service environment and the staged rollout in
125[docs/link-share-rollout.md](docs/link-share-rollout.md). Do not install a second copy of the vhost
126(its maps and rate-limit zones are declared once) and do not replace the Website certificate.
127
128## Tests
129
130`npm test` runs each `test/*.test.js` in its own process with a temp PGlite database, an in-process
131Network mock and Chat/Live/Video fixtures. It covers share creation and the gate, both share aliases,
132tunnel auth and revocation, the Link API and egress relay, API key quotas, MCP OAuth (metadata,
133registration, PKCE, rotation), webhooks and their sealed bodies, the pages/llms/openapi, the nginx Link
134rules and owner settings. `ov test` runs the same.
135
136## Conventions
137
138- CommonJS, 4-space indent, single quotes, semicolons; factories with injectable config/db/clock/fetch/
139 log; the edge has its own modules and must not import page code.
140- Errors are problem+json on the API and small pages elsewhere; never echo an upstream app's body on a
141 proxy error.
142- Hostnames and origins are validated exactly: a share is `<label>--<user>.openvibe.link`
143 (lowercase letters/digits/hyphens; a Space site name can never contain `--`).
144- Secrets (connect keys, tunnel/OAuth tokens, webhook bodies) are shown once or sealed; never logged.
145
146## Pitfalls
147
148- **Order matters in the app:** the share-host branch must stay before every ordinary route, or a
149 share can reach Connect's own pages/API.
150- **The client only ever connects to `localhost`** on the port it was given; do not add a remote
151 target.
152- **A share key stops working while a Link computer serves the share**; "Use a connect key instead"
153 lets the computer go and shows a new key once.
154- **The tunnel token is one-use and 60 seconds**; the node token is 5 minutes and refreshed. Do not
155 cache or log either.
156- **`X-OpenVibe-User`/`X-OpenVibe-Subject` come from the edge only.** A visitor-supplied value must be
157 stripped.
158- **OAuth errors that must not redirect** (unknown client, bad redirect URI) render a page; never
159 redirect.
160- **One combined vhost only**; a second install duplicates maps/zones and breaks nginx.
161- **100 MB bodies and WebSockets are part of the product**; a proxy change that buffers or caps them
162 lower is a regression.
163- **Store a webhook body sealed** and remove it at expiry; a plaintext copy is a bug.
164
165## Documentation
166
167- [README.md](README.md) — what a share is, the gate, Link, MCP OAuth, API and run instructions.
168- [docs/link-share-rollout.md](docs/link-share-rollout.md) — the staged host rollout and commands.
169- ADR-057 (Link shares) in OpenVibe.Contracts; CON-4/CON-16 track the work.
170