openvibers/openvibe.studio AGENTS.md
AGENTS.md
AGENTS.md — OpenVibe.Studio
Project overview
OpenVibe.Studio is the network's creative-app host at https://openvibe.studio. It serves two
open-source native editors compiled to WebAssembly — Photo (PhotoCraft, a layered photo editor)
and Light (LightCraft, a photo library and RAW developer) — inside an OpenVibe account, plus the
signed-in hand-offs from Video and Cam and the Pics image tools.
- Entry:
server/index.js(node server/index.js); app inserver/app.js; config
server/config.js; port5155, drill port15155, service idstudio. - Pages and API:
server/pages.js(the app pages and/edit),server/api.js
(/api/v1/studio/*: file bytes, media bytes, publish, image edits),server/app.jsmounts
health,/auth,/shared, discovery (/robots.txt,/sitemap.xml,/llms.txt,
/llms-full.txt,/.well-known/agent.json) and/openapi.json. - Adapters:
server/adapters/—contract.js,index.js,photo.js,light.js. One adapter
per app keeps the same contract (open(file),save(),commands()) so a base can be swapped. - OpenVibe clients:
server/openvibe/media.js,pics.js,video.js— each call is scoped to
the person's own Network token. - Browser:
public/js/drivers.js(one driver per app),studio.js,edit-video.js,
image-tools.js; own CSS inpublic/css. The apps run in a same-origin iframe with the shared
navbar outside it. No bundler for our layer. - Bundles:
scripts/build-bundles.sh,scripts/fetch-bundles.sh,scripts/smoke.js; the
bundlesGitHub workflow builds from pinned upstream tags and publishes an artifact with a
checksum file. A bundle is never committed.
Owns, does not own
Owns the service layer, the adapter contract, the signed-in hand-off routes, the Pics image-edit
endpoint and the STUDIO_BUNDLE_ROOT bundle upload/fetch path. It does not own the editors: Photo is
PhotoCraft, Light is
LightCraft, both MIT OR Apache-2.0, built from pinned
release tags; the licence and attribution stay with every build (ATTRIBUTION.md, NOTICE,
STATUS.json). It does not own media bytes (Media/Video), pictures (Pics), the download drive
(Download) or identity (Network).
Who calls it, what it calls
A signed-in person opens the app pages; Video and Cam send them to
GET /edit?media=<Video id>&from=video and GET /edit?pics=<Pics slug>&from=cam. Studio asks the
source service with the person's own Network token whether that person may read the object, and a
private object of someone else answers 404 exactly like a missing id. It calls OpenVibe.Video
(/api/v1/video/studio/publish to send a trimmed range, cover, title and visibility), OpenVibe.Pics
(read bytes, save a separate private picture through its re-encoding pipeline), OpenVibe.Download
(hand an export to the person's drive) and Network (SSO/JWKS). The Pics image edit needs
STUDIO_IMAGE_WORKER_URL, an operator-controlled worker (POST /edit, image in/out); no person token
is sent to it, and without it the endpoint answers 503 and the UI disables submission.
Auth
Network SSO (RS256 JWT + session cookie). Every Studio API and hand-off is the signed-in person's own
call — never a service token on their behalf — and authorization is the source service's answer with
that token. A private object of another person is a 404, not a 403. The image worker receives only
the selected picture, never a token.
Bundles and deploy
The bundles workflow checks out the pinned upstream tags, builds with Rust stable,
wasm32-unknown-unknown, Trunk 0.21.14 (PhotoCraft) or cargo xtask web + wasm-bindgen 0.2.129
(LightCraft), runs the first-frame smoke test in headless Chrome and publishes the bundle with a
checksum file. The deploy fetches that artifact, verifies the checksums and unpacks it into
STUDIO_BUNDLE_ROOT (default public/apps); without a bundle the page says so instead of showing an
empty frame. sudo ovhost deploy studio deploys the service; unit
deploy/systemd/openvibe-studio.service, env /etc/openvibe/studio.env, nginx
deploy/nginx/openvibe.studio.conf. Rollback with sudo ovhost rollback studio --to <sha>.
Commands and tests
npm ci
npm start # http://127.0.0.1:5155
npm test # node --test test/*.test.js
node scripts/smoke.js --app light --dir artifacts/light # first-frame check
node scripts/fetch-bundles.sh # fetch the built artifact in a deploy
npm test uses the built-in Node test runner (not openvibe-shared/test-runner); tests use an
in-process fake image worker and stub services, never model calls or spending. test/adapters.test.js
enforces the one adapter contract; test/studio.test.js, test/media.test.js and
test/image-tools.test.js cover the pages, hand-off authorization, publish polling and the worker
fallback.
Conventions and pitfalls
- CommonJS, 4-space indent, single quotes, semicolons; no linter. Server-rendered pages with the
shared Frame; phone-first CSS. - A bundle is never committed. Ship it as an artifact, verify the checksums, unpack into the
bundle root; a bundle that loads but never reaches its first frame is worse than no bundle. - Keep the adapter contract identical across apps (
assertAdapter/assertDriver); a new base is
an adapter, not a fork of the page. - The hand-off never trusts an id: ask the source service with the person's token, and keep the
private-object 404 indistinguishable from missing. - Photo has no scriptable browser API: a dropped file and no export the page can ask for — say so
on the page instead of faking a bridge. - Image edits need the operator worker; without it, fail available/503, never call a model from
the service. - Honest limits stay documented: the heavy first load, the 4 GB phone memory warning and
online-only use are product statements, not bugs to hide.