Commits
-
Registry (roadmap W2 D4, §5.1, §5.3): /api/v1/registry/categories and /featured answered 404. categories[/:id] groups every service by what it is, each with its rule, from Network's exposure overlay and site list: site, platform, library, repository, planned. featured lists the open sites whose last check (not stale) was up or degraded, in the navigation's usage order (7-day page views plus 14-day signed-in history, the chrome service's ranking, now exposed as ranking()), with ranked_at and stale; the hub is not listed and nothing is picked by hand. Hand-kept maps: site hosts now come from each manifest's publicOrigin (only chat, whose manifest has none, names its host; the derived list is identical); READY_PATHS drops tools, whose manifest carries ready, and a test keeps only entries whose manifest lacks one; INTERNAL takes OV_<ID>_INTERNAL_URL overrides (loopback URLs only, others ignored and logged). What cannot come from the manifests yet has a TODO with its plan section: INTERNAL (no internal address in service-manifest@1), EXPOSURE (the override half: what a domain serves today), the sites' presentation fields. Tests: registry-ecosystem (categories, featured, READY_PATHS, env overrides, hosts), chrome (ranking), unknown-paths (both routes answer on the real server)OpenVibers · 24 Sept 2026
9e64f600 -
Internal-key retirement (register C-50/C-52): GET /internal/url-registry/resolved, GET /internal/coins/stats, POST /internal/resolve-anon, POST /internal/identity/legacy-map and POST /internal/link-account (the routes principal_usage still shows called with X-Internal-Key) now also take a service token through principals.guard, and keep the key so callers switch on their own schedule. Capabilities are existing contracts names: identity.subject.resolve for the registry read, anon resolution, legacy-map writes and account links, network.coins.credit for the coin totals. A token may only map ids of its own source_system and only report links for its own service (capability.owner_denied); the key keeps its old behaviour. resolve-batch was already guarded. No grant was added: the only caller of the five is Live, which already holds both capabilities on openvibe.network (Media's resolve-batch grant landed in c3b7cf3; Chat calls none of them, Tools none). docs/retirement.md lists every token route with its capability, rule and holders, and what Live changes (TOKEN_PATHS in server/net/network-principal.js). test/principals.test.js: token, narrow token, ownership, key and usage rows for eachOpenVibers · 24 Sept 2026
829dafa5 -
secrets-out-of-db.js --copy-to-env [--apply]: moves each provider secret the database holds and the env file does not set (an empty VAR= counts as unset) into /etc/openvibe/network.env as VAR=value, after keeping the previous file as network.env.bak-<time> with its mode, so the operator never has to read a value out of sqlite. The database is read by a child process running as its owner and the values reach the file through a pipe, never a terminal; a value that would need quoting is left to add by hand, and a variable the file already sets is left as it is (the dry run says whether it matches). README: the full procedureOpenVibers · 24 Sept 2026
11e716d0 -
Provider secrets from the environment first (roadmap §18.2(12)): the Resend API key and webhook secret, the Discord bot token and OAuth client secret, and the VAPID private key were read only from site_settings in plaintext. Each now has a variable (RESEND_API_KEY, RESEND_WEBHOOK_SECRET, DISCORD_BOT_TOKEN, DISCORD_OAUTH_CLIENT_SECRET, VAPID_PRIVATE_KEY; server/secrets.js) that db.getSetting reads first, so every reader is environment-first; the database row stays the fallback, so nothing changes until the operator moves a value. Which source each uses is reported by name only: the boot line [Secrets] key=env|database|unset, GET /api/admin/secrets (owner), api_key_source in the email status and sources in the Discord settings. The admin panel shows an environment-provided secret as set in the environment (input disabled), and the email, settings and Discord endpoints never save one into the database while its variable is set. Web push derives the VAPID public key from the private key, so a private key in the environment (or a stale public key beside it) still gives a matching pair and no new pair is generated over it. scripts/secrets-out-of-db.js lists the secret setting names with their variable, whether the env file sets it to the same value and whether the running service has it (never a value); --apply --backup <new file> blanks only the copies the environment and the restarted service already provide (a different value only with --allow-different) plus the settings Network never reads (net.* tokens, old ses_* keys); --restore-from <backup> [--apply] rolls back. Run with sudo, it reads the env file and /proc/<pid>/environ, then becomes the database owner. test/provider-secrets.test.jsOpenVibers · 24 Sept 2026
70050bca -
Refresh tokens (roadmap §18.2(2)): oauth_tokens held each refresh token in clear, and presenting a rotated token again only failed. Now only its SHA-256 is stored; every token belongs to the family of the sign-in that issued it (family_id) and carries a generation (0 at sign-in, +1 per rotation); presenting an already rotated token revokes every live token of its family (revoked_reason reuse) and writes an oauth_refresh_reuse audit row with the presented and current generation, never a token. A second use within 10 seconds of the rotation is refused without revoking the family (two tabs refreshing through Live's cookie at once). Boot adds the columns only. Rows written before keep working: a raw row is matched and hashed on first use, and a stored hash presented as a token matches nothing. scripts/hash-refresh-tokens.js hashes the rest in place, one-way: dry run by default, --apply requires --backup <new file> (online, 0600, quick_check), --restore-from <backup> [--apply] puts the raw values back for a rollback to the previous release; run as root it becomes the database owner first (scripts/lib/db-ops.js). server/auth/refresh-tokens.js; test/refresh-tokens.test.jsOpenVibers · 24 Sept 2026
1cf0191c -
OpenID Connect discovery at the issuer (roadmap §4.1, §15.4): https://openvibe.network/.well-known/openid-configuration answered with the account page; discovery lived only at /oauth/.well-known/openid-configuration. The document is now served at the issuer's root, at RFC 8414's /.well-known/oauth-authorization-server and still at the old path, and /.well-known/openvibe points at the standard one. The metadata is made true rather than aspirational: it advertised scope openid and RS256 id tokens while none was ever issued, and named /api/auth/me (not OIDC-shaped) as userinfo. Scope openid now adds an RS256 id_token to the code grant (kid ov-network-1 as the JWKS publishes, aud = client, sub = the Network id as a string like access tokens, subject_id, and the authorize nonce, threaded through the account chooser and stored with the code in oauth_codes.nonce); /oauth/userinfo returns the same claims for an unexpired access token. Without openid the token response is unchanged. server/auth/oidc.js; test/oidc-discovery.test.js boots the real server for the documents and runs the code flow for id_token and userinfoOpenVibers · 24 Sept 2026
abee5dc1 -
openvibe-shared v1.5.0 (Track R release manifest), openvibe-contracts v0.32.0, openvibe-sdk v0.5.0OpenVibers · 23 Sept 2026
0b756833 -
User modules (roadmap Wave 1 item 24): every change emits network.module.updated, account removal and merge handle module rows, onOwnerRemoved is enforced, and chat.preferences belongs to Chat. Events: server/identity/module-events.js builds the envelope (source network, visibility internal, subject { type user_module, id <owner>:<namespace>, revision }, actor = the person, the owning service or system:network) and writes it into network_event_outbox in the transaction that makes the change; payload owner, namespace, namespace_owner, schema_version, revision, change (created|updated|deleted), reason (write|delete|owner_delete|subject_removed|subject_merged), keys (changed top-level field names), public (new values of changed public fields only; never a value of a private namespace), merged_into/merged_from. event-relay.js writerFor() enqueues into the running relay's outbox or, when this process relays nothing (OV_EVENTS_INTERNAL_URL unset, or a script), a passive one on the same table, so rows wait there and the relay sends them; the table is created at boot. Revisions only grow per (subject, namespace): user_module_revisions keeps the last one issued, so a delete has a revision and a re-created record continues after it. Owner services get DELETE /internal/modules/:ns/:subject (token only, If-Match optional). Accounts: onSubjectRemoved() deletes a subject's records and revisions with one event each; onSubjectMerged() moves each record to the survivor where it has none and drops it where it has one (updated_by svc:network), events on both sides. Network has no account deletion or merge today, so triggers refuse deleting a users/anon_users row or changing its subject_id while module rows remain; the one merge that exists, POST /api/auth/anon/:token/link, now moves the guest's records to the account. onOwnerRemoved (contract modules.namespace@1): when the owner's service manifest says retired the namespace is read-only (409 modules.namespace_retired, userWritable false), and a daily sweepRetired() empties delete-after-retention namespaces retentionDays after Network first saw the retirement (user_module_retirements). chat.preferences: OWNER_HANDOFFS makes Chat its owner while the installed manifest (0.30.x) still names live ("moves to OpenVibe.Chat in Wave 6"); Chat gets network.modules.read/write on chat.preferences, Live keeps reading it and its default write grant narrows to chat.tts_defaults and live.profile at boot (only a row still as seeded). The registry lists network.module.updated as an observed Network topic. test/module-events.test.js covers all of it plus the relay publishing rows written while it was off; test/developer-defaults.test.js now checks that nothing is queued while the relay is off (the table exists from boot). README: User modules section and tables.OpenVibers · 23 Sept 2026
ea29f8e8 -
Analytics comes from openvibe-shared/analytics (v1.4.0; retires C-80): server/analytics/ keeps only network.js, the service specifics (SERVICE, PARAM_PREFIXES, PATH_RULES, and PATH_OPTS = { paramPrefixes, pathRules }); openAnalytics keeps its own connection to network.db and returns the package's AnalyticsTracker with retention: false and those path options, so the NetworkAnalyticsTracker subclass, preReducePath and prescrub are gone (the package applies pathRules in the tracker and in the scrub); schedulePrune is retention.schedulePrune. The hand-copied index/privacy/retention/schema/tracker files are deleted. scripts/analytics-prune.js is a wrapper over openvibe-shared/analytics/prune-cli with Network's path options, repo-root-relative paths, the defaultDbPath export and the VACUUM warning in its help. test/analytics-privacy.test.js tests the package with Network's options, checks every written row against analytics/event.v1, and sends a Sec-GPC and a DNT request through Network's routers that leave nothing. New with v1.4.0: a request with Sec-GPC: 1 or DNT: 1 is not recorded at all (no raw row, visitor hash, session id or rate counter); README notes itOpenVibers · 23 Sept 2026
9bbd90f7 -
Go-live notifications from Events: POST /internal/events consumes live.stream.started (source live) and notifies every follower of payload.channel.subject once (STREAM_LIVE, category stream, so the person's mute and email choice apply), plus everyone with stream_live_all, never the streamer; the Discord live alert runs after the commit. Live owns the follow graph, so before the inbox transaction Network reads it from Live's GET /internal/followers?stream_id=&limit=&after= with a self-signed service token (sub svc:network, aud openvibe.live, cap live.follower.read; server/notifications/live-followers.js), resolving followers by usr_ subject, then Network user id; if Live cannot answer the delivery gets 503 and nothing is recorded, so Events retries. A redelivery is a duplicate before Live is asked. A start more than 30 minutes old announces nothing. The per-streamer window (1/hour, 8/day) moves to server/notifications/stream-live.js and is shared with POST /internal/events/stream-live, so Live's direct call and the event cannot both announce during the switch. scripts/subscribe-events.js subscribes the new topic too (--topic to add it alone). test/events-consumer.test.js: followers notified once, preference applies, duplicate ignored without asking Live, v1-only refused, Live down then retried, stale/foreign/mismatched/unknown streams ignoredOpenVibers · 23 Sept 2026
2a95548a -
Registry completeness: /api/v1/registry/topics lists every event type from the manifests' eventsProduced/eventsConsumed and the v0.30 payload contracts (producers, consumers with the Events-syntax pattern that matched, payload contract with its $id and status; filters service/producer/consumer/prefix, plus /topics/:topic). Live and Media list no eventsProduced in contracts 0.30.1, so server/registry/topics.js OBSERVED declares what their code publishes (live.stream.started/ended from stream-events.js; media.vod/clip ready|failed, media.object.uploaded, media.storage.alert|recovered from Media server/events.js), marked observed on the service row, and Network's consumed topics come from its own Events consumer TOPICS. New /releases (each running service's /release.json from the loopback health poll, now keeping contracts_version and openvibe-* package versions, with drift against the libraries' current releases), /health and /search?q=. Payload schemas are served at their $id (/contracts/events/payloads/<type>.v1.json) and are CORS-public. scripts/contracts-drift.js warns (::warning:: in Actions, exit 0 unless --strict) about services pinning an older openvibe-contracts/sdk/shared than the latest tag or an unpublished tag, from GitHub, local checkouts (--dir) or the running registry (--registry); CI runs it as a continue-on-error step and weekly. test/registry-topics.test.jsOpenVibers · 23 Sept 2026
5ad26eca -
Notifications from Events: POST /internal/events turns deals.watch.matched (payload.recipient) and trade.alert.triggered (subject user) into inbox notifications (DEAL_WATCH_MATCH / TRADE_ALERT, category service, so the person's mute and email choices apply). openvibe-sdk v0.4.0 parseDelivery with requireV2 (v1-only, stale or forged deliveries are refused), exactly once per event_id through the SDK inbox (network_event_inbox), unknown types acknowledged and ignored, links only to sites whose domain serves the service. Inert until NETWORK_EVENTS_SECRET is set and scripts/subscribe-events.js has created the two subscriptions (consumer network, endpoint http://127.0.0.1:4000/internal/events). Coupons publishes no watch event yet, so there is nothing of it to consumeOpenVibers · 23 Sept 2026
07cd98c7 -
Registry, status and chrome say where each service really answers: an exposure overlay (server/registry/exposure.js) gives every manifest one state — live (public), internal (loopback only, no public site yet), library (released), repository or placeholder — from curl checks of every public domain on 2026-09-23. News, Reviews, Deals, Coupons, Trade, Host, Tips, VIP, OpenRe, Search and Sources are internal (their domains serve a placeholder page); AI, Chat internal; SDK v0.4.0, Shared v1.3.0, Contracts v0.29.0 libraries; Examples a repository. The descriptor gives an origin only to live services (planned_origin otherwise), AI is polled on loopback, /status reads "up (loopback only)" and never a bare Up for a placeholder domain, and the chrome nav derives open/soon from the same table. test/registry-exposure.test.js pins the statesOpenVibers · 23 Sept 2026
29cdb09a -
Analytics within ADR-021: no IP, user id or city in raw events; route templates, referer origins and user-agent classes; rotating session ids; raw events pruned after 30 days (nightly job, bounded batches), rollups keptOpenVibers · 23 Sept 2026
3ffd73a9 -
Developer projects work without staff in sandbox: DEV_SANDBOX_AUDIENCES defaults to openvibe.media, openvibe.events, openvibe.tools; sandbox allowance (DEV_SANDBOX_ALLOWANCE, default media.object.upload/read, events.app.publish/read/subscribe, tools.job.create/read/cancel, public capabilities only) for sandbox apps; production allowance stays staff-set. dev_audit events relayed to OpenVibe.Events through an openvibe-sdk v0.2.2 outbox when OV_EVENTS_INTERNAL_URL is set (backfilled, self-signed svc:network token)OpenVibers · 23 Sept 2026
a46f6096 -
Docs: developer projects model, token claims, limits and quotas; contracts proposal (app claims, sandbox refusal, prj prefix, partner visibility, network.project.read|manage, network events)OpenVibers · 23 Sept 2026
84697895 -
Track O: operator status and readiness. GET /status (server-rendered, noindex), /api/v1/status and /api/v1/status/slo; /api/ready with named checks; /metrics (loopback only) with token issuance by grant type and principal-token failures; the registry poll reads each service's readiness and /release.json and reports up|degraded|down|not-running|unknown, never a stale up; openvibe-shared v1.3.0. (Most of this change was swept into 89b67de by a concurrent commit; this adds the README section, the assigned ports of new services and one page fix.)OpenVibers · 23 Sept 2026
bc748936 -
openvibe-shared is the pinned OpenVibe.Shared v1.0.0 release: packages/openvibe-shared is gone, /shared/* (and /shared/v1/*) serves only the package's browser files from node_modules, a ?v= content-hash URL is immutable and nginx no longer adds its own Cache-ControlOpenVibers · 23 Sept 2026
d0771c7c -
Initial OpenVibe releaseOpenVibers · 17 Aug 2026
db04c27f