openvibers/openvibe.ai AGENTS.md
AGENTS.md
AGENTS.md — OpenVibe.AI
Project overview
The AI authority of the OpenVibe network: providers, models, routing, versioned prompt templates and
workflows, runs, citations, scoped cache, quotas, free allowance, abuse limits, BYO keys and the public
AI Exchange API. Node.js/Express, PostgreSQL (ov_ai), CommonJS, no build step. Extracted from
OpenVibe.Live's AI subsystem; Live now runs with AI_SERVICE=remote.
- Service id
ai, port4700, public originhttps://ai.openvibe.services, API host
https://api.openvibe.exchange, unitopenvibe-ai.service. - The reusable rule: a run returns a draft/evidence package (structured output + citations +
provenance) attributed to a workflow version, model and run id — never to a person. AI never owns
publication truth; Wiki, Blog, News, Live, Report and the rest decide what to publish.
What it owns, and what it must not touch
Owns the eleven record groups in migrations/: providers, models, routing profiles, prompt
templates, workflow definitions, runs, the request log (hashes, not prompts), citations, cache,
quotas/usage and audit. Also owns people's provider keys (server/credentials.js,
server/vault.js), people's own machines as providers (server/machines.js), free allowance
(server/free-allowance.js), tiers/budgets (server/govern.js, server/run-budgets.js), abuse
prevention (server/abuse/), the public price book and Exchange APIs (server/api/exchange.js,
server/pricebook/), GPU supply/provisioning (server/gpu/, server/platform/supply.js),
batches, deep dives, memory and the ai.run.* events.
Does not own: publication (it drafts/evidences), the recordings it analyses (OpenVibe.Media),
identity (OpenVibe.Network), or the meaning of a product's prompt (each workflow's owner).
Who calls it, and whom it calls
- Callers: every first-party product, with an OpenVibe.Network service token audience
openvibe.aiand the route's capability (ai.run.create,ai.workflow.manage,ai.app.run, …).
Live (AI_SERVICE=remote), Network (footer site copy), Actor (model routing), Blog (devblog
agent), Exchange pages and developer apps are the current callers. - Depends on: PostgreSQL 18 through
openvibe-sdk/db; Network (server/auth.js: JWKS, service
tokens, SSO for the console); Events (ai.run.queued|succeeded|failed|cachedoutbox, and the
bench.rating.changedsubscription); Bench (ratings/cost boards for routed evidence); Media
(recordings read over allow-listed https/signed URLs); Bot (bot.machine.aifor a person's own
machine); provider APIs, whisper.cpp andopenvibe-llm.serviceon this host.
Repository map
- Entry: [server/index.js](server/index.js) —
start()wires config, database, registry, provider
pool, cache, quotas, runs, engine, auth, subscriptions and telemetry; also what tests call. - App: [server/app.js](server/app.js) — Express middleware, CSP headers, CORS for the public
reads, the/api/v1,/internal,/you,/consolemounts,/shared, legal pages. - Config: [server/config.js](server/config.js) — pure
load(env); secrets are only ever read by
name throughsecretRef(). - Database: [server/db.js](server/db.js) opens/migrates
ov_ai; schema in
[migrations/](migrations/) (currently0001_initial.sql…0081_deep_dives.sql). - Auth: [server/auth.js](server/auth.js) —
openvibe-contractsserviceAuth+ capabilities; the
JWKS key comes fromopenvibe-sdk/auth. Person sessions in [server/person-session.js](server/person-session.js). - API: [server/api/](server/api/) —
runs.js,admin.js,exchange.js,openai.js,
messages.js(Anthropic-compatible),models.js,credential.js,batches.js,actions.js. - Workflows: [server/workflows/engine.js](server/workflows/engine.js), definitions and seed in
[server/workflows/](server/workflows/). - Providers: [server/providers/](server/providers/) — adapters, pool, circuits, fallbacks.
- Registry/run state: [server/registry.js](server/registry.js), [server/runs.js](server/runs.js),
[server/cache.js](server/cache.js), [server/quota.js](server/quota.js). - Public pages: [server/home.js](server/home.js), [server/openapi.js](server/openapi.js);
server-rendered run receipts in [server/run-pages.js](server/run-pages.js). - Console: [server/console/](server/console/) — operator console, Network staff only.
- Capture worker: [server/capture/worker.js](server/capture/worker.js) — headless Chrome for deep
dives, separate unitopenvibe-ai-capture.serviceon127.0.0.1:4711.
Commands
npm install # or npm ci
cp .env.example .env
npm run dev # NODE_ENV=development, http://localhost:4700
npm start # node server/index.js
npm test # every test/*.test.js
npm test -- quota cache # only files whose name contains one of the words
ov test # the harness's sharded run of what the change reaches
node --check <file.js> # syntax check; no linter/formatter configured
Without a provider key everything falls back to the deterministic stub provider, so no test needs the
network or a paid key.
Database and migrations
PostgreSQL database ov_ai on the host's data role (ADR-035). Without DATABASE_URL, development and
tests use embedded PGlite. Every read/write is async through openvibe-sdk/db; an INSERT that needs
its id uses RETURNING id. Migrations are migrations/NNNN_name.sql, applied at boot; never edit a
migration that has run — ov_migrations records its checksum (see
[docs/cutover-pr-openvibe-ai-15.md](docs/cutover-pr-openvibe-ai-15.md)). Add a new number instead.
Migrations only add tables and columns.
Auth and capabilities
Every API route needs a Network service token for audience openvibe.ai, the route's capability and,
for runs, a namespace the token's ns claim allows (namespaces fail closed). Capability ids are
decided by the pinned openvibe-contracts release: an unknown id is refused, never allowed. The
operator console is Network staff only and needs AI_CONSOLE_SESSION_SECRET and
OV_OAUTH_CLIENT_SECRET; person routes under /api/v1/me/* accept only the person's own token or
AI's same-origin session — never a service token or on_behalf_of. Developer-app tokens
(sub app:app_<ULID>) are accepted only on ai.app.run routes and act as their project.
HTTP surface (short)
- Service-to-service:
POST /api/v1/runs,GET /api/v1/runs/:id, workflows, providers, models,
credentials, quotas, usage and audit under/api/v1/*. - Developer apps:
POST /api/v1/{chat,generate,summarize,classify,extract,embed}and
GET /api/v1/runs/:id, rate-limited per address on the public vhost. - Exchange: the OpenAI-compatible
POST /v1/chat/completionsand Anthropic-compatible
POST /v1/messages, model lists, read APIs and/openapi.json, served on
api.openvibe.exchange. - Health:
/api/health,/api/ready;/metricsfor direct loopback callers only.
Frontend loading model
There is no SPA and no build step. Human pages are server-rendered strings with the console's
classes and CSP (no external script on run receipts); the public human pages moved to
OpenVibe.Exchange in 2026-11-07 and this service 301s them by name. /shared/* is served from the
pinned openvibe-shared package in node_modules by openvibe-shared/serve. Never edit
node_modules; change the package and bump the pinned release.
Deploy
sudo ovhost deploy ai (strategy git-checkout): fetch, fast-forward /opt/openvibe.ai, install on
a lockfile change, restart, wait for /api/ready, roll back automatically on failure. Unit
openvibe-ai.service, env /etc/openvibe/ai.env (0600), vhost
[deploy/nginx/ai.openvibe.services.conf](deploy/nginx/ai.openvibe.services.conf). The release migrates
ov_ai at boot. sudo ovhost rollback ai --to <sha>; sudo ovhost drill ai for a restore drill.
Never put a secret in a command line, a file or a log.
Tests
npm test runs test/run.js, which executes each test/*.test.js in its own process through
openvibe-shared/test-runner (PGlite by default; npm run test:pg uses PostgreSQL). Local servers on
random ports, generated signing keys, the stub provider. A test that cannot run prints
<label>: skipped (<why>) and is not counted as passed; -- --strict makes a skip fail. Read
test/README-style guarantees in the README's "Acceptance" table: every guarantee names its test.
ov test is the harness wrapper for the same suite.
Conventions
- CommonJS (
require/module.exports), 4-space indent, single quotes, semicolons. camelCaseJS,snake_casePostgreSQL columns/tables.- Errors are RFC 9457
application/problem+jsonfromopenvibe-contractshttp; never return raw
provider errors or secrets. - The request log keeps hashes, not prompts; raw prompts and inline images are not stored; cache
entries never cross requester/actor/target/attribution scope. openvibe-contracts,openvibe-sdkandopenvibe-sharedare pinned release tarballs in
package.json; bump by changing the tag, never by editing installed files.- CORS: exact origins for
/statsand/release.json,*only for the public creation catalogue;
token and console routes never use that list.
Key pitfalls
- Capabilities come from the pinned contracts release. A feature that adds a capability must bump
openvibe-contractsfirst, or the route refuses every caller. - Namespaces fail closed. A service token without
nsruns nothing; do not add a fallback. - The public pages are Exchange's. Do not re-grow HTML here; redirect instead.
- Never edit an applied migration (checksum recorded) and never create a table at runtime.
- Secrets by name only.
env:NAMEreferences, AES-256-GCM for people's keys with
AI_CREDENTIALS_KEY; a response or log must never carry a value. - Egress is allow-listed. Media is fetched only from approved OpenVibe https hosts with every DNS
answer and redirect hop re-checked (test/ssrf.test.js). - Quotas refuse before any provider call, with 429 +
Retry-After; abuse limits and the free
allowance run before metered capacity. /metricsis host-local. Do not expose it on the public vhost.- The capture worker is separate on purpose (memory cap, one browser); a page that eats memory
must not take the API down. - Docs drift: the README's "Depends on" version list can lag
package.json;package.jsonis
what runs.
Documentation
- [README.md](README.md) — the full product, API and acceptance record.
- [docs/migration.md](docs/migration.md) — cutover from Live and the
ov_ailayout. - [docs/abuse-prevention.md](docs/abuse-prevention.md) — trust tiers, device cookie, proof of work.
- [docs/provider-matrix.md](docs/provider-matrix.md) — what may be resold and what is UNVERIFIED.
- [docs/provider-adapters.md](docs/provider-adapters.md), [docs/provider-registry.md](docs/provider-registry.md).
- [docs/ai-exchange-baseline.md](docs/ai-exchange-baseline.md), [docs/exchange-platform.md](docs/exchange-platform.md).
- [docs/memory-m5.md](docs/memory-m5.md), [docs/private-ai.md](docs/private-ai.md),
[docs/measured-metrics.md](docs/measured-metrics.md), [docs/deep-dive.md](docs/deep-dive.md). - Contracts live in OpenVibe.Contracts; the service's own schemes are documented in
README.md.