- JavaScript 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| .github/workflows | ||
| examples | ||
| scripts | ||
| test | ||
| .gitignore | ||
| .gitleaks.toml | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| SECURITY.md | ||
| STATUS.json | ||
OpenVibe.Examples
Executable public integration examples for the OpenVibe platform: the SDK, scoped credentials and public APIs, nothing else.
Status: alpha, 0.3.0 (roadmap Wave 20; the create-openvibe-app scaffolder, WS-N task 6). Fifteen examples, each with a smoke test that CI runs on
Node 22.22.1 against openvibe-sdk/testing's mock platform (no network). npm run e2e runs three
of them (Media, Events pull, a Tools job) against the real platform with your own sandbox app; it
is not part of CI or npm test, and it was not run against production as part of this release.
npm run developer-path is the Wave 20 exit check (account to Media, Events and AI to revoked
credentials); CI runs it against the mock platform, and OpenVibe.Host runs it against production daily.
Built on: openvibe-sdk v0.65.0, and
openvibe-contracts v0.215.0 for the mod manifest.
Plan: OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026), §15.1
and §18.9; roadmap Wave 20, §30 (public SDK surface), ADR-014 (developer projects).
License: MIT (LICENSE), so you can copy any example into your own project. The
OpenVibe services themselves are AGPL-3.0.
Purpose
Show, with code a developer can copy, how an app outside the network uses OpenVibe: only the public SDK, scoped developer-app credentials and public APIs, never a loopback key or a first-party database. Each example is small, runs on its own and proves one thing with a smoke test.
The examples
| Example | What it proves | Uses |
|---|---|---|
| browser-app | A plain page (no build step) imports the SDK's browser bundle, reads the registry cross-origin and signs in with PKCE; a public app needs no secret | openvibe-sdk/browser/openvibe-sdk.mjs, /auth (exchangeCode, verifyAppToken) |
| node-server-app | Client-credentials app token per audience and what it carries, registry discovery, contracts version check, trace propagation | openvibe-sdk/auth (getTokenInfo), /registry, /core |
| media-uploader | Upload into, read from and delete in your project's Media tenant; sandbox files come back with signed URLs | openvibe-sdk/media |
| event-subscriber | Publish to your project's topic app.<project_key>.*, pull it with a durable cursor, gaps; anonymous realtime for public events |
openvibe-sdk/events (createAppEvents), /realtime |
| webhook-consumer | An app subscription on your own topic; X-OpenVibe-Signature-V2 (±300 s replay window) verified on the raw body; exactly-once handling with an inbox; secret rotation |
openvibe-sdk/events (createAppEvents, parseDelivery, createPgInbox) |
| tool-job | An OpenVibe.Tools job submitted once (idempotency key), reattached after a restart, its SSE stream resumed with Last-Event-ID |
openvibe-sdk/jobs |
| oauth-app | Authorization code + PKCE for a confidential app, exchange on the server, offline app-token verification, session hygiene | openvibe-sdk/auth |
| chat-bot | A bot on Chat's WebSocket protocol that stays in its one configured room and always identifies as a bot | Chat /ws/chat protocol (no SDK client: Chat is first-party) |
| mod-manifest | A mods.mod-manifest@1 validated against the schema, the capability catalog and compatibility ranges |
openvibe-contracts, openvibe-sdk/registry |
| go-live | Go live on OpenVibe.Live from code: a Live API token finds or creates a WHIP slot and reads its WHIP URL, FFmpeg 8+ publishes (test pattern, a file or a camera), the session is named once media flows and ended when FFmpeg stops; the stream key is never printed | openvibe-sdk/live (slots, streams, channels.url), /core, FFmpeg's WHIP muxer |
| game-scores | Achievements and a leaderboard for your game on OpenVibe.Play, from a sandbox app any developer can make today: register a sandbox- title, post a round (counted once on a retry), unlock, read the public board, delete the title |
openvibe-sdk/play (titles, scores, achievements, boards) |
| inventory-wear | Show what people wear on OpenVibe (hats, chat tags, badges, name effects) next to names in your own app: the public catalog and who wears what, 100 people per call, with no token; text and escaped HTML | openvibe-sdk/inventory (kinds, definitions, equipped) |
| space-deploy | Deploy a static site to OpenVibe.Space from CI with an app token (space.deploy.create, deployer role): the built folder as one deploy, live on main or a preview for a pull request, a refusal that changes nothing, rollback; a ready GitHub Actions workflow | openvibe-sdk/space (deploys.uploadFiles, rollback), /auth |
| zone-s3 | OpenVibe.Zone's private buckets over S3 with the official AWS SDK v3: put, get, a paged list, a presigned share link (at most 7 days) and delete; path-style, us-east-1, SigV4, an access key scoped to the zone | @aws-sdk/client-s3, @aws-sdk/s3-request-presigner |
| ai-chat | The official openai npm client on https://ai.openvibe.services/v1 with a Network app token: plain and streamed Chat Completions, x_openvibe run info, the project's own provider by secret name |
openai (baseURL, apiKey as a token function), openvibe-sdk/auth |
Each example folder has a README, a .env.example, its own package.json and a
test/smoke.test.js. Configuration is environment variables only (OV_CLIENT_ID,
OV_CLIENT_SECRET, OV_PROJECT_ID, …); secrets are never logged or sent to a browser.
Start a new app
create-openvibe-app copies one of these examples into a new folder as your starting point, with
its smoke test, a .gitignore, the package renamed and the SDK pinned to the same tag. It needs no
network beyond fetching this package.
npx --package=https://codeload.github.com/OpenVibers/OpenVibe.Examples/tar.gz/refs/tags/v0.3.0 create-openvibe-app my-app --template web
cd my-app && npm install && cp .env.example .env && npm test
| Template | Starts from | For |
|---|---|---|
web |
browser-app | a plain web page that signs in with OpenVibe (PKCE) and calls a public API |
server |
node-server-app | a Node server with a client-credentials app token and registry discovery |
bot |
chat-bot | a chat bot that stays in its one room and always identifies as a bot |
mod |
mod-manifest | a mod manifest validated against the schema, capabilities and compatibility ranges |
Without --template it asks on a terminal (and picks web otherwise). npm create openvibe-app
will work the same way once the package is on the npm registry; until then use the npx line.
End-to-end walkthrough
From a new account to the examples running against the platform, with public surfaces only.
Nothing here needs staff: a new project is sandbox, and a sandbox app's owner gets the sandbox
capabilities approved at once.
1. Create an account at https://openvibe.network.
2. Create a project, a sandbox app and its grants. Either:
-
in the developer console, https://openvibe.services: sign in, create a project, add a confidential app in the
sandboxenvironment with the redirect URIhttp://localhost:3009/callback(for oauth-app), copy the client secret (it is shown once), and request the grantsmedia.object.upload,media.object.read,events.app.publish,events.app.read,events.app.subscribe,tools.job.create,tools.job.read; or -
with the projects API (
openvibe-sdk/projects):npm run new-appdoes exactly the above and writesOV_CLIENT_ID,OV_CLIENT_SECRETandOV_PROJECT_IDinto./.env(mode 0600) without printing the secret. It signs in with your username and password (not echoed) or usesOV_USER_TOKEN. The same in code:const { createClient } = require('openvibe-sdk/core'); const { createProjectsClient } = require('openvibe-sdk/projects'); const projects = createProjectsClient(createClient({ token: userAccessToken })); // a Network user token const project = await projects.create({ name: 'My first OpenVibe app' }); // sandbox, you are the owner const app = await projects.apps.create(project.id, { name: 'server', environment: 'sandbox', type: 'confidential' }); app.credential.client_secret; // shown ONCE: store it now await projects.grants.request(project.id, app.id, 'media.object.upload'); // approved at once for the owner
For browser-app add a second, public app with the redirect URI
http://localhost:3001/callback and media.object.read (npm run new-app -- --public --project prj_…). A public app has no secret.
3. Check it works end to end:
npm ci
npm run e2e # reads ./.env: OV_CLIENT_ID, OV_CLIENT_SECRET, OV_PROJECT_ID
It uploads a small file to your Media tenant, reads it back and deletes it; publishes
app.<project_key>.e2e.ran and pulls your topic until it finds it; converts a 1x1 PNG to WebP on
openvibe.tools (the gateway's jobs facade), following the job's events. It prints each step (never the secret, a token
or a URL signature) and exits 1 if a step failed, 2 if a variable is missing.
4. Run the examples. Each reads .env in its own folder (cp .env.example .env), or point it
at the root one: node --env-file=../../.env upload.js ./logo.png.
| Example | App | Grants | Environment variables |
|---|---|---|---|
| node-server-app | confidential | any | OV_CLIENT_ID, OV_CLIENT_SECRET, OV_AUDIENCES |
| media-uploader | confidential | media.object.upload, media.object.read |
OV_CLIENT_ID, OV_CLIENT_SECRET, optional OV_PROJECT_ID |
| event-subscriber | confidential | events.app.publish, events.app.read |
OV_CLIENT_ID, OV_CLIENT_SECRET, optional OV_PROJECT_ID, OV_TOPICS, OV_PLATFORM_TOPICS; realtime: OV_EVENTS_MODE=realtime, OV_TOPICS only |
| webhook-consumer | confidential | events.app.subscribe |
OV_CLIENT_ID, OV_CLIENT_SECRET; the consumer: OV_WEBHOOK_SECRET (written by subscribe.js); a public https host |
| tool-job | confidential | tools.job.create, tools.job.read |
OV_CLIENT_ID, OV_CLIENT_SECRET, optional OV_TOOLS_JOBS_URL, OV_JOB_TYPE, OV_JOB_INPUT |
| oauth-app | confidential, redirect http://localhost:3009/callback |
every capability in OV_SCOPE |
OV_CLIENT_ID, OV_CLIENT_SECRET, OV_AUDIENCE, OV_SCOPE |
| browser-app | public, redirect http://localhost:3001/callback |
media.object.read |
OV_CLIENT_ID, OV_AUDIENCE, OV_SCOPE |
| chat-bot | none: a Live API token (hbt_…) of a dedicated bot account |
Live token scope chat |
OV_CHAT_TOKEN, one of OV_CHAT_CHANNEL_USER_ID / OV_CHAT_STREAM_ID |
| mod-manifest | none | none | OV_NETWORK_URL for --registry |
| ai-chat | confidential | ai.app.run |
OV_CLIENT_ID, OV_CLIENT_SECRET, optional OV_AI_URL; own provider: OV_PROVIDER_SECRET (a secret's name), OV_PROVIDER_BASE_URL, OV_PROVIDER_MODEL |
Rotate a secret in the portal or with projects.credentials.rotate(), revoke the app with
projects.apps.revoke(). Issued tokens end within their 5-minute lifetime.
How the platform works for a sandbox app
Verified in production on 2026-09-23 with a brand-new account, using the public endpoints only:
- Projects. A new project is
sandbox. Its owner's grant requests for the sandbox allowance are approved at once:media.object.upload|read,events.app.publish|read|subscribe,tools.job.create|read|cancel. - Tokens.
POST https://openvibe.network/oauth/tokenwithgrant_type=client_credentialsgives a confidential sandbox app a 5-minute token foropenvibe.media,openvibe.eventsoropenvibe.tools, withproject_id,env: sandbox,ns: [project_id]and the approved capabilities for that audience. - Media. The tenant is the project id:
/api/v1/<prj_…>/files. Sandbox objects are stored in a separate sandbox tenant (<prj_…>-sandbox, not addressable by path) and never served publicly: signed URLs only. - Events. App events are
app.<project_key>.<name…>(project_key=p+ the lowercased project ULID) withsource: app-<lowercased app ULID>. Read them withGET /api/v1/events?topic=app.<project_key>.*; subscribe with public https endpoints only. Realtime (SSE) never streams app events. - Tools.
/api/v1/jobsaccepts sandbox app tokens. - Discovery.
/.well-known/openvibeand/api/v1/registry/*answer any origin (CORS), so a browser page reads them directly.
Developer path check
scripts/developer-path.js is the roadmap Wave 20 exit check as a repeatable script: a developer
goes from an account to a working Media, Events and AI integration with public endpoints, the SDK and
scoped credentials only, then rotates and revokes those credentials and cleans up. OpenVibe.Host runs it
against production every day (openvibe-devpath.timer, shown on openvibe.network/status).
| Step | What it does |
|---|---|
| 1. account | registers OV_E2E_USERNAME (--register) or signs in at /api/auth/*, or uses OV_USER_TOKEN |
| 2. discovery | reads /.well-known/openvibe as any origin may |
| 3. project | creates a sandbox project with openvibe-sdk/projects |
| 4. app | creates a confidential sandbox app; its secret stays in memory |
| 5. grants | requests media.object.upload, media.object.read, events.app.publish, events.app.read, ai.app.run, play.title.manage, play.result.write, services.release.manage; all must be approved at once |
| 6. media | media-uploader: upload a small file, read it back, delete it |
| 7. events | event-subscriber: publish app.<project_key>.developer_path.ran, pull it with a cursor |
| 8. ai | ai-chat: the official openai client on OpenVibe.AI's OpenAI-compatible endpoint (OV_AI_URL) with an app token for openvibe.ai; one short answer, which must carry its x_openvibe run id |
| 9. play | game-scores: a sandbox- title on OpenVibe.Play (OV_PLAY_URL), a round posted twice and recorded once, an unlock, the public board read without a token, the title deleted |
| 10. export | as the project's owner: Network mints 5-minute read-only export tokens; Media lists the project's sandbox files with one and refuses an upload; Events returns the event from step 7 with the other |
| 11. release | as the app, on OpenVibe.Services (OV_SERVICES_URL): a sandbox release of itself goes draft → published → deprecated → revoked, checked in the public list each time |
| 12. credentials | rotates with no overlap and revokes the first credential; the old secret must be refused at /oauth/token, the new one must work |
| 13. cleanup | archives the project, even when an earlier step failed; the app's secret must then be refused |
In CI, on every push, npm run developer-path:mock runs the same code against
openvibe-sdk/testing's mock platform (the developer-path job; test/developer-path.test.js
also checks it leaves nothing behind, never prints a secret, and still cleans up after a failure).
The mock has no account API, so scripts/developer-path-mock.js adds register and login routes
that mint mock user tokens, mints export tokens, and stands in for Services' release API, AI's
/v1/chat/completions and Play's title, score, unlock and board routes (with its sandbox rules); it hands the mock the contract's public capabilities, as Network reads them. Every
other step is the platform mock's own.
Against production it runs only when a person starts it. Credentials come from the
environment (or ./.env) only:
OV_E2E_USERNAME=… OV_E2E_PASSWORD=… npm run developer-path # an existing account
OV_E2E_USERNAME=… OV_E2E_PASSWORD=… npm run developer-path -- --register # create that account first
OV_USER_TOKEN=… npm run developer-path # a Network user access token
It creates one project per run and archives it at the end (Network keeps archived projects).
It refuses to start without credentials, and refuses in CI against the production Network (CI
set and OV_NETWORK_URL unset or https://openvibe.network), so it can later run in CI against
an integration environment but never against production by accident. It masks every password,
secret and token in its output and prints signed URLs without their signature. Exit 0 when all
thirteen steps passed, 1 when one failed, 2 when it refused to start.
It does not yet cover webhook delivery to an external endpoint (that needs a public https host) or publishing an app release in OpenVibe.Services.
Tools job proof
scripts/tools-job-proof.js (npm run tools-job-proof) is the scheduled end-to-end proof of
OpenVibe.Tools jobs (roadmap WS-L task 4), again with the SDK only. A service principal runs it,
because sandbox jobs are never announced to Events or copied to Media:
- token: client credentials for
tools.job.createandtools.job.read(openvibe.tools) andevents.event.read(openvibe.events). - cursor: the head of the Events store for
tools.job.*, before anything is submitted. - submit:
img.processconverts a small generated PNG to WebP. - reattach: the progress stream is dropped after its first event and reattached with
Last-Event-ID, as the Tools UI does after a reload. Every later event must arrive once, in order, up tojob.succeeded. - result: the job reads back
succeeded, and its file is stored in Media (storage: "media"with a media id). The download must be a WebP that matches the listed sha256. Tools keeps no local copy of a Media-stored result. - events:
tools.job.created,tools.job.startedandtools.job.succeededfor the job are in the Events store.
OV_CLIENT_ID=… OV_CLIENT_SECRET=… npm run tools-job-proof [-- --result last.json]
OV_OAUTH_CLIENT_ID and OV_OAUTH_CLIENT_SECRET also work; Network's service-principal setup
writes those names. OpenVibe.Host runs it every six hours as the probe principal
(openvibe-toolsjob.timer). CI runs it against the mock platform (test/tools-job-proof.test.js),
and it refuses to run in CI against production.
What still does not work
- No refresh tokens, and no profile. Network's consent screen lists the capabilities an app asks for, and a
person who continues gives it a 5-minute app token with no refresh token. The app learns only the person's
usr_…id (on_behalf_of): profile lookups (identity.subject.resolve) arefirst-party. /oauth/tokenis not CORS-open, like every Network route outside discovery and the registry, so a browser app exchanges its code on its own server (browser-app does).- Chat has no app principal.
chat.message.sendisfirst-party; the chat-bot signs in with a Live API token (hbt_…, scopechat) of a dedicated bot account, not with a developer app. - Production apps need staff: switching a project to
sandbox+productionand setting its allowance. - Publishing a mod into Games is not public (
games.mod.manageisfirst-party).
Running the tests
fnm use 22.22.1 # or any Node 22
npm ci # one install for all examples (npm workspaces)
npm test # every smoke test + the repository checks
npm test -- chat oauth # only some
cd examples/tool-job && npm test
How the tests stay offline:
- Network, Events, Media and Tools jobs are
createMockPlatform()fromopenvibe-sdk/testing: an in-process fake at the real public origins, with real RS256 tokens, developer apps and projects, sandbox handling per service,/oauth/authorizewith auto-consent, retention gaps (pruneEvents()), a delivery worker (deliverEvents()) and Tools jobs with SSE. - The mock plays the developer-app rules the way production does: Events'
events.app.*(own-project topics,app-<ULID>sources, env separation), Media's project tenants (prj_…andprj_…-sandbox, signed URLs for sandbox files) and Tools at its satellites' origins. - Chat is the only service the SDK does not mock: chat-bot carries
test/mock-chat-server.js. - Every smoke test replaces the global
fetchwith one that throws, so an accidental real network call fails the test.
Repository checks (test/):
public-surface.test.jsfails on the loopback shared-key header, on the platform's internal route prefix, on hard-coded loopback service ports outside the local mocks, and on any secret value in a.env.example.structure.test.jschecks the ten examples are complete and copyable: README with "What it proves" and "Run it against the real platform", a.env.examplethat names every variable the code reads, MIT license,openvibe-sdkpinned to one tag (the root package.json's), nofile:links; and thatnpm run e2eis outside CI andnpm testand refuses to start without its variables; and that CI runs the developer path against the mock platform only.developer-path.test.jsruns the developer path against the mock platform and checks the result (see Developer path check).
Layout
examples/<name>/ one example: README.md, .env.example, package.json, code, test/smoke.test.js
scripts/e2e.js npm run e2e: three examples against the real platform (on demand only)
scripts/developer-path.js npm run developer-path: the Wave 20 exit check against the real platform (on demand only)
scripts/developer-path-mock.js npm run developer-path:mock: the same flow against the mock platform (CI)
scripts/new-sandbox-app.js npm run new-app: project + sandbox app + grants via openvibe-sdk/projects
test/run.js runs every smoke test and the repository checks (npm test)
test/public-surface.test.js, test/structure.test.js
.github/workflows/ci.yml Node 22.22.1: npm test; the developer path against the mock platform
Owns
- the nine charter examples: vanilla browser app, Node server app, webhook consumer, event subscriber, Media uploader, Chat bot, tool/job example, mod manifest example, OAuth app example; plus the AI chat example (the openai client on AI's OpenAI-compatible endpoint) the go-live example (Live over WHIP from code), the zone-s3 example (Zone with the AWS SDK v3), the space-deploy example (Space deploys from CI) and the inventory-wear example (what people wear, rendered in your app)
- the on-demand end-to-end run (
npm run e2e) and the sandbox-app setup script - the developer path check (
npm run developer-path, anddeveloper-path:mockin CI)
Does not own
- any platform service, the SDK (OpenVibe.SDK), the contracts (OpenVibe.Contracts) or the developer console (OpenVibe.Services)
Depends on
- OpenVibe.SDK (v0.65.0), OpenVibe.Contracts (v0.214.0, mod manifest only)
- at run time against the real platform: Network (tokens, registry, developer projects), Events, Media, Tools, Chat (on openvibe.live)
Capabilities
Examples implements no capability (its manifest lists none). The examples call the platform as a
developer app with the grants a sandbox project asks for (npm run new-app): media.object.upload
and media.object.read (Media), events.app.publish, events.app.read and events.app.subscribe
(Events), and tools.job.create, tools.job.read and tools.job.cancel (Tools); a public browser app
gets media.object.read only. The developer path check also uses ai.app.run (AI) and services.release.manage (Services). The
chat bot uses a Live hbt_ token with scope chat, because chat.message.send is first-party.
Acceptance
- CI runs every example: yes, against the SDK's mock platform (and a small Chat WebSocket
mock for chat-bot; no network), and the developer path check against the same mock. Against
the real platform on demand:
npm run e2ewith a sandbox app's credentials andnpm run developer-pathwith an account; there is no integration environment with credentials for CI. - A new developer goes from account creation to a working Media, event and capability
integration using only public documentation, the SDK and scoped credentials: the platform path
was verified in production on 2026-09-23 (see
How the platform works) by a curl script against the
public endpoints, not by the SDK or these examples. That check is now committed as
npm run developer-path(SDK and examples, plus credential rotation and revocation), runs in CI against the mock platform, and has not yet been run against production.npm run e2ewalks three of the nine examples along the same path. Partly met. - No example needs a loopback-only internal key or first-party database access: yes, enforced
by
test/public-surface.test.js.
Security
Reporting a vulnerability: SECURITY.md. Configuration is environment variables only;
secrets are never logged or sent to a browser, and .env.example files hold names, not values
(test/public-surface.test.js fails on a secret value there, on the loopback shared-key header and on
internal routes or ports). Every smoke test replaces fetch with one that throws, so no test reaches the
network. npm run developer-path refuses to run against production in CI.
Deploy
Nothing is deployed from this repository: it has no unit, port or env file and is not published as a
package. CI (.github/workflows/ci.yml) runs npm test and the developer path against the mock
platform on every push. Copy an example, or start one with create-openvibe-app, and deploy it as your
own app.
Part of the OpenVibe network. Built in the open by OpenVibers.
- openvibe-contracts: v0.215.0
- openvibe-sdk: v0.65.0