openvibers/openvibe.ai Commits

README and STATUS.json are current (roadmap WS-U task 3): the README gains Purpose (the opening paragraph), Owns, Does not own, Depends on, Capabilities (all seven ai.* capabilities, including ai.credential.manage and ai.quota.attribution.manage, and the grants AI uses at Events and Network) and Security sections; the guarantees table is the Acceptance section; Deploy opens with sudo ovhost deploy ai (unit, port, env file, the local model unit, rollback). Not done yet no longer lists ai.run.* events or people's own provider keys (both built) and dates the citation and passthrough notes. STATUS.json names openvibe-contracts v0.75.0, the seven capabilities and the local model, gains features, dates the Live patch note and is updated 2026-09-28.

OP OpenVibers committed 28 Sept 2026 · 6c8b8338be · 2 files, +97 −11


Changes

ddiff --git a/README.md b/README.md
iindex 195aed8..2db1647 100644
---- a/README.md
++++ b/README.md
@@@ -10,11 +10,53 @@ page until the launch rule below is met.
**Plan:** OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026), §12.1, §12.13, §15.14, §33, §34.
**License:** AGPL-3.0 (same as every OpenVibe service).
++## Purpose
++
The reusable AI authority extracted from OpenVibe.Live. Products run registered, versioned
workflows; every run returns a **draft/evidence package** (structured output + citations +
provenance) attributed to a workflow, its version, a model and a run id — never to a person. AI never
owns publication truth: Wiki, Blog, News, Live, … decide what to publish.
++## Owns
++
++- the eleven record groups ([below](#the-eleven-record-groups)): providers, models, routing profiles,
++ prompt templates, workflow definitions, runs, the request log (hashes, not prompts), citations, the
++ cache, quotas and usage, and the audit log, in AI's own SQLite
++- people's own provider keys (`ai.credential.manage`), encrypted at rest, and per-streamer
++ attribution quotas (`ai.quota.attribution.manage`)
++- the `ai.run.queued|succeeded|failed|cached` events and the `ai.preferences` / `ai.usage_summary`
++ user modules' AI side
++
++## Does not own
++
++- publication: every run is a draft/evidence package; Wiki, Blog, News, Live and the rest decide what is
++ published
++- the recordings it analyses (OpenVibe.Media), identity (OpenVibe.Network), the products' prompts'
++ meaning (each workflow's owner)
++
++## Depends on
++
++- OpenVibe.Network (JWKS, service tokens, SSO for the operator console, user modules)
++- OpenVibe.Events (the `ai.run.*` outbox relay; off without `EVENTS_URL`)
++- OpenVibe.Media (recordings `media.analyze` reads, over allow-listed https URLs or signed URLs)
++- provider APIs configured by the operator (OpenAI-compatible, Anthropic, the HTTP seam), whisper.cpp
++ and the local model server `openvibe-llm.service` on this host
++- `openvibe-contracts` v0.75.0, `openvibe-sdk` v0.11.0 (service tokens, events outbox),
++ `openvibe-shared` v1.22.0, pinned by release tarball
++
++## Capabilities
++
++Implemented here (the service manifest's `capabilities`, audience `openvibe.ai`; routes under
++[API](#api)): `ai.run.create`, `ai.run.read`, `ai.workflow.manage`, `ai.provider.manage`,
++`ai.usage.read`, `ai.credential.manage` (`/api/v1/credentials/:subject`) and
++`ai.quota.attribution.manage` (per-streamer quotas). A token's `ns` claim limits which workflow
++namespaces it may run.
++
++Called elsewhere, as the service principal `ai`: `events.event.publish` (Events, the outbox relay)
++and `network.modules.read` / `network.modules.write` for `ai.preferences` and `ai.usage_summary`
++(Network). Other services grant `ai` their own draft capabilities (for example
++`blog.post.create`, `news.story.revise`, `reviews.summary.propose`) to receive drafts.
++
## Run it
```bash
@@@ -212,7 +254,9 @@ streams always win: 2 threads, at most 1.5 CPUs, nice 19, 3 GB. It uses about 0.
28 tokens/s. Without it, `media.analyze` writes an extractive overview and says so. To swap the model, put the
file beside it, change `--model`/`--alias` and `AI_LOCAL_LLM_MODEL`, and restart both units.
--## Guarantees and where they are tested
++## Acceptance: guarantees and where they are tested
++
++`npm test` runs every `test/*.test.js` with the stub provider, temp databases and local servers only.
| Guarantee | Test |
|---|---|
@@@ -232,6 +276,25 @@ file beside it, change `--model`/`--alias` and `AI_LOCAL_LLM_MODEL`, and restart
| Import from a Live snapshot: dry run, holds, idempotent re-run | `test/import.test.js` |
| Capability proposals are valid contracts documents matching what is enforced | `test/proposals.test.js` |
++## Security
++
++Reporting a vulnerability: [SECURITY.md](SECURITY.md). The rules the code keeps:
++
++- **Auth.** Every API route needs a Network service token for audience `openvibe.ai`, with the route's
++ capability and, for runs, a namespace its `ns` claim allows. Runs are private to their requester.
++ The operator console is Network staff only, signed in with SSO and PKCE, with CSRF on every write.
++- **Secrets.** Shared provider keys are `env:NAME` references, never values; a person's own key is
++ AES-256-GCM encrypted with `AI_CREDENTIALS_KEY`, bound to its owner and subject, and never returned
++ (a four-character hint only). No response, log or console page carries a secret value.
++- **Private data.** The request log keeps hashes, not prompts; raw prompts and inline images are not
++ kept; the cache never crosses requester, actor, target or attribution; `history: false` keeps
++ neither input nor cache entry.
++- **Egress.** Provider calls go only to configured providers. Media is fetched only from allow-listed
++ https OpenVibe hosts, with every DNS answer and redirect hop re-checked and size caps
++ (`test/ssrf.test.js`); ffmpeg reads through a loopback proxy that applies the same rules.
++- **Abuse.** Quotas refuse with 429 before any provider call; queue caps per caller and overall.
++ `/metrics` answers direct loopback callers only.
++
## Live and Network
`docs/live-patch.diff` added `AI_SERVICE=remote` to Live (deployed as Live `fa22de5`; production runs
@@@ -246,6 +309,14 @@ and rollback are in `docs/migration.md`.
## Deploy
++Production deploys with `sudo ovhost deploy ai` on the host (strategy `git-checkout`: fetch,
++fast-forward `/opt/openvibe.ai`, install on a lockfile change, restart, wait for `/api/ready`).
++The unit is `openvibe-ai.service` on `127.0.0.1:4700`, the env file `/etc/openvibe/ai.env`. The local model
++runs as `openvibe-llm.service` on 127.0.0.1:8090 ([Local model](#local-model)).
++Rollback: ovhost puts the previous sha back by itself when `/api/ready` does not answer 2xx after the
++restart; afterwards `sudo ovhost rollback ai --to <sha>`. Nothing blocks a rollback: the schema
++code only adds tables and columns.
++
Deployed: `/opt/openvibe.ai`, env `/etc/openvibe/ai.env` (0600), unit `deploy/systemd/openvibe-ai.service`
(`StateDirectory=openvibe-ai`, database `/var/lib/openvibe-ai/ai.db`), principal `ai`. The nginx vhost
`deploy/nginx/ai.openvibe.network.conf` (health/ready, `/`, `/robots.txt`, the operator console
@@@ -257,9 +328,9 @@ Deployed: `/opt/openvibe.ai`, env `/etc/openvibe/ai.env` (0600), unit `deploy/sy
## Not done yet
--- `ai.run.*` events to OpenVibe.Events (and their schemas in Contracts); per-actor (BYO) provider
-- secrets; Live's VOD and clip overviews calling `media.analyze` instead of extracting frames and transcribing
-- themselves; moving the passthrough prompts into AI templates;
++- Live's VOD and clip overviews calling `media.analyze` instead of extracting frames and transcribing
++ themselves; moving the remaining passthrough prompts into AI templates (most `live.*` workflows are
++ versioned templates since 2026-09-27);
removing Network's fallback to Live's `/internal/ai/site-copy`; a public server-rendered status page.
- The operator console is built but not reachable yet: it needs the vhost below installed (which
replaces the Sites placeholder), `https://ai.openvibe.network/auth/callback` listed as a redirect URI
@@@ -267,7 +338,8 @@ Deployed: `/opt/openvibe.ai`, env `/etc/openvibe/ai.env` (0600), unit `deploy/sy
- Import holds from the 2026-09-23 run: a streamer's own provider key stays in Live, and 3,021 Live
translations cannot become cache entries (they have no source text) and stay in Live.
- No fallback is declared on any production route, so an outage of the one real provider fails every
-- Live AI feature; `live.*` and `network.site_copy` outputs carry no citations or gaps (0 citation rows).
++ Live AI feature. On 2026-09-23 `live.*` and `network.site_copy` outputs carried no citations or gaps
++ (0 citation rows); runs record grounding (cited sources and named gaps) since 2026-09-25.
- The host has a backup of `ai.db` but no restore drill has run for it; `/metrics` is built but not
deployed yet.
ddiff --git a/STATUS.json b/STATUS.json
iindex 02405f8..efc0ab9 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -6,12 +6,26 @@
"plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
"runtime": "node 22, express, better-sqlite3",
"port": 4700,
-- "domain": "ai.openvibe.network still serves the OpenVibe.Sites placeholder; the service runs on openvibe-ovh (/opt/openvibe.ai, release bdca064, unit openvibe-ai) on 127.0.0.1:4700, loopback only, used by OpenVibe.Live and OpenVibe.Network",
-- "contracts": "openvibe-contracts v0.33.0",
-- "capabilities": ["ai.run.create", "ai.run.read", "ai.workflow.manage", "ai.provider.manage", "ai.usage.read"],
++ "domain": "ai.openvibe.network still serves the OpenVibe.Sites placeholder; the service runs on openvibe-ovh (/opt/openvibe.ai, unit openvibe-ai) on 127.0.0.1:4700, loopback only, used by OpenVibe.Live and OpenVibe.Network",
++ "contracts": "openvibe-contracts v0.75.0",
++ "capabilities": ["ai.run.create", "ai.run.read", "ai.workflow.manage", "ai.provider.manage", "ai.usage.read", "ai.credential.manage", "ai.quota.attribution.manage"],
"capabilitiesRegistered": true,
-- "providers": ["stub", "openai-compatible", "anthropic", "http-seam", "whisper.cpp"],
-- "livePatch": "applied: Live fa22de5 runs AI_SERVICE=remote in production since 2026-09-23 ~15:34 UTC (309 runs by 18:30 UTC across 13 live.* workflows and 1 network.site_copy, 0 failed). Not moved yet: VOD captions/whisper and streamers' own provider keys stay in Live; most Live features are passthrough prompts built by Live; the deployed Network still falls back to Live's /internal/ai/site-copy; scripts/import-from-live.js ran on production at 2026-09-23 18:29 UTC (79,657 ledger rows: 79,647 live.ai_usage, 2 channel_ai_config, 8 site_settings; 11 holds: provider settings and the key stay env references, 1 streamer BYO key stays in Live, 3,021 translations are unrepresentable and stay in Live); every route has fallbacks=[]; 0 citation rows; no /metrics",
++ "providers": ["stub", "openai-compatible", "anthropic", "http-seam", "whisper.cpp", "local llama.cpp server (openvibe-llm.service)"],
++ "livePatch": "applied: Live fa22de5 runs AI_SERVICE=remote in production since 2026-09-23 ~15:34 UTC (309 runs by 18:30 UTC across 13 live.* workflows and 1 network.site_copy, 0 failed). As of 2026-09-23, not moved yet: VOD captions/whisper and streamers' own provider keys stayed in Live and most Live features were passthrough prompts built by Live (since 2026-09-27 AI has live.media.transcribe, stored provider keys and versioned templates for most live.* workflows); the deployed Network still falls back to Live's /internal/ai/site-copy; scripts/import-from-live.js ran on production at 2026-09-23 18:29 UTC (79,657 ledger rows: 79,647 live.ai_usage, 2 channel_ai_config, 8 site_settings; 11 holds: provider settings and the key stay env references, 1 streamer BYO key stays in Live, 3,021 translations are unrepresentable and stay in Live); every route has fallbacks=[]; 0 citation rows; no /metrics",
"deployed": true,
-- "updated": "2026-09-23"
++ "updated": "2026-09-28",
++ "features": [
++ "versioned templates, workflows and routing profiles",
++ "runs with idempotency, cancel, retry, async polling and restart recovery",
++ "provider fallback and circuit breakers",
++ "quotas before any provider call, including per-streamer attribution quotas",
++ "scoped cache",
++ "citations or explicit gaps on every run",
++ "people's own provider keys, encrypted at rest",
++ "media.analyze: local-first recording analysis with windowed speech-to-text",
++ "ai.run.* events through the SDK outbox",
++ "ai.preferences and ai.usage_summary user modules",
++ "server-rendered operator console for Network staff",
++ "Live import"
++ ]
}