openvibers/openvibe.chat Commits

AGENTS.md: the context an agent starts from (#539)

OP OpenVibers committed 1 days ago · 7149ac0793 · 2 files, +167 −0

Changes

ddiff --git a/AGENTS.md b/AGENTS.md
nnew file mode 100644
iindex 0000000..ef11196
---- /dev/null
++++ b/AGENTS.md
@@@ -0,0 +1,166 @@
++# AGENTS.md — OpenVibe.Chat
++
++## Project overview
++
++The communication authority of the OpenVibe network: rooms, messages, DMs, group DMs, voice channels
++and calls, TTS/sounds, moderation, presence, and both of Live's chat paths. It was moved out of Live
++behind a service boundary (Wave 6) and has been the authority for `openvibe.live` chat in every mode
++since 2026-09-23; Live runs no chat of its own. Node.js/Express + `ws`, PostgreSQL, CommonJS, **no
++build step**.
++
++- Origin `https://openvibe.chat`; also served on **Live's origin**: nginx routes `/ws/chat`,
++ `/api/chat/`, `/api/dm/`, `/api/tts/`, `/api/sounds`, `/api/emotes` (and, after the calls cutover,
++ `/ws/call` and the call routes under `/api/streams`) to Chat.
++- Port `4400`, unit `openvibe-chat.service`, release root `/opt/openvibe.chat`; `openvibe.party` is
++ Chat's Watch Together host.
++
++## What it owns
++
++- Rooms: global, stream, channel, community, DM/group DM, call, system; servers (groups of rooms as
++ text and voice channels, with roles and invites).
++- Messages, replies/rich payloads, edit history, membership/roles, unread/read state, presence and
++ typing (ephemeral).
++- Moderation state: bans, timeouts, delete/purge, filters, slow/member-only modes, the moderation
++ audit and the moderation read API Live uses (`GET /internal/moderation/*`).
++- TTS/audio/soundboard/channel-sound queues with skip/clear/failure; the call signalling metadata and
++ the `pending/ringing/active/ended/missed/declined/failed` lifecycle.
++- The tables moved from Live: `chat_messages`, the DM tables, `tts_voice_overrides`,
++ `channel_sounds`, `relay_users`, `hidden_relay_users`, `pending_ip_messages`,
++ `stream_first_chats`, `moderation_actions`, plus `channel_moderators`,
++ `channel_moderation_settings`, `emotes`, `user_tags`, `chat_ai_summaries`,
++ `chat_timeline_events`. Live keeps no copy and reads through the internal API.
++- Rooms, Vibe (the assistant), support inboxes, the website widget, feeds, `openvibe.party`, agent
++ rooms and chat apps.
++
++**Stays in Live:** `media_requests` and `media_request_settings`. Do not grow Live's chat tables back.
++
++## Who calls it, and whom it calls
++
++- Callers: browsers on `openvibe.chat` and Live's origin; Live (internal reads, bridge and event
++ consumer); Bot (chat-app bridge); Actor (asks); other services with a Network service token
++ audience `openvibe.chat` and a `chat.*` capability.
++- Depends on: Network (identity, SSO, service tokens, the `chat.preferences` module, VIP badges),
++ Live (channel/stream context through `server/live-context.js` — the only module that talks to Live),
++ Events (outbox and subscriptions), Media (emote image bytes, namespace `chat`), AI
++ (`server/ai/`, off unless enabled), VIP (optional; without it nobody has a badge), Tools/Search
++ where the assistant needs them.
++
++## Repository map
++
++- **Entry:** [server/index.js](server/index.js) — database, first Live sync, chat and call WebSocket
++ servers, HTTP app with the Events consumer and `POST /internal/events`, background loops,
++ subscription check, graceful stop (tells clients the chat is restarting).
++- **App:** [server/app.js](server/app.js) — Live's guards for these routes: credentialed CORS for an
++ exact origin list, `/api` rate limit, IP/network bans, `ov_banned` cookie, WS origin+IP checks.
++- **Config:** [server/config.js](server/config.js); database [server/db/database.js](server/db/database.js)
++ (`openDb`, migrations via `DATABASE_DIRECT_URL`).
++- **Chat core:** [server/chat/](server/chat/) — `chat-server.js`, `dm.js`, `dm-routes.js`,
++ `history-store.js`, `moderation-utils.js`, `word-filter.js`, `deploy-notice.js`, the T3 APIs.
++- **Calls:** [server/calls/](server/calls/) — call server, lifecycle, REST routes, Live stream hooks.
++- **Rooms/servers:** [server/rooms/](server/rooms/) — `rooms.js`, `servers.js`, `vibe.js`,
++ `workspace.js`, parties, memory, feeds.
++- **Events:** [server/events/](server/events/) — `service-outbox.js`, `consumer.js`, `subscriptions.js`.
++- **Auth/net:** [server/auth/](server/auth/), [server/net/](server/net/) (service tokens, client IP).
++- **Browser:** [public/web/](public/web/) — `chat.js`, `chat-shell.js`, `call.js`, `vibe.js`,
++ `widget.js`, `embed.js`, `party.js`, `feed-view.js`, plus their CSS.
++- **Deploy:** [deploy/systemd/openvibe-chat.service](deploy/systemd/openvibe-chat.service),
++ [deploy/nginx/openvibe.chat.conf](deploy/nginx/openvibe.chat.conf),
++ [deploy/nginx/openvibe.live-chat.locations.conf](deploy/nginx/openvibe.live-chat.locations.conf),
++ [deploy/nginx/calls/openvibe.live-calls.locations.conf](deploy/nginx/calls/openvibe.live-calls.locations.conf).
++
++## Commands
++
++```bash
++npm ci
++npm run dev # NODE_ENV=development node server/index.js
++npm start # node server/index.js, http://127.0.0.1:4400
++npm test # test/run.js: every file in its own process, PGlite per process
++npm run test:pg # same on PostgreSQL + PgBouncer + Valkey (containers)
++npm test -- ws import # filter by file name; --strict makes a skip fail
++npm run n-1:record # record the N-1 fixtures after a deploy
++ov test # the harness's sharded run of what the change reaches
++```
++
++## Database and migrations
++
++Chat's own PostgreSQL database on the host's data role (`add-service.sh chat`); development and
++tests use embedded PGlite, PostgreSQL in production through PgBouncer. Migrations are
++`migrations/NNNN_*.sql`, applied at boot with the owner connection (`DATABASE_DIRECT_URL`), then the
++app serves through the pooled runtime role (`DATABASE_URL`, DML only). Never edit an applied
++migration. The moved tables keep Live's ids and columns plus Network subject columns
++(`subject_id`, `sender_subject_id`, …). `docs/parity.md` records the parity method.
++
++## Auth and the WebSocket rules
++
++- People sign in with a Network session JWT, verified against the Network key; tokens issued before a
++ subject's `token_valid_after` are refused. `hbt_` API tokens are resolved through Live.
++- A WebSocket upgrade authenticates with an `Authorization: Bearer` header only (bots); a browser's
++ socket starts anonymous and presents its token in the first message (`join`, `join_room`,
++ `auth-update`). **Neither the `ov_token` cookie nor `?token=` in the URL signs a socket in** (removed
++ 2026-10-10); a token in the URL gets a `system` frame saying to use the header or `join`.
++- The `ov_token` cookie only exempts staff from a network/IP ban at upgrade.
++- Services use client-credentials tokens for audience `openvibe.chat`, checked per capability
++ (`chat.live_bridge.write`, `chat.message.send`, `chat.presence.read`, `chat.moderation.read`, …).
++- DMs go only to participants, private rooms only to members; network blocks count like DM blocks; a
++ staff member reading someone else's chat is recorded as a moderation action.
++
++## Frontend loading model
++
++Browsers load plain, no-build scripts from [public/web/](public/web/) on Chat's own site; Live's
++origin keeps Live's existing chat JavaScript and talks to the same sockets/APIs (the Wave 6 rule was
++"move behind a service boundary before changing behaviour"). There is no SPA, bundler or transpiler.
++`openvibe-shared` supplies the Frame and `/shared/*` from the pinned node_modules. The widget is a
++shadow-root bubble under 3 KB compressed; embeds and `openvibe.party` are separate pages/scripts.
++
++## Deploy
++
++`sudo ovhost deploy chat` (git-checkout: fetch, fast-forward `/opt/openvibe.chat`, install on a
++lockfile change, restart, wait for **`/ready`**, not `/api/ready`). Unit `openvibe-chat.service` on
++`127.0.0.1:4400`, env `/etc/openvibe/chat.env`; vhosts
++[deploy/nginx/openvibe.chat.conf](deploy/nginx/openvibe.chat.conf) and the Live locations include
++(inside Live's `server { … }`). After a deploy, record the N-1 fixtures (`npm run n-1:record`).
++Rollback is a Chat release rollback (`sudo ovhost rollback chat --to <sha>`), never a switch back to
++Live. Migrations only add tables and columns.
++
++## Tests
++
++`npm test` runs each `test/*.test.js` in its own process (60 s) against one migrated PGlite database
++per process (`test/helpers/pg-preload.mjs`); `npm run test:pg` uses the PostgreSQL/PgBouncer/Valkey
++containers. The suite covers the WS gates and commands, DMs and privacy, history latency, moderation
++and staff audit, calls and capture-on-load, TTS/audio queues, rooms/servers/Vibe, the widget/embeds,
++feeds, chat apps, Events consumption, account export/deletion and the N-1 fixtures. `ov test` runs the
++same.
++
++## Conventions
++
++- CommonJS; 4-space indent in the newer modules, Live's original style preserved in the moved files.
++- `server/live-context.js` is the **only** module that talks to Live; do not call Live from routes.
++- Errors are problem+json or the chat frames' own stable codes; never leak a DM, a staff log or a
++ token into a response or a log.
++- CORS and WS origins are exact origins; no wildcard for credentialed requests.
++
++## Pitfalls
++
++- **A token in the socket URL or a cookie must not sign the socket in** (C-05/C-06). Keep the
++ first-message auth and the `Authorization` header for bots.
++- **Chat is required in every mode.** Live's chat routes are retired; do not add a fallback to Live.
++- **The CORS/WS origin list is exact.** `*.openvibe.tools` must not be honoured: a credentialed grant
++ would let it read signed-in DMs.
++- **`/internal/*` and `/metrics` must stay unreachable through nginx** (404 there); nginx sets the
++ client address from the connection only.
++- **DM privacy:** participants only, block-aware, and staff reads are audited moderation actions.
++- **Bans/mutes are Chat's**, and Live reads moderation through the internal API; `media_requests`
++ remains Live's.
++- **Readiness is `/ready`**, not `/api/ready` — the standard deploy probe assumes the latter.
++- **Do not "fix" a message by editing the store**: the Live bridge mirrors changes; use the service
++ paths so events, the bridge and Search stay consistent.
++- **N-1 fixtures** are part of shipping: record them after the deploy.
++
++## Documentation
++
++- [README.md](README.md) — the product, cutover, data, events, rooms, Vibe, inboxes, feeds, deploy.
++- [docs/platform.md](docs/platform.md) — agent rooms and the digest Code/Chat/Actor use.
++- [docs/chat-ingress.md](docs/chat-ingress.md) — `/internal/chat/*` tickets and reads.
++- [docs/calls-cutover.md](docs/calls-cutover.md), [docs/parity.md](docs/parity.md),
++ [docs/chat-apps.md](docs/chat-apps.md), [docs/party-pool.md](docs/party-pool.md).
++- `openvibe-contracts` holds `chat.*` shapes and capabilities.
ddiff --git a/CLAUDE.md b/CLAUDE.md
nnew file mode 100644
iindex 0000000..43c994c
---- /dev/null
++++ b/CLAUDE.md
@@@ -0,0 +1 @@