openvibers/openvibe.space README.md

README.md

main 13.5 KB · 159 lines · markdown

1# OpenVibe.Space
2
3**https://openvibe.space — spaces (apps people make on their OpenVibe account) and static hosting for the OpenVibe network.**
4
5**Live site:** https://openvibe.space · **API docs:** https://openvibe.services · **For AI:** https://openvibe.space/llms.txt
6**Run it:** `npm ci && npm run dev` (Node 22 or newer) · **Tests:** `npm test`
7
8## Purpose and status
9
10Space owns static hosting. The Stage B engine moved here from OpenVibe.Host as code; production had no projects, sites or deploys to migrate. It supports projects and members, immutable content-addressed deploys, activation and one-step rollback, private previews, CI Git ingest, per-site configuration, verified custom domains, takedowns, Media-backed objects, Host-header tenant serving, and a resource index. Tenant names serve at `<name>.openvibe.website`.
11
12Public hosting remains off by default (`SPACE_PUBLIC_HOSTING=off`). Only OpenVibe staff can create projects. Abuse handling and a designated DMCA agent precede public launch.
13
14**Spaces** (plan T20 step 2; `server/spaces/`, `docs/spaces.md`) are small web apps people make here: described to the AI
15builder (OpenVibe.AI `space.app.build`), started from a template (guestbook, AI buddy, poll, reaction game, trivia,
16private to-dos, blank) or forked, then edited in a browser editor with a live preview. A space uses the visitor's
17OpenVibe account through the space SDK (`/_openvibe/sdk.js`): sign-in that the visitor approves once, shared lists
18(`OV.items`), private storage (`OV.kv`), the space's own AI (`OV.ai.ask`, OpenVibe.AI `space.app.ask` on behalf of
19the visitor) and its own server code (`functions/<name>.js`, run in a QuickJS WebAssembly sandbox at `/api/<name>`
20and from `OV.fn`). Making, editing, previewing and the AI builder work for everyone signed in. Publishing deploys a space to
21a site of its maker's Spaces project at `<slug>.openvibe.website`, so it follows the same launch switch: staff until
22`SPACE_PUBLIC_HOSTING=on`.
23
24OpenVibe.Host is the hosting price comparison; its copy of the engine is being removed, and its old hosting URLs will answer 410 with a link here.
25
26## Owns
27
28- Spaces: their source files and settings, the gallery, the editor, the space SDK and its API, each space's shared lists,
29 its visitors' private storage, the sign-ins visitors approved, and the daily AI allowances of spaces and builders.
30- Static hosting: projects and members, sites, immutable content-addressed deploys, activation and rollback,
31 previews, Git ingest, per-site configuration, custom domains with TXT verification, and takedowns.
32- Serving tenant sites by Host header at `<name>.openvibe.website` and on verified custom domains.
33- Its tables in `ov_space` (`space_*`), its control log and its outbox (`space.deploy.*`, `space.domain.verified`).
34
35## Does not own
36
37- People, sessions and projects' identity (OpenVibe.Network), stored bytes when `SPACE_OBJECT_STORE=media`
38 (OpenVibe.Media), money and usage rating (OpenVibe.Billing), the forum (OpenVibe.Community, see below) and the
39 hosting price comparison (OpenVibe.Host).
40
41## Depends on
42
43- **OpenVibe.Network:** sign-in (OAuth with PKCE), tokens and their keys, project membership.
44- **OpenVibe.Events:** the outbox relay, when `EVENTS_URL` is set, and account export and deletion (ADR-033):
45 `network.account.export_requested` and `network.account.deleted` reach the loopback `/internal/events` consumer
46 (`server/account-data.js`, openvibe-sdk/account-data), signed with `SPACE_EVENTS_SECRET`. A person's spaces are
47 exported with their files; on deletion their published spaces stop serving and the spaces go with what others kept
48 in them, and their list items, private storage and sign-ins leave other people's spaces.
49- **OpenVibe.Media:** tenant objects, when `SPACE_OBJECT_STORE=media`.
50- **OpenVibe.Billing:** storage readings, when `SPACE_BILLING_INTERVAL_MS` is on.
51- **OpenVibe.Services:** reads the resource index and sends control requests.
52- **OpenVibe.AI:** the space builder and a space's own AI (`space.*` workflows; Network grants `ai.run.create` and
53 `ai.run.read` on `space.*`).
54
55## The forum moved to OpenVibe.Community
56
57The forum lived here from 3 October 2026 (it moved off Community then). On **2026-10-08 the owner
58moved it back**: OpenVibe.Community owns the forum (spaces, threads, posts, votes, categories,
59per-space moderators, the Discord relay) again, and Space became the network's hosting (static sites now; dynamic pages and spaces next).
60
61So Space keeps no forum code and **every forum URL answers a permanent redirect to the forum's
62address on https://openvibe.community** (which moved from `/s` to `/forum` on 2026-11-07):
63
64| Path | Methods | Status | Goes to |
65| --- | --- | --- | --- |
66| `/s`, `/s/*` (pages, feeds) | GET, HEAD | `301` | `/forum/<space>/<thread>` (the `/t/` segment is gone) |
67| `/s`, `/s/*` | anything else | `308` (the method and body are preserved) | the same `/forum` path |
68| `/api/v1/spaces*`, `/api/v1/posts/*`, `/api/v1/space-groups*`, `/api/v1/relay*` | all | `308` | the same API path |
69
70A redirected bookmark, crawler or API client therefore lands on Community's copy in one hop.
71Nothing that is not a forum path redirects.
72
73## Routes
74
75| Route | Purpose |
76| --- | --- |
77| `/` | Spaces and static hosting |
78| `/spaces`, `/spaces/new`, `/spaces/mine`, `/spaces/:slug` | The gallery and templates, making a space (template or AI builder), your spaces, a space's page |
79| `/spaces/:slug/edit` and its JSON calls (`files`, `settings`, `ai`, `publish`, `unpublish`, `delete`, `preview`) | The editor (maker or staff; same origin with the form token in `X-CSRF-Token`) |
80| `/spaces/:slug/fork`, `/spaces/:slug/connect` | Fork a space; the sign-in a visitor approves for a published space |
81| `/_openvibe/sdk.js` | The space SDK: live on a space's own address, preview mode on openvibe.space |
82| `/api/v1/apps/:id/token`, `/api/v1/apps/:id/run`, `/api/v1/apps/:id/items/:collection` | The space SDK's API, from the space's own origin only (CORS for that origin, Bearer space token, never cookies) |
83| `/dashboard`, `/projects/:id`, `/sites/:id`, `/deploys/:id` | Signed-in hosting dashboard and forms |
84| `/preview/:deployId/*` | Private, sandboxed deploy preview |
85| `/api/v1/projects`, `/api/v1/sites`, `/api/v1/deploys`, `/api/v1/domains` | Hosting API (Bearer tokens, no cookie auth) |
86| `/api/v1/resources` | First-party resource index |
87| `POST /api/v1/resources/control` | First-party control: `delete` a site, deploy or domain for a person (Space's own roles decide) |
88| `/limits.json` | Enforced hosting limits |
89| `/auth/login|callback|fedcm|logout|me|refresh` | Space's Network OAuth client |
90| `/robots.txt`, `/llms.txt`, `/llms-full.txt`, `/sitemap.xml` | Discovery for Space's public page |
91| `/api/health`, `/api/ready`, `/release.json`, `/metrics` | Health, release and observability |
92
93Sitemap entries carry lastmod from the release commit or, for hosted pages, the active deploy.
94
95Tenant requests are dispatched by Host header before dashboard routes. `deploy/nginx/openvibe.website.conf` is a reference wildcard vhost; the existing `openvibe.space` vhost includes larger upload locations. Neither is installed by this code change. Unknown hosts answer 404. Tenant sites cannot reach the dashboard, API, auth or metrics. A custom domain serves only after TXT verification.
96
97## Capabilities and events
98
99The hosting API guards `space.site.manage`, `space.site.config`, `space.deploy.create`, `space.domain.manage`, `space.resource.read` and `space.resource.control` (openvibe-contracts 0.170.0, owner `space`); every route names exactly one, and `npx openvibe-contracts-check --service space` checks the code against the release. Service tokens need audience `openvibe.space`, and project membership still applies. The per-actor limits (`server/http/actor-limits.js`) count uploads, domain checks, project creation and writes per person or app; those are limit buckets, not capabilities. The retired `space.forum.*` contracts are unrelated to hosting. The control route (`server/registry/resource-control.js`, plan T13 step 10) takes a `common.resource-control-request@1` from OpenVibe.Services and acts as the person in `on_behalf_of` through the same domain code as the DELETE routes: deleting needs maintainer or owner, a non-member gets `resources.unknown_resource`, and the served deploy (`deploy.active`), a default domain (`domain.default`) and anything taken down are refused with Space's own reason. Dry runs; repeats with the same key return the first answer (`space_control_log`).
100
101The transactional outbox emits `space.deploy.created`, `space.deploy.activated`, `space.deploy.failed` and `space.domain.verified`; each has a payload schema in openvibe-contracts, and the SDK outbox refuses any other type.
102
103## Data and configuration
104
105`migrations/0002_initial.sql` through `0005_git_sources.sql` create the hosting tables, `0006_billing_readings.sql` the usage-reading outbox,, indexes and immutable-artifact triggers from Host Stage B with `space_` names. They run through `openvibe-sdk/db` at boot; tables are never created by a request. `0001_initial.sql` remains the historical forum schema, which this release does not drop. This is a code move, with no data copy.
106
107`migrations/0009_spaces.sql` creates the spaces tables (`space_apps`, `space_app_files`, `space_app_projects`, `space_app_items`, `space_app_kv`, `space_app_grants`, `space_app_codes`, `space_app_tokens`, `space_app_counters`). Only hashes of sign-in codes and tokens are kept.
108`migrations/0010_account_data.sql` creates openvibe-sdk/account-data's `account_data_events` (the forum-era table of that name is kept as `account_data_events_forum`).
109
110Request telemetry is aggregated by route template, method and status class, never by raw URL. Once a minute one `http.request` sample per key, plus the active-requests, p95 latency and event-loop-lag gauges, is written to this service's own `telemetry_samples` table (openvibe-sdk 0.46 telemetry, the `platform.telemetry-sample@1` shape), which `migrations/0008_telemetry.sql` creates; rows older than 14 days are deleted daily. `/shared` files, static assets, health, readiness and `/metrics` are not counted. `server/app.js` mounts openvibe-sdk's `telemetryMiddleware` (a no-op until init); `server/index.js` calls `telemetry.init()` at production start, runs the 14-day retention and flushes on stop.
111
112Copy `.env.example` to `.env`. Production uses `/etc/openvibe/space.env` (0600). Important values:
113
114| Variable | Default | Purpose |
115| --- | --- | --- |
116| `SPACE_PUBLIC_HOSTING` | `off` | Staff-only project creation (and publishing spaces) until launch |
117| `SPACE_EVENTS_SECRET` | unset | Signs OpenVibe.Events deliveries to `/internal/events` (account export and deletion); unset answers 503 |
118| `SPACE_AI`, `OV_AI_INTERNAL_URL` | `on`, `http://127.0.0.1:4700` | The space builder and spaces' AI through OpenVibe.AI |
119| `SPACE_SPACES_PER_OWNER`, `SPACE_AI_BUILDS_PER_DAY`, `SPACE_AI_ASKS_PER_SPACE_PER_DAY` | 25, 20, 500 | Spaces per person, AI builds per person a day, AI answers per space a day (more `SPACE_SPACE_*` limits in `server/hosting-config.js`) |
120| `SPACE_SITES_DOMAIN` | `openvibe.website` | Tenant names |
121| `SPACE_STORAGE_DIR` | `./data/objects` | Content-addressed local object store |
122| `SPACE_OBJECT_STORE` | `local` | Set `media` for Media write-through and cache |
123| `SPACE_FORM_SECRET` | random per process | Dashboard CSRF form signing |
124| `SPACE_WORKER` | on | Domain rechecks, object sweep, outbox pruning |
125| `EVENTS_URL` | unset | Outbox relay destination; unset keeps rows queued |
126| `SPACE_BILLING_INTERVAL_MS` | `0` (off) | Daily `gb-month` storage readings to OpenVibe.Billing (`OV_BILLING_URL`) in local mode, per OpenVibe.Network project (`network_project_id`; a project that names none gets no reading); Media mode emits none (Media bills the bytes) |
127| `DATABASE_URL`, `DATABASE_DIRECT_URL` | unset | PostgreSQL serving and owner migration connections |
128
129Other `SPACE_*` quota, upload, domain and DNS settings are listed in `.env.example` and `server/hosting-config.js`. Space keeps its existing OAuth and forum redirect settings.
130
131## Tests and running
132
133```sh
134npm install
135npm start
136npm test
137```
138
139`test/hosting-core.test.js` exercises migrations, launch gate, project/site creation, tenant dispatch, immutable deploys, pointer rollback and events directly against PGlite without a socket. The ported Stage B integration files under `test/hosting-*.test.js` use `test/stageb/boot.js` and raw HTTP for Host-header and request-target cases. The existing `home`, `redirects`, and `boot` tests cover Space's public app and forum redirects.
140
141## Security and dependencies
142
143Static uploads are validated before storage; tenant content runs on a separate registrable domain and previews use a sandbox CSP. APIs ignore cookies. OpenVibe.Network supplies identity; `svc:space` needs `events.event.publish`, `billing.usage.record` (audience `openvibe.billing`) once `SPACE_BILLING_INTERVAL_MS` is on, and, when `SPACE_OBJECT_STORE=media`, the `media.object.upload/read/list/delete` grants for the `space` namespace. OpenVibe.Media may hold tenant objects; OpenVibe.Events receives the outbox when configured. Space's own database is `ov_space` through `openvibe-sdk/db`. See [SECURITY.md](SECURITY.md).
144
145## Deploy
146
147`sudo ovhost deploy space` on the host: the release layout under `/opt/openvibe.space`, `/etc/openvibe/space.env`, the
148`openvibe-space` unit, migrations at boot. The vhosts are `deploy/nginx/` (installed with `ov-vhost-install`).
149
150## License
151
152AGPL-3.0-only. See [LICENSE](LICENSE).
153
154<!-- versions:start -->
155- openvibe-contracts: v0.419.0
156- openvibe-sdk: v0.161.0
157- openvibe-shared: v4.241.0
158<!-- versions:end -->
159