openvibers/openvibe.network Commits

Network: the home page's Build on OpenVibe card, the README and the developer-projects doc point at openvibe.services (the console moved there from openvibe.codes) (#82)

OP OpenVibers committed 8 Oct 2026 · 590a8fad22 · 3 files, +9 −9

Claude-Session: https://claude.ai/code/session_01K8AYn3FF3HE7CPfM262qdh

Co-authored-by: Claude Opus 5.5 <[email protected]>

Changes

ddiff --git a/README.md b/README.md
iindex 567a83e4..e48ff553 100644
---- a/README.md
++++ b/README.md
@@@ -20,7 +20,7 @@ Identity and account service for the OpenVibe network. Manages user accounts, OA
- **Theme Catalog** — Shared theme system with ~30 built-in themes and community submissions. Theme preferences sync across all services.
- **Admin Panel** — email configuration, user management (role changes, bans), broadcast notifications, system health dashboard, and audit log.
- **Internal API** — Server-to-server endpoints for token verification, user lookup, notification push, account linking, and audit logging. Every route takes a capability-scoped service token; the deployment-wide key they used to accept was retired on 2026-09-29 (plan T2, [docs/retirement.md](docs/retirement.md)) and opens nothing.
--- **Developer projects (foundation)** — projects, members, apps (OAuth clients with `app_` subjects), hashed client secrets with rotation and revocation, capability grants within a staff-set allowance (sandbox apps also get a default sandbox allowance of public Media, Events-app and Tools-job capabilities, so a new project works without staff), recorded quotas, per-project usage from the services' hourly rollups (`GET /api/v1/projects/:project/usage`) and an append-only audit at `/api/v1/projects`. Its events can be relayed to OpenVibe.Events through an outbox when `OV_EVENTS_INTERNAL_URL` is set. Bearer user tokens only; no portal UI yet (that is OpenVibe.Codes). See [docs/developer-projects.md](docs/developer-projects.md).
++- **Developer projects (foundation)** — projects, members, apps (OAuth clients with `app_` subjects), hashed client secrets with rotation and revocation, capability grants within a staff-set allowance (sandbox apps also get a default sandbox allowance of public Media, Events-app and Tools-job capabilities, so a new project works without staff), recorded quotas, per-project usage from the services' hourly rollups (`GET /api/v1/projects/:project/usage`) and an append-only audit at `/api/v1/projects`. Its events can be relayed to OpenVibe.Events through an outbox when `OV_EVENTS_INTERNAL_URL` is set. Bearer user tokens only; the console over this API is OpenVibe.Services at openvibe.services (it moved there from OpenVibe.Codes on 2026-10-08). See [docs/developer-projects.md](docs/developer-projects.md).
- **Public discovery** — `/.well-known/openvibe`, `/api/v1/registry/*` and `/contracts/*.json` (services, capabilities, contract schemas) answer any origin with `Access-Control-Allow-Origin: *`, preflight included, so a browser app can discover the platform directly. Every other route keeps the first-party CORS allow-list (`server/public-cors.js`).
- **OpenCoins Wallet** — Network-wide currency. User balance/history at `/api/coins/*`; atomic credit/debit/transfer for services at `/internal/coins/*` with idempotency-key dedupe.
- **User modules** — versioned per-person preferences and summaries in namespaces owned by services (openvibe-contracts `manifests/namespaces`); see [User modules](#user-modules).
@@@ -296,7 +296,7 @@ Until `NETWORK_GO_LIVE_FOLLOWS_READY=1` is set on Network, Events go-lives are a
**Project usage.** `tools.usage.recorded` and `events.usage.recorded` (hourly rollups of a developer
project's use, `common.usage-recorded@1`) are kept per project and day (`server/developer/usage.js`,
tables `dev_usage_windows`, `dev_usage_daily`, `dev_usage_errors`) for `GET /api/v1/projects/:project/usage`
--and the project's usage page on openvibe.codes; they notify nobody. See
++and the project's usage page on openvibe.services; they notify nobody. See
[docs/developer-projects.md](docs/developer-projects.md#usage).
Coupons publishes no watch event yet: its merchant watches never leave Coupons, and its `coupons.*`
ddiff --git a/docs/developer-projects.md b/docs/developer-projects.md
iindex 8985b6c5..c3d3c85d 100644
---- a/docs/developer-projects.md
++++ b/docs/developer-projects.md
@@@ -1,6 +1,6 @@
# Developer projects
--Roadmap Wave 20 foundation. The binding decision is ADR-014 in OpenVibe.Contracts. Network owns developer projects: who may build on OpenVibe, which apps they run, what those apps may do, and how much they may use. OpenVibe.Codes (the portal, the next step) is a client of this API. It never enforces grants or quotas itself.
++Roadmap Wave 20 foundation. The binding decision is ADR-014 in OpenVibe.Contracts. Network owns developer projects: who may build on OpenVibe, which apps they run, what those apps may do, and how much they may use. OpenVibe.OpenVibe.Services (the developer console at openvibe.services, which moved there from OpenVibe.Codes on 2026-10-08) is a client of this API. It never enforces grants or quotas itself.
What exists today: the data model, the `/api/v1/projects` API, app tokens from `/oauth/token`, the sandbox check in Network's own capability guard, defaults that let a new project's sandbox apps work without staff (sandbox audiences and a sandbox allowance), the audit, and a relay of the audit's events to OpenVibe.Events (off until `OV_EVENTS_INTERNAL_URL` is set). The backend of the consent screen is built (`GET /oauth/client-info` with `scope`), the page that renders it is next (see [Consent screen](#known-gaps-next-steps)); what does not exist yet: a UI (Codes), the consent-screen page, and refresh tokens for apps. Owning services read a project through `GET /internal/projects/:project_id` ([Service-side project reads](#service-side-project-reads)). Quotas are enforced by the owning services, not here.
@@@ -31,7 +31,7 @@ What exists today: the data model, the `/api/v1/projects` API, app tokens from `
| Add or remove admins | | | | yes | |
| Read the audit | | | yes | yes | yes |
| Archive the project | | | | yes | yes |
--| Mint an export token (read-only, for OpenVibe.Codes' project export) | | | yes | yes | only as an admin member |
++| Mint an export token (read-only, for OpenVibe.Services' project export) | | | yes | yes | only as an admin member |
| Set the allowance, environment policy and quotas | | | | | yes |
Non-members get `404 project.not_found`, so a project's existence is not disclosed. Any member can leave, except the owner.
@@@ -90,7 +90,7 @@ An audience opts in when it can keep sandbox traffic apart from real data: for e
### Export tokens
--A project's owner or admin exports everything the project holds on openvibe.codes (roadmap WS-N task 9). Codes calls `POST /api/v1/projects/:project/export-tokens { audience, env }` with the person's own token, once per audience and environment, and reads with what Network returns. Network checks the role at mint time; developers and viewers get `403 project.forbidden`, non-members `404`, and staff who are not an admin member of the project `403`. An archived project can still be exported.
++A project's owner or admin exports everything the project holds on openvibe.services (roadmap WS-N task 9; the developer console moved there from openvibe.codes on 2026-10-08). Services calls `POST /api/v1/projects/:project/export-tokens { audience, env }` with the person's own token, once per audience and environment, and reads with what Network returns. Network checks the role at mint time; developers and viewers get `403 project.forbidden`, non-members `404`, and staff who are not an admin member of the project `403`. An archived project can still be exported.
The token is shaped as an app token, so Media and Events accept it without changes:
@@@ -104,7 +104,7 @@ The token is shaped as an app token, so Media and Events accept it without chang
| `env` | `sandbox` or `production`, as asked (anything else is `422 export.invalid_env`). Each receiver still decides whether it accepts `env: sandbox`. |
| `on_behalf_of` | the person exporting |
| `purpose` | `export` |
--| `iat`, `exp`, `jti` | 300-second lifetime, no refresh (Codes asks again during a long export) |
++| `iat`, `exp`, `jti` | 300-second lifetime, no refresh (Services asks again during a long export) |
The capabilities do not come from the project's grants or allowance. Those bound what the project's apps may do; an export token reads back what the project already holds, for its owner or admin. Every mint writes a `dev_audit` row, `project.export_token_issued`, with the audience, environment, capabilities, `jti` and expiry. The row is not a platform event, and the token itself is never stored or logged (`test/developer-export-tokens.test.js`).
@@@ -149,7 +149,7 @@ The Events consumer (`POST /internal/events`, [server/developer/usage.js](../ser
A rollup for a project Network does not know, from the wrong source, whose subject is not its project, whose window is not a whole hour (or day), from the future or older than 35 days, or that fails its contract is ignored (the consumer's `outcome` says which).
--`GET /api/v1/projects/:project/usage?days=30&env=all` (owner and admins, and staff) answers `network.project-usage-result@1`: `daily` rows (newest first), `totals` for the range, `quotas` (each recorded quota with `used` and `remaining` for its current window: today or this month in UTC, or the 400 days kept for `total`, in every environment together; `null` with a `note` for a minute or hour window, which rollups cannot show, and for a capability or unit no service reports), and `errors` (`total`, `by_code`, and the 50 most recent sampled failures). `days` is 1 to 90; `env` is `all`, `sandbox` or `production`; anything else is `422 usage.invalid`. The numbers lag by one hour: an hour appears a few minutes after it closes. OpenVibe.Codes shows it as the project's usage page (`/projects/:project/usage`).
++`GET /api/v1/projects/:project/usage?days=30&env=all` (owner and admins, and staff) answers `network.project-usage-result@1`: `daily` rows (newest first), `totals` for the range, `quotas` (each recorded quota with `used` and `remaining` for its current window: today or this month in UTC, or the 400 days kept for `total`, in every environment together; `null` with a `note` for a minute or hour window, which rollups cannot show, and for a capability or unit no service reports), and `errors` (`total`, `by_code`, and the 50 most recent sampled failures). `days` is 1 to 90; `env` is `all`, `sandbox` or `production`; anything else is `422 usage.invalid`. The numbers lag by one hour: an hour appears a few minutes after it closes. OpenVibe.OpenVibe.Services shows it as the project's usage page (`/projects/:project/usage`).
Operator steps: subscribe Network to the two topics once (`node --env-file=/etc/openvibe/network.env scripts/subscribe-events.js --topic tools.usage.recorded --topic events.usage.recorded`), with Events' usage on (default) and Tools' job events on (`EVENTS_URL` set for the satellites).
@@@ -307,7 +307,7 @@ Until the relay is turned on in an environment, nobody else receives these event
## Known gaps (next steps)
--- **Consent screen (backend built; page next).** The account chooser shows the app's name, not the capabilities it asks for. Codes and Network need a consent step before third-party production apps go live, and it must list the capability ids the app asks for, marking the ones `capability@1` marks `sensitive`; a request without a `scope` must not consent to capabilities the screen did not name. This screen is the app path's compensation for not using the agent confirmation flow — an authorization-code app acts for a person who is present and authorizes it capability by capability, so it carries no `cap_confirm` (decided 2026-10-04, `docs/t2-projects-and-grants.md` section 9). **The backend half is built:** `GET /oauth/client-info` with the authorize request's `scope` returns `capabilities: [{ id, name, description, sensitive }]` — the app-grantable requested ids, in the order requested, with `description` and `sensitive` from the `capability@1` catalog (`name` is the catalog id, since `capability@1` has no separate display name) — and `refused: [id]` for the unknown or ungrantable ones the page must not name; without a `scope` it returns `capabilities: []`. The authorization code an app receives now carries the explicit consented set (`tokens.issueCode`), so an absent or empty `scope` consents to nothing: the code stores an empty set and the token exchange mints a token with `cap: []` (the person is still identified by `on_behalf_of`), and asking the token endpoint for an id outside that set is `invalid_scope`. Apps have no refresh tokens, so no refreshed token can widen the set either. What remains is the API design session's page: render `capabilities` from `client-info`, mark the `sensitive` ones, and say the `refused` ids will not be granted (`test/consent-screen.test.js`).
++- **Consent screen (backend built; page next).** The account chooser shows the app's name, not the capabilities it asks for. Services and Network need a consent step before third-party production apps go live, and it must list the capability ids the app asks for, marking the ones `capability@1` marks `sensitive`; a request without a `scope` must not consent to capabilities the screen did not name. This screen is the app path's compensation for not using the agent confirmation flow — an authorization-code app acts for a person who is present and authorizes it capability by capability, so it carries no `cap_confirm` (decided 2026-10-04, `docs/t2-projects-and-grants.md` section 9). **The backend half is built:** `GET /oauth/client-info` with the authorize request's `scope` returns `capabilities: [{ id, name, description, sensitive }]` — the app-grantable requested ids, in the order requested, with `description` and `sensitive` from the `capability@1` catalog (`name` is the catalog id, since `capability@1` has no separate display name) — and `refused: [id]` for the unknown or ungrantable ones the page must not name; without a `scope` it returns `capabilities: []`. The authorization code an app receives now carries the explicit consented set (`tokens.issueCode`), so an absent or empty `scope` consents to nothing: the code stores an empty set and the token exchange mints a token with `cap: []` (the person is still identified by `on_behalf_of`), and asking the token endpoint for an id outside that set is `invalid_scope`. Apps have no refresh tokens, so no refreshed token can widen the set either. What remains is the API design session's page: render `capabilities` from `client-info`, mark the `sensitive` ones, and say the `refused` ids will not be granted (`test/consent-screen.test.js`).
- **Service-side project reads.** The route is built, but no service calls it yet. Host holds the grant. `network.project.read` has been `active` since openvibe-contracts v0.88.0.
- **PowerChat as the first project** (ADR-014 migration). This is not done. PowerChat's existing OAuth client is unchanged.
- **Receivers.** Every default sandbox audience must refuse `env: sandbox` except on routes that keep sandbox data apart, and must key tenancy by `project_id`. Media and Events do this in Wave 20. Tools does not yet (see [Sandbox tokens](#sandbox-tokens)).
ddiff --git a/server/home/render.js b/server/home/render.js
iindex c187fd9a..1c6ea131 100644
---- a/server/home/render.js
++++ b/server/home/render.js
@@@ -158,7 +158,7 @@ ${chips(SOON.map((site) => [`https://${site.host}/`, site.tagline, site.icon, si
id: 'developers', title: 'For developers and crawlers',
lede: 'OpenVibe is open source and community-run. Everything public is meant to be read by machines too.',
items: [
-- { icon: 'ov:codes', title: 'Build on OpenVibe', text: 'Sign in with OpenVibe (OAuth 2.0 and FedCM), the API, SDK and webhooks: all on OpenVibe.Codes.', href: 'https://openvibe.codes/' },
++ { icon: 'ov:code', title: 'Build on OpenVibe', text: 'Sign in with OpenVibe (OAuth 2.0 and FedCM), the API, SDK and webhooks: all on OpenVibe.Services.', href: 'https://openvibe.services/' },
{ icon: 'ov:tools', title: 'Tool catalog', text: 'Every tool with its description, keywords and addresses as JSON.', href: 'https://openvibe.tools/api/catalog.json' },
{ icon: 'ov:blog', title: 'llms.txt', text: 'A plain-text map of the network for AI assistants, with the full text beside it.', href: '/llms.txt' },
{ icon: 'fa-code-branch', title: 'Source on GitHub', text: 'Every OpenVibe service is open source under the OpenVibers organization.', href: 'https://github.com/OpenVibers' },