openvibers/openvibe.design AGENTS.md
AGENTS.md
AGENTS.md — OpenVibe.Design
Project overview
The Design room: the theme studio where a person browses OpenVibe's themes, tries them on previews, edits
one with WCAG contrast checks, uses it, submits it to the gallery and rolls back to an earlier setting;
Design Studio v1 (/create) where one prompt becomes three model variants; and the Design Arena
where two outputs are judged blind at phone and desktop sizes and an Elo leaderboard per category ranks
the designers with cost and time. Node.js/Express, PostgreSQL (or PGlite in development), CommonJS, no
build step.
- Service id
design, port5090, originhttps://openvibe.design, unitopenvibe-design.service,
release root/opt/openvibe.design. - It stores no accounts and no tokens: the person's Network session lives in httpOnly cookies and their
theme lives in OpenVibe.Network; Arena rounds, entries, judgements and ratings live in
OpenVibe.Bench. The legacyarena_*tables remain only for historical account events/migration. - Sign-in is an OAuth 2 client of Network (
design, authorization code + PKCE S256, state check, server-
side exchange). Every theme read/write is proxied to Network's/api/themeswith the person's token.
What it owns
- The studio UI and its derived token set, the contrast checks, previews (Live channel, Chat room, shop
grid), colour-vision views, gallery submission, history and restore. - The token files (
/tokens/<theme>.<ext>) every tool can read, the icon/font/typeface endpoints, the
component registry, patterns and vectors, brand motion and thechangeshistory. - Design Studio (
/create,/d/:id): three model variants through AI route pins, an HTML sanitizer,
phone/tablet/desktop canvases, guest drafts (private cookie, max three) and per-day limits. - The Design Arena surface, its sanitized HTML in sandboxed CSP-capped iframes, blind voting and the
open JSONL dataset; Bench is the rating/storage authority. - The design-owned rows: designs, components, gallery/element records, deep-dive proposals, account-data
receipts and the migration's legacy tables. - The pages (
/,/studio,/t/:slug,/arena/*,/create,/d/:id,/developers,/brand), the
MCP endpoint, the API and discovery files.
Not owned: theme selection/storage (Network), Arena ratings/rounds (Bench), model execution (AI), the
shared Frame/kit (openvibe-shared), identity.
Who calls it, and whom it calls
- Callers: people in the browser; MCP clients; sites/tools reading
/tokens/*, icons, fonts; services
with a Network service token audienceopenvibe.designand the route's capability. - Depends on: Network (SSO,
/api/themes, projects/grants); AI (generate jobs,ai.run.create,
Design's client credentials); Bench (Arena rounds/entries/judgements/ratings, including
bench.entry.create); Events (account export/deletion); Publishing (SEO/SSR/index); PostgreSQL
andopenvibe-sharedfor the Frame/kit/legal/SEO. The Bench scope must includebench.entry.createin
addition to the Arena capabilities.
Repository map
- Entry: [server/index.js](server/index.js) —
createApp(), listen, graceful stop. - App: [server/app.js](server/app.js) — helmet/CSP,
/auth,/internal/events,/internal/config,
/shared,/css,/js,/brand/motion,/changes,/api/v1/designs,/api/v1/gallery,/api
(themes proxy),/api/create,/api/v1/arena,/api/v1/brand,/tokens, icons/fonts. - Config/db: [server/config.js](server/config.js), [server/db.js](server/db.js),
[server/settings.js](server/settings.js). - Studio/tokens: [server/themes.js](server/themes.js), [server/tokens.js](server/tokens.js),
[server/tokens/](server/tokens/) (kit.js,formats.js,exports.js), [server/typefaces/](server/typefaces/),
[server/vectors/svg.js](server/vectors/svg.js), [server/patterns/catalog.js](server/patterns/catalog.js),
[server/kit/](server/kit/). - Arena/studio: [server/arena/](server/arena/) (
ai.js,jobs.js,store.js,bench.js,
sanitize.js,kinds.js), [server/designs.js](server/designs.js),
[server/create/](server/create/) (api.js,options.js,billing.js), [server/deepdive/](server/deepdive/). - Elements/components: [server/elements/](server/elements/), [server/components/store.js](server/components/store.js),
[server/publish.js](server/publish.js), [server/design-edit.js](server/design-edit.js). - HTTP: [server/http/](server/http/) —
pages.js,api.js,arena-api.js,design-api.js,tokens.js,
mcp.js,icons.js,fonts.js,brand-api.js,gallery-api.js,discovery.js,same-origin.js. - Browser: [public/js/studio.js](public/js/studio.js) and the page scripts/CSS in [public/](public/).
- Deploy: [deploy/systemd/openvibe-design.service](deploy/systemd/openvibe-design.service),
[deploy/nginx/openvibe.design.conf](deploy/nginx/openvibe.design.conf).
Commands
npm ci
cp .env.example .env # BASE_URL, OV_OAUTH_CLIENT_SECRET; Network URLs optional
npm run dev # http://localhost:5090
npm test # test/run.js, PGlite, mock Network/AI/Bench
npm test -- studio # filter by file name
ov test # the harness's sharded run of what the change reaches
node --check <file.js>
Database and migrations
Design's own PostgreSQL database on the host's data role (ADR-035); embedded PGlite in development and
tests. Async through openvibe-sdk/db; migrations migrations/NNNN_*.sql (through 0024_element_gallery.sql)
applied at boot; never edit an applied one, add a new number. Themes are not stored here — Network owns
them; rounds/ratings are Bench's. The arena_* tables are legacy and must not be written.
Auth and security
- People sign in through Network SSO; session tokens are httpOnly cookies and are only ever sent as the
Authorizationheader to the configured Network origin. Nothing renders or logs them. - A theme write must come from this site's own origin (
Origincheck); another site cannot make a
signed-in visitor change their theme. nextafter sign-in is a path on this site only; control characters and backslashes are refused.- Model output is sanitized and rendered only in sandboxed, CSP-capped iframes; every dynamic value
in the placeholder views goes through the sharedesc. - Service callers need a Network service token audience
openvibe.designand the route's capability. - Secrets are environment-only; only variable names appear in code and docs.
Frontend loading model
Server-rendered pages plus no-build browser scripts ([public/js/studio.js](public/js/studio.js) derives the
token set from six colours); no SPA, bundler or transpiler. openvibe-shared supplies the Frame, kit and
/shared/* from the pinned node_modules; /tokens/*, icons and fonts are served by Design. Readiness is
truthful: /api/ready reports degraded (HTTP 200) when Network does not answer, and the pages and crawl
files still serve.
Deploy
sudo ovhost deploy design (checkout at /opt/openvibe.design, unit openvibe-design.service on
127.0.0.1:5090, env /etc/openvibe/design.env). nginx
[deploy/nginx/openvibe.design.conf](deploy/nginx/openvibe.design.conf) is installed with ov-vhost-install
and allows GET/POST/PUT on /api and GET on /auth. Migrations run at boot; no secret in a command
line, file or log.
Tests
npm test runs each test/*.test.js in its own process with PGlite and in-process Network/AI/Bench
mocks. It pins the theme proxy (token handling, 4xx pass-through, 502 on a Network failure), the Origin
check, the WCAG gate and token derivation, the studio/previews, the Arena sanitizer and sandboxed frames,
blind voting and Bench calls, designs/guest limits, account export/deletion and discovery. ov test runs
the same.
Conventions
- CommonJS, 4-space indent, single quotes, semicolons; factories with injectable config/db/clock/fetch/log.
- Colours/tokens are data: derive them, never hard-code a token set in a page. WCAG contrast is a gate,
not a warning, except "lines against cards", which is advisory. - Errors are problem+json with stable codes; Network 4xx answers pass through, a Network failure is 502.
- Model output is untrusted: sanitize, sandbox, escape, cap.
Pitfalls
- Network owns the theme. Do not add a local theme store or a direct browser write; the proxy is the
only path, and the person's token never leaves the server header. - The Origin check protects writes. A same-site-but-not-own-origin POST must fail.
- The contrast checks gate "Use this theme". Do not enable it when a required check fails; do not turn
the advisory "lines against cards" into a blocker. - Arena output is untrusted HTML. Keep the sanitizer and the sandboxed CSP iframe; never render model
HTML in the page's own document. - Bench is the rating authority. Do not add a local Elo table or count a vote twice; rounds/entries/
judgements go to Bench, and the legacyarena_*tables stay read-only. - Guests are rate-limited and drafts are capped at three in a private cookie; a signed-in design
belongs to its subject, not the cookie. - Do not log, render or echo a token, including in an error path.
/api/readydegraded is not a failure when Network is down; do not turn it into a 503 that removes
the studio from service.
Documentation
- [README.md](README.md) — what the studio/Arena are, pages, token files, API/MCP, sign-in, config, deploy,
security. - [STATUS.json](STATUS.json) — the precise v2 summary.
- OpenVibe.Contracts holds
design.*/bench.*shapes; Network ownsthemes, Bench owns ratings.