openvibers/openvibe.contracts Commits
Contracts 0.149.0: the static hosting engine's contracts as space.* (T20 step 2) (#105)
Changes
@@@ -4,6 +4,18 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) thfrom `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,the notes were in the tag and commit messages (`git tag -n1`).++## 0.149.0 — 2026-10-12++++- The static hosting engine moved from OpenVibe.Host to OpenVibe.Space (plan T20 step 2), so its contracts are now++ `space.*`: capabilities `space.site.manage`, `space.deploy.create`, `space.domain.manage`, `space.site.config` and++ `space.resource.read`; schemas `space.project`, `space.site`, `space.deploy`, `space.domain`, `space.site-config` and++ their request/result pairs; events `space.deploy.created`, `space.deploy.activated`, `space.deploy.failed` and++ `space.domain.verified`, each with a payload schema (Host only ever had one for `deploy.activated`). Tenant sites are++ `<site>.openvibe.website`; a custom domain is verified by `_openvibe-space.<hostname>`. The space manifest owns them++ and its catalog text says what is built (staff-only until the public launch).++- `space.site` uses the site id pattern the engine has always issued (`sit_`); `host.site` said `site_`.++- The `host.*` hosting ids stay until OpenVibe.Host has removed the engine; a later release retires them.++## 0.148.0 — 2026-10-12- Catalog text (`site.what`) for rent, food, work and games names what shipped today: Rent's affordable housing from@@@ -6856,6 +6856,163 @@"visibility": "public","compatibility": "backward","status": "active"++ },++ {++ "id": "space.deploy-create-request",++ "version": "1.0.0",++ "owner": "space",++ "schema": "space/deploy-create-request.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "space.deploy-create-result",++ "version": "1.0.0",++ "owner": "space",++ "schema": "space/deploy-create-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "space.deploy",++ "version": "1.0.0",++ "owner": "space",++ "schema": "space/deploy.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "space.domain-manage-request",++ "version": "1.0.0",++ "owner": "space",++ "schema": "space/domain-manage-request.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "space.domain-manage-result",++ "version": "1.0.0",++ "owner": "space",++ "schema": "space/domain-manage-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "space.domain",++ "version": "1.0.0",++ "owner": "space",++ "schema": "space/domain.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "space.project",++ "version": "1.0.0",++ "owner": "space",++ "schema": "space/project.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "space.site-manage-request",++ "version": "1.0.0",++ "owner": "space",++ "schema": "space/site-manage-request.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "space.site-manage-result",++ "version": "1.0.0",++ "owner": "space",++ "schema": "space/site-manage-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "space.site",++ "version": "1.0.0",++ "owner": "space",++ "schema": "space/site.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "space.site-config",++ "version": "1.0.0",++ "owner": "space",++ "schema": "space/site-config.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "space.site-config-request",++ "version": "1.0.0",++ "owner": "space",++ "schema": "space/site-config-request.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "space.site-config-result",++ "version": "1.0.0",++ "owner": "space",++ "schema": "space/site-config-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "space.deploy.activated",++ "version": "1.0.0",++ "owner": "space",++ "schema": "events/payloads/space.deploy.activated.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active",++ "adr": "ADR-004"++ },++ {++ "id": "space.deploy.created",++ "version": "1.0.0",++ "owner": "space",++ "schema": "events/payloads/space.deploy.created.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active",++ "adr": "ADR-004"++ },++ {++ "id": "space.deploy.failed",++ "version": "1.0.0",++ "owner": "space",++ "schema": "events/payloads/space.deploy.failed.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active",++ "adr": "ADR-004"++ },++ {++ "id": "space.domain.verified",++ "version": "1.0.0",++ "owner": "space",++ "schema": "events/payloads/space.domain.verified.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active",++ "adr": "ADR-004"}]}nnew file mode 100644@@@ -0,0 +1,47 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/space.deploy.activated.v1.json",++ "title": "SpaceDeployActivatedPayload",++ "description": "space.deploy.activated v1 (OpenVibe.Space server/domain/deploys.js switchPointer, Stage B tenant hosting). A tenant site's active deploy changed, written into Host's event outbox in the transaction that moves the pointer (activate or rollback). Envelope: subject { type: deploy, id: <deploy id> }, visibility internal, priority low, actor the person or app that switched it. A network service's release going live is space.release.published, not this.",++ "type": "object",++ "required": [++ "project_id",++ "site_id",++ "site",++ "deploy_id",++ "previous_deploy_id",++ "rollback"++ ],++ "additionalProperties": false,++ "properties": {++ "project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "site_id": {++ "type": "string",++ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "site": {++ "type": "string",++ "pattern": "^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){2,39}$",++ "description": "The site's name (<site>.openvibe.website)."++ },++ "deploy_id": {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "previous_deploy_id": {++ "type": [++ "string",++ "null"++ ],++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The deploy that served before; null for the site's first activation."++ },++ "rollback": {++ "type": "boolean",++ "description": "true when the switch was a rollback."++ }++ }++}nnew file mode 100644@@@ -0,0 +1,54 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/space.deploy.created.v1.json",++ "title": "SpaceDeployCreatedPayload",++ "description": "space.deploy.created v1 (OpenVibe.Space, subject deploy dpl_…, visibility internal): an upload or a Git ingest became an immutable, content-addressed deploy in the ready state. It is not yet serving: space.deploy.activated follows when it is made active. No file contents or paths.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "project_id",++ "site_id",++ "site",++ "source",++ "file_count",++ "total_bytes",++ "manifest_sha256"++ ],++ "properties": {++ "project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "site_id": {++ "type": "string",++ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "site": {++ "type": "string",++ "pattern": "^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){2,39}$",++ "description": "the site name (one DNS label; the site is served at <site>.openvibe.website)"++ },++ "source": {++ "enum": [++ "archive",++ "files",++ "preview",++ "git"++ ],++ "description": "how the deploy arrived: an archive or individual files, a private preview upload, or a Git ingest (always a preview)"++ },++ "file_count": {++ "type": "integer",++ "minimum": 0++ },++ "total_bytes": {++ "type": "integer",++ "minimum": 0++ },++ "manifest_sha256": {++ "type": "string",++ "pattern": "^[a-f0-9]{64}$",++ "description": "sha256 of the deploy manifest (the content address of the whole deploy)"++ }++ }++}nnew file mode 100644@@@ -0,0 +1,44 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/space.deploy.failed.v1.json",++ "title": "SpaceDeployFailedPayload",++ "description": "space.deploy.failed v1 (OpenVibe.Space, subject deploy dpl_…, visibility internal): an upload was refused and recorded as a failed deploy with its log, so the owner can see why. Problem codes only, at most ten; never file contents.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "project_id",++ "site_id",++ "site",++ "code",++ "problems"++ ],++ "properties": {++ "project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "site_id": {++ "type": "string",++ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "site": {++ "type": "string",++ "pattern": "^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){2,39}$",++ "description": "the site name (one DNS label; the site is served at <site>.openvibe.website)"++ },++ "code": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)+$",++ "description": "the refusal problem code (for example deploy.too_large or storage.space_full)"++ },++ "problems": {++ "type": "array",++ "maxItems": 10,++ "items": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)*$"++ },++ "description": "the problem codes of the files that were refused"++ }++ }++}nnew file mode 100644@@@ -0,0 +1,35 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/space.domain.verified.v1.json",++ "title": "SpaceDomainVerifiedPayload",++ "description": "space.domain.verified v1 (OpenVibe.Space, subject domain dom_…, visibility internal): a custom domain's TXT record (_openvibe-space.<hostname>) was found, and the domain now serves the site.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "project_id",++ "site_id",++ "site",++ "hostname"++ ],++ "properties": {++ "project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "site_id": {++ "type": "string",++ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "site": {++ "type": "string",++ "pattern": "^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){2,39}$",++ "description": "the site name (one DNS label; the site is served at <site>.openvibe.website)"++ },++ "hostname": {++ "type": "string",++ "minLength": 4,++ "maxLength": 253,++ "description": "the custom domain, a fully qualified host name in lower case"++ }++ }++}nnew file mode 100644@@@ -0,0 +1,44 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/deploy-create-request.v1.json",++ "title": "SpaceDeployCreateRequest",++ "description": "space.deploy-create-request@1: bodies of space.deploy.create on OpenVibe.Space (a deployer or above of the project; an app principal added as deployer can deploy from CI). POST /api/v1/sites/:id/deploys uploads a deploy: the raw body is a tar or tar.gz archive (Content-Type application/gzip, application/x-gzip, application/x-tar or application/octet-stream), or multipart/form-data with archive=<one archive> or files=<file>… (each part's filename is its path); optional fields activate=1, root=<subdirectory to deploy> and strip=folder (removes a shared first segment) also work as query parameters. It is validated (path traversal, links, special and hidden files, server-side code, file types, sizes and quotas) and never executed; a refusal is a problem whose extra carries the failed deploy_id and its log (413 upload.too_large, quota.storage; 422 validation codes; 429 quota.deploys_per_day; 403 site.taken_down; 503 upload.busy; 507 storage.host_full). POST /api/v1/deploys/:id/activate { expected_active? } and POST /api/v1/sites/:id/rollback { deploy_id?, expected_active? } switch the site's pointer in one step (rollback without deploy_id goes to the deploy active before; 409 deploy.not_ready, site.active_changed when expected_active is stale, deploy.no_previous). GET /api/v1/sites/:id/deploys, /deploys/:id and /deploys/:id/log take no body.",++ "anyOf": [++ {++ "type": "string",++ "contentEncoding": "binary",++ "description": "The upload itself: the tar or tar.gz bytes, or the multipart/form-data body (as common.binary@1)."++ },++ {++ "type": "object",++ "properties": {++ "deploy_id": {++ "anyOf": [++ {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ {++ "type": "null"++ }++ ]++ },++ "expected_active": {++ "anyOf": [++ {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ {++ "type": "null"++ }++ ],++ "description": "The deploy you saw active (null: none); refused with 409 site.active_changed when it moved."++ }++ }++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,171 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/deploy-create-result.v1.json",++ "title": "SpaceDeployCreateResult",++ "description": "space.deploy-create-result@1: answers of space.deploy.create on OpenVibe.Space. GET /api/v1/sites/:id/deploys?limit= → { active_deploy_id, deploys, activations } (newest first; the last 20 pointer switches). POST /api/v1/sites/:id/deploys → 201 { deploy, activated, url } (the ready deploy with its validation log; activated when activate=1 switched the site to it). GET /api/v1/deploys/:id → { deploy } with its files. GET /api/v1/deploys/:id/log → { deploy_id, state, log }. POST /api/v1/deploys/:id/activate and /sites/:id/rollback → { active_deploy_id, previous_deploy_id, changed } (changed false when it was already active). Emits space.deploy.created|activated|failed.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "active_deploy_id",++ "deploys",++ "activations"++ ],++ "properties": {++ "active_deploy_id": {++ "anyOf": [++ {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ {++ "type": "null"++ }++ ]++ },++ "deploys": {++ "type": "array",++ "items": {++ "$ref": "deploy.v1.json"++ }++ },++ "activations": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "deploy_id",++ "previous_deploy_id",++ "kind",++ "actor",++ "at"++ ],++ "properties": {++ "deploy_id": {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "previous_deploy_id": {++ "anyOf": [++ {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ {++ "type": "null"++ }++ ]++ },++ "kind": {++ "enum": [++ "activate",++ "rollback"++ ]++ },++ "actor": {++ "type": "string"++ },++ "at": {++ "type": "string",++ "format": "date-time"++ }++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "deploy"++ ],++ "properties": {++ "deploy": {++ "$ref": "deploy.v1.json"++ },++ "activated": {++ "type": "boolean"++ },++ "url": {++ "type": "string"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "deploy_id",++ "state",++ "log"++ ],++ "properties": {++ "deploy_id": {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "state": {++ "enum": [++ "ready",++ "failed",++ "deleted"++ ]++ },++ "log": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "level",++ "message",++ "at"++ ],++ "properties": {++ "level": {++ "enum": [++ "info",++ "warn",++ "error"++ ]++ },++ "message": {++ "type": "string"++ },++ "at": {++ "type": "string",++ "format": "date-time"++ }++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "active_deploy_id",++ "previous_deploy_id",++ "changed"++ ],++ "properties": {++ "active_deploy_id": {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "previous_deploy_id": {++ "anyOf": [++ {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ {++ "type": "null"++ }++ ]++ },++ "changed": {++ "type": "boolean"++ }++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,167 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/deploy.v1.json",++ "title": "SpaceDeploy",++ "description": "space.deploy@1: one immutable static deploy on OpenVibe.Space (server/http/serialize.js deploy): ready (validated and stored, never executed) or failed (refused, with its failure code and log), whether the site serves it, its size and manifest hash. GET /api/v1/deploys/:id adds the files; the upload answer adds the log.",++ "type": "object",++ "required": [++ "id",++ "site_id",++ "project_id",++ "state",++ "active",++ "source",++ "file_count",++ "total_bytes",++ "new_bytes",++ "manifest_sha256",++ "failure_code",++ "created_by",++ "created_at"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "site_id": {++ "type": "string",++ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "state": {++ "enum": [++ "ready",++ "failed",++ "deleted"++ ]++ },++ "active": {++ "type": "boolean"++ },++ "source": {++ "enum": [++ "archive",++ "files",++ "preview",++ "git"++ ]++ },++ "git": {++ "type": "object",++ "required": [++ "provider",++ "repo_url",++ "ref",++ "commit_sha"++ ],++ "properties": {++ "provider": {++ "type": "string"++ },++ "repo_url": {++ "type": "string"++ },++ "ref": {++ "type": "string"++ },++ "commit_sha": {++ "type": "string",++ "pattern": "^[0-9a-f]{40}$"++ }++ },++ "description": "Present on source git: the commit this deploy was built from (public provenance, never a credential)."++ },++ "file_count": {++ "type": "integer",++ "minimum": 0++ },++ "total_bytes": {++ "type": "integer",++ "minimum": 0++ },++ "new_bytes": {++ "type": "integer",++ "minimum": 0,++ "description": "Bytes this deploy added to the project's store."++ },++ "manifest_sha256": {++ "type": [++ "string",++ "null"++ ],++ "pattern": "^[0-9a-f]{64}$"++ },++ "failure_code": {++ "type": [++ "string",++ "null"++ ]++ },++ "created_by": {++ "type": "string"++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "files": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "path",++ "sha256",++ "size",++ "content_type"++ ],++ "properties": {++ "path": {++ "type": "string"++ },++ "sha256": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$"++ },++ "size": {++ "type": "integer",++ "minimum": 0++ },++ "content_type": {++ "type": "string"++ }++ }++ }++ },++ "log": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "level",++ "message",++ "at"++ ],++ "properties": {++ "level": {++ "enum": [++ "info",++ "warn",++ "error"++ ]++ },++ "message": {++ "type": "string"++ },++ "at": {++ "type": "string",++ "format": "date-time"++ }++ }++ }++ }++ }++}nnew file mode 100644@@@ -0,0 +1,25 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/domain-manage-request.v1.json",++ "title": "SpaceDomainManageRequest",++ "description": "space.domain-manage-request@1: bodies of space.domain.manage on OpenVibe.Space (a maintainer or above of the project). POST /api/v1/sites/:id/domains { hostname } adds a custom domain, pending until its TXT record is verified: a fully qualified host name, lowercased (422 domain.invalid_hostname; 422 domain.reserved for any OpenVibe domain; 403 domain.sandbox in sandbox projects; 429 quota.custom_domains; 409 domain.exists, domain.taken). GET /api/v1/sites/:id/domains, POST /api/v1/domains/:id/verify (checks the TXT record now) and DELETE /api/v1/domains/:id (409 domain.default: the default domain goes only with the site) take no body.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "hostname"++ ],++ "properties": {++ "hostname": {++ "type": "string",++ "minLength": 1,++ "maxLength": 254,++ "description": "e.g. www.example.org (a trailing dot is dropped)."++ }++ }++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,44 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/domain-manage-result.v1.json",++ "title": "SpaceDomainManageResult",++ "description": "space.domain-manage-result@1: answers of space.domain.manage on OpenVibe.Space. GET /api/v1/sites/:id/domains → { domains } (the default domain first). POST /api/v1/sites/:id/domains → 201 { domain } with the DNS records to publish. POST /api/v1/domains/:id/verify → { domain } after the check (status verified and served once the TXT record matches, else last_error says what was found; emits space.domain.verified). DELETE /api/v1/domains/:id → { deleted: true }.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "domains"++ ],++ "properties": {++ "domains": {++ "type": "array",++ "items": {++ "$ref": "domain.v1.json"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "domain"++ ],++ "properties": {++ "domain": {++ "$ref": "domain.v1.json"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "deleted"++ ],++ "properties": {++ "deleted": {++ "const": true++ }++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,131 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/domain.v1.json",++ "title": "SpaceDomain",++ "description": "space.domain@1: one domain of a static site on OpenVibe.Space (server/http/serialize.js domain): the default <site>.openvibe.website, or a custom domain that is served only once its DNS TXT record is verified (re-checked daily; it lapses when the record is gone). A custom domain carries the records its owner must publish (the TXT verification value is public by design and shown only to members); TLS certificates are issued by an operator.",++ "type": "object",++ "required": [++ "id",++ "site_id",++ "hostname",++ "kind",++ "status",++ "served",++ "verified_at",++ "last_checked_at",++ "last_error"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^dom_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "site_id": {++ "type": "string",++ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "hostname": {++ "type": "string"++ },++ "kind": {++ "enum": [++ "default",++ "custom"++ ]++ },++ "status": {++ "enum": [++ "pending",++ "verified",++ "failed",++ "lapsed"++ ]++ },++ "served": {++ "type": "boolean"++ },++ "verified_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "last_checked_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "last_error": {++ "type": [++ "string",++ "null"++ ]++ },++ "instructions": {++ "type": "object",++ "required": [++ "verification",++ "routing",++ "tls"++ ],++ "properties": {++ "verification": {++ "type": "object",++ "required": [++ "type",++ "name",++ "value"++ ],++ "properties": {++ "type": {++ "const": "TXT"++ },++ "name": {++ "type": "string",++ "description": "_openvibe-space.<hostname>"++ },++ "value": {++ "type": "string",++ "description": "openvibe-space-verification=<token>"++ },++ "note": {++ "type": "string"++ }++ }++ },++ "routing": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "type",++ "name",++ "value"++ ],++ "properties": {++ "type": {++ "type": "string"++ },++ "name": {++ "type": "string"++ },++ "value": {++ "type": "string"++ },++ "note": {++ "type": "string"++ }++ }++ },++ "description": "A CNAME for a subdomain; A/AAAA for an apex domain when the operator configured them."++ },++ "tls": {++ "type": "string"++ }++ }++ }++ }++}nnew file mode 100644@@@ -0,0 +1,159 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/project.v1.json",++ "title": "SpaceProject",++ "description": "space.project@1: one OpenVibe.Space tenant project (server/http/serialize.js project): owned by a person (usr_…), production or sandbox, with the caller's role in it and any staff takedown. POST /api/v1/projects and GET /api/v1/projects/:id add the effective quota and the usage.",++ "type": "object",++ "required": [++ "id",++ "name",++ "environment",++ "network_project_id",++ "owner",++ "role",++ "created_at",++ "takedown"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "name": {++ "type": "string",++ "minLength": 1,++ "maxLength": 80++ },++ "environment": {++ "enum": [++ "production",++ "sandbox"++ ]++ },++ "network_project_id": {++ "type": [++ "string",++ "null"++ ],++ "description": "The OpenVibe.Network project once Network has projects."++ },++ "owner": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "role": {++ "enum": [++ "owner",++ "maintainer",++ "deployer",++ "staff",++ null++ ],++ "description": "The caller's role (staff: a Network admin who is not a member)."++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "takedown": {++ "type": [++ "object",++ "null"++ ],++ "required": [++ "scope",++ "reason",++ "since"++ ],++ "description": "A staff takedown covering it: serving stops while the content is kept for review.",++ "properties": {++ "scope": {++ "enum": [++ "project",++ "site"++ ]++ },++ "reason": {++ "type": "string"++ },++ "since": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ },++ "quota": {++ "type": "object",++ "required": [++ "storage_bytes",++ "deploys_per_day",++ "max_files",++ "max_file_bytes",++ "sites",++ "custom_domains"++ ],++ "description": "The effective quota: the project's override where set, else its environment's default.",++ "properties": {++ "storage_bytes": {++ "type": "integer",++ "minimum": 0++ },++ "deploys_per_day": {++ "type": "integer",++ "minimum": 0++ },++ "max_files": {++ "type": "integer",++ "minimum": 0++ },++ "max_file_bytes": {++ "type": "integer",++ "minimum": 0++ },++ "sites": {++ "type": "integer",++ "minimum": 0++ },++ "custom_domains": {++ "type": "integer",++ "minimum": 0++ }++ }++ },++ "usage": {++ "type": "object",++ "required": [++ "storage_bytes",++ "objects",++ "deploys_last_24h",++ "sites",++ "custom_domains"++ ],++ "properties": {++ "storage_bytes": {++ "type": "integer",++ "minimum": 0++ },++ "objects": {++ "type": "integer",++ "minimum": 0++ },++ "deploys_last_24h": {++ "type": "integer",++ "minimum": 0++ },++ "sites": {++ "type": "integer",++ "minimum": 0++ },++ "custom_domains": {++ "type": "integer",++ "minimum": 0++ }++ }++ }++ }++}nnew file mode 100644@@@ -0,0 +1,28 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/site-config-request.v1.json",++ "title": "SpaceSiteConfigRequest",++ "description": "space.site-config-request@1: the body of PUT /api/v1/sites/:id/config on OpenVibe.Space (space.site.config; the acting principal must be a maintainer or owner of the site's project). { headers?, redirects?, spa? } replaces the whole configuration: a field left out is reset to its default. A redirect's status defaults to 301. Refusals are 422 problem+json: site_config.reserved_header, site_config.header_name, site_config.header_value (longer than 1024 characters or a control character), site_config.too_many (over 50 headers or 200 redirects), site_config.redirect (not a local path, a duplicate from, or a status outside 301/302/307/308), site_config.headers and site_config.redirects (wrong type). GET and DELETE /api/v1/sites/:id/config take no body (DELETE resets to the defaults).",++ "type": "object",++ "additionalProperties": false,++ "properties": {++ "headers": {++ "type": "object",++ "additionalProperties": { "type": "string" }++ },++ "redirects": {++ "type": "array",++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": ["from", "to"],++ "properties": {++ "from": { "type": "string" },++ "to": { "type": "string" },++ "status": { "enum": [301, 302, 307, 308] }++ }++ }++ },++ "spa": { "type": "boolean" }++ }++}nnew file mode 100644@@@ -0,0 +1,12 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/site-config-result.v1.json",++ "title": "SpaceSiteConfigResult",++ "description": "space.site-config-result@1: every answer of space.site.config on OpenVibe.Space. GET, PUT and DELETE /api/v1/sites/:id/config → { config } (after a PUT, the configuration as stored; after a DELETE, the defaults).",++ "type": "object",++ "additionalProperties": false,++ "required": ["config"],++ "properties": {++ "config": { "$ref": "site-config.v1.json" }++ }++}nnew file mode 100644@@@ -0,0 +1,32 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/site-config.v1.json",++ "title": "SpaceSiteConfig",++ "description": "space.site-config@1: one Host site's serving configuration (plan T12 J3). headers are applied to every response of the site before the platform's own, which always win; the platform's security, routing, caching, scope and transport headers (Content-Security-Policy, Strict-Transport-Security, Set-Cookie, Cache-Control, Content-Type, Location, Service-Worker-Allowed, Alt-Svc and the rest of Host's reserved list) and every X-Forwarded-* header are refused. redirects are local paths only (no scheme, host, protocol-relative URL, backslash or control character, also after percent-decoding). spa serves /index.html for an extensionless path that has no file. A site with no configuration answers { headers: {}, redirects: [], spa: false }.",++ "type": "object",++ "additionalProperties": false,++ "required": ["headers", "redirects", "spa"],++ "properties": {++ "headers": {++ "type": "object",++ "maxProperties": 50,++ "propertyNames": { "pattern": "^[A-Za-z0-9][A-Za-z0-9-]{0,63}$" },++ "additionalProperties": { "type": "string", "maxLength": 1024 }++ },++ "redirects": {++ "type": "array",++ "maxItems": 200,++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": ["from", "to", "status"],++ "properties": {++ "from": { "type": "string", "pattern": "^/(?!/)", "maxLength": 1024 },++ "to": { "type": "string", "pattern": "^/(?!/)", "maxLength": 1024 },++ "status": { "enum": [301, 302, 307, 308] }++ }++ }++ },++ "spa": { "type": "boolean" }++ }++}nnew file mode 100644@@@ -0,0 +1,151 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/site-manage-request.v1.json",++ "title": "SpaceSiteManageRequest",++ "description": "space.site-manage-request@1: bodies of space.site.manage on OpenVibe.Space (judged by the acting principal's project role too; a sandbox token is refused on production projects). POST /api/v1/projects { name, environment?, network_project_id? } creates a project owned by the person named with X-OV-Subject (403 project.owner_must_be_user; 422 project.invalid_name over 80 characters, project.invalid_environment; 429 quota.projects; 409 project.network_project_taken). PUT /api/v1/projects/:id/quota { storage_bytes?, deploys_per_day?, max_files?, max_file_bytes?, sites?, custom_domains? } (a Network staff user: 403 auth.staff_only; fields left out keep their value). PUT /api/v1/projects/:id/members/:principal { role } (the owner: 422 member.invalid_role, 409 member.owner_fixed). POST /api/v1/projects/:id/sites { name } (a maintainer; lowercased and trimmed, one DNS label of 3–40 characters: 422 site.invalid_name, site.name_reserved; 409 site.name_taken, site.name_held; 429 quota.sites). POST /api/v1/projects/:id/takedown and /sites/:id/takedown { reason } (staff: stop serving, keep the content; 409 takedown.exists); DELETE on those { note? } lifts it. GET /api/v1/projects, /projects/:id, /projects/:id/quota, /projects/:id/sites and /sites/:id, and DELETE /api/v1/projects/:id, /projects/:id/members/:principal, /sites/:id and /deploys/:id take no body (deleting refuses taken-down content for non-staff: 409 site.taken_down; an active deploy: 409 deploy.active).",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "name"++ ],++ "properties": {++ "name": {++ "type": "string",++ "minLength": 1,++ "description": "1–80 characters once trimmed."++ },++ "environment": {++ "enum": [++ "production",++ "sandbox",++ null++ ],++ "description": "Default production; a sandbox credential creates sandbox projects only."++ },++ "network_project_id": {++ "anyOf": [++ {++ "type": "string",++ "pattern": "^([A-Za-z0-9_:-]{3,80})?$"++ },++ {++ "type": "null"++ }++ ]++ }++ }++ },++ {++ "type": "object",++ "properties": {++ "storage_bytes": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 0,++ "description": "A non-negative integer, or null for the environment default."++ },++ "deploys_per_day": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 0,++ "description": "A non-negative integer, or null for the environment default."++ },++ "max_files": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 0,++ "description": "A non-negative integer, or null for the environment default."++ },++ "max_file_bytes": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 0,++ "description": "A non-negative integer, or null for the environment default."++ },++ "sites": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 0,++ "description": "A non-negative integer, or null for the environment default."++ },++ "custom_domains": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 0,++ "description": "A non-negative integer, or null for the environment default."++ }++ }++ },++ {++ "type": "object",++ "required": [++ "role"++ ],++ "properties": {++ "role": {++ "enum": [++ "owner",++ "maintainer",++ "deployer"++ ],++ "description": "Only a person (usr_…) can be an owner."++ }++ }++ },++ {++ "type": "object",++ "required": [++ "name"++ ],++ "properties": {++ "name": {++ "type": "string",++ "pattern": "^\\s*[A-Za-z0-9](?:[A-Za-z0-9]|-(?=[A-Za-z0-9])){2,39}\\s*$",++ "description": "The site name (lowercased)."++ }++ }++ },++ {++ "type": "object",++ "required": [++ "reason"++ ],++ "properties": {++ "reason": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Members see it."++ }++ }++ },++ {++ "type": "object",++ "properties": {++ "note": {++ "type": [++ "string",++ "null"++ ],++ "description": "Cut to 500 characters."++ }++ }++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,274 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/site-manage-result.v1.json",++ "title": "SpaceSiteManageResult",++ "description": "space.site-manage-result@1: answers of space.site.manage on OpenVibe.Space. GET /api/v1/projects → { projects } (the caller's projects with their role). POST /api/v1/projects → 201 { project } (with quota and usage). GET /api/v1/projects/:id → { project, sites, members }. GET and PUT /api/v1/projects/:id/quota → { quota, usage }. PUT and DELETE /api/v1/projects/:id/members/:principal → { members }. GET /api/v1/projects/:id/sites → { sites }. POST /api/v1/projects/:id/sites → 201 { site }. GET /api/v1/sites/:id → { site, domains }. DELETE /api/v1/projects/:id and /sites/:id → { deleted: true } (serving stops at once; the objects are removed). DELETE /api/v1/deploys/:id → { deleted: true, objects_removed }. POST …/takedown → 201 { takedown }; DELETE …/takedown → { lifted: true }.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "projects"++ ],++ "properties": {++ "projects": {++ "type": "array",++ "items": {++ "$ref": "project.v1.json"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "project"++ ],++ "properties": {++ "project": {++ "$ref": "project.v1.json"++ },++ "sites": {++ "type": "array",++ "items": {++ "$ref": "site.v1.json"++ }++ },++ "members": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "principal",++ "role",++ "added_at"++ ],++ "properties": {++ "principal": {++ "type": "string",++ "description": "usr_…, app:app_… or svc:…"++ },++ "role": {++ "enum": [++ "owner",++ "maintainer",++ "deployer"++ ]++ },++ "added_at": {++ "type": "string",++ "format": "date-time"++ }++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "quota",++ "usage"++ ],++ "properties": {++ "quota": {++ "type": "object",++ "required": [++ "storage_bytes",++ "deploys_per_day",++ "max_files",++ "max_file_bytes",++ "sites",++ "custom_domains"++ ],++ "description": "The effective quota: the project's override where set, else its environment's default.",++ "properties": {++ "storage_bytes": {++ "type": "integer",++ "minimum": 0++ },++ "deploys_per_day": {++ "type": "integer",++ "minimum": 0++ },++ "max_files": {++ "type": "integer",++ "minimum": 0++ },++ "max_file_bytes": {++ "type": "integer",++ "minimum": 0++ },++ "sites": {++ "type": "integer",++ "minimum": 0++ },++ "custom_domains": {++ "type": "integer",++ "minimum": 0++ }++ }++ },++ "usage": {++ "type": "object",++ "required": [++ "storage_bytes",++ "objects",++ "deploys_last_24h",++ "sites",++ "custom_domains"++ ],++ "properties": {++ "storage_bytes": {++ "type": "integer",++ "minimum": 0++ },++ "objects": {++ "type": "integer",++ "minimum": 0++ },++ "deploys_last_24h": {++ "type": "integer",++ "minimum": 0++ },++ "sites": {++ "type": "integer",++ "minimum": 0++ },++ "custom_domains": {++ "type": "integer",++ "minimum": 0++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "members"++ ],++ "properties": {++ "members": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "principal",++ "role",++ "added_at"++ ],++ "properties": {++ "principal": {++ "type": "string",++ "description": "usr_…, app:app_… or svc:…"++ },++ "role": {++ "enum": [++ "owner",++ "maintainer",++ "deployer"++ ]++ },++ "added_at": {++ "type": "string",++ "format": "date-time"++ }++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "sites"++ ],++ "properties": {++ "sites": {++ "type": "array",++ "items": {++ "$ref": "site.v1.json"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "site"++ ],++ "properties": {++ "site": {++ "$ref": "site.v1.json"++ },++ "domains": {++ "type": "array",++ "items": {++ "$ref": "domain.v1.json"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "deleted"++ ],++ "properties": {++ "deleted": {++ "const": true++ },++ "objects_removed": {++ "type": "integer",++ "minimum": 0++ }++ }++ },++ {++ "type": "object",++ "required": [++ "takedown"++ ],++ "properties": {++ "takedown": {++ "type": "object",++ "required": [++ "scope",++ "reason",++ "since"++ ],++ "description": "The takedown now in force.",++ "properties": {++ "scope": {++ "enum": [++ "project",++ "site"++ ]++ },++ "reason": {++ "type": "string"++ },++ "since": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "lifted"++ ],++ "properties": {++ "lifted": {++ "const": true++ }++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,87 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/site.v1.json",++ "title": "SpaceSite",++ "description": "space.site@1: one static site on OpenVibe.Space (server/http/serialize.js site): its name (one DNS label), the default hostname and URL it is served at, the deploy it serves (one pointer; null until a deploy is activated) and any staff takedown.",++ "type": "object",++ "required": [++ "id",++ "project_id",++ "name",++ "hostname",++ "url",++ "active_deploy_id",++ "takedown",++ "created_at",++ "updated_at"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "name": {++ "type": "string",++ "pattern": "^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){2,39}$"++ },++ "hostname": {++ "type": "string"++ },++ "url": {++ "type": "string"++ },++ "active_deploy_id": {++ "anyOf": [++ {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ {++ "type": "null"++ }++ ]++ },++ "takedown": {++ "type": [++ "object",++ "null"++ ],++ "required": [++ "scope",++ "reason",++ "since"++ ],++ "description": "A staff takedown covering it: serving stops while the content is kept for review.",++ "properties": {++ "scope": {++ "enum": [++ "project",++ "site"++ ]++ },++ "reason": {++ "type": "string"++ },++ "since": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "updated_at": {++ "type": "string",++ "format": "date-time"++ }++ }++}nnew file mode 100644@@@ -0,0 +1,4 @@++{++ "deploy_id": 42,++ "expected_active": 7++}nnew file mode 100644@@@ -0,0 +1,3 @@++{++ "expected_active": "dpl_01JAD3D4E5F6G7H8J9K0M1N2P3"++}nnew file mode 100644@@@ -0,0 +1 @@++{}nnew file mode 100644@@@ -0,0 +1,4 @@++{++ "deploy_id": "dpl_01JAD3D4E5F6G7H8J9K0M1N2P3",++ "expected_active": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3"++}nnew file mode 100644@@@ -0,0 +1,13 @@++{++ "active_deploy_id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",++ "deploys": [],++ "activations": [++ {++ "deploy_id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",++ "previous_deploy_id": null,++ "kind": "deploy",++ "actor": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "at": "2026-09-24T09:40:12.000Z"++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,29 @@++{++ "active_deploy_id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",++ "deploys": [++ {++ "id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",++ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",++ "project_id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",++ "state": "ready",++ "active": true,++ "source": "archive",++ "file_count": 37,++ "total_bytes": 482113,++ "new_bytes": 20411,++ "manifest_sha256": "a3f1c9e0b4d2e6f8a1c3e5f7091b2d4f6a8c0e2f4a6b8d0f2e4c6a8b0d2f4e6a",++ "failure_code": null,++ "created_by": "app:app_01JAD7A8B9C0D1E2F3G4H5J6K7",++ "created_at": "2026-09-24T09:40:12.000Z"++ }++ ],++ "activations": [++ {++ "deploy_id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",++ "previous_deploy_id": "dpl_01JAD3D4E5F6G7H8J9K0M1N2P3",++ "kind": "activate",++ "actor": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "at": "2026-09-24T09:40:12.000Z"++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,11 @@++{++ "deploy_id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",++ "state": "ready",++ "log": [++ {++ "level": "info",++ "message": "ready; not active until you activate it",++ "at": "2026-09-24T09:40:12.000Z"++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,25 @@++{++ "deploy": {++ "id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",++ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",++ "project_id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",++ "state": "ready",++ "active": true,++ "source": "archive",++ "file_count": 37,++ "total_bytes": 482113,++ "new_bytes": 20411,++ "manifest_sha256": "a3f1c9e0b4d2e6f8a1c3e5f7091b2d4f6a8c0e2f4a6b8d0f2e4c6a8b0d2f4e6a",++ "failure_code": null,++ "created_by": "app:app_01JAD7A8B9C0D1E2F3G4H5J6K7",++ "created_at": "2026-09-24T09:40:12.000Z",++ "files": [++ {++ "path": "index.html",++ "sha256": "a3f1c9e0b4d2e6f8a1c3e5f7091b2d4f6a8c0e2f4a6b8d0f2e4c6a8b0d2f4e6a",++ "size": 5120,++ "content_type": "text/html; charset=utf-8"++ }++ ]++ }++}nnew file mode 100644@@@ -0,0 +1,5 @@++{++ "active_deploy_id": "dpl_01JAD3D4E5F6G7H8J9K0M1N2P3",++ "previous_deploy_id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",++ "changed": true++}nnew file mode 100644@@@ -0,0 +1,31 @@++{++ "deploy": {++ "id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",++ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",++ "project_id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",++ "state": "ready",++ "active": true,++ "source": "archive",++ "file_count": 37,++ "total_bytes": 482113,++ "new_bytes": 20411,++ "manifest_sha256": "a3f1c9e0b4d2e6f8a1c3e5f7091b2d4f6a8c0e2f4a6b8d0f2e4c6a8b0d2f4e6a",++ "failure_code": null,++ "created_by": "app:app_01JAD7A8B9C0D1E2F3G4H5J6K7",++ "created_at": "2026-09-24T09:40:12.000Z",++ "log": [++ {++ "level": "info",++ "message": "validated 37 files, 470.8 KiB (paths, file types, sizes)",++ "at": "2026-09-24T09:40:12.000Z"++ },++ {++ "level": "info",++ "message": "activated: the site now serves this deploy",++ "at": "2026-09-24T09:40:12.000Z"++ }++ ]++ },++ "activated": true,++ "url": "https://harbour-radio.openvibe.website"++}nnew file mode 100644@@@ -0,0 +1,7 @@++{++ "service": "live",++ "release": "d4e849f2",++ "commit": null,++ "origin": "https://openvibe.live",++ "deployed_at": "2026-09-26T08:00:05.000Z"++}nnew file mode 100644@@@ -0,0 +1,7 @@++{++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",++ "site": "my-site",++ "deploy_id": "dpl_01JAB2C3D4E5F6G7H8J9K0MNPS",++ "rollback": false++}nnew file mode 100644@@@ -0,0 +1,8 @@++{++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",++ "site": "my-site",++ "deploy_id": "dpl_01JAB2C3D4E5F6G7H8J9K0MNPT",++ "previous_deploy_id": "dpl_01JAB2C3D4E5F6G7H8J9K0MNPS",++ "rollback": true++}nnew file mode 100644@@@ -0,0 +1,8 @@++{++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",++ "site": "my-site",++ "deploy_id": "dpl_01JAB2C3D4E5F6G7H8J9K0MNPS",++ "previous_deploy_id": null,++ "rollback": false++}nnew file mode 100644@@@ -0,0 +1,9 @@++{++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "site_id": "site_01JAB2C3D4E5F6G7H8J9K0MNPR",++ "site": "alpha-site",++ "source": "files",++ "file_count": 1,++ "total_bytes": 1,++ "manifest_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"++}nnew file mode 100644@@@ -0,0 +1,9 @@++{++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",++ "site": "alpha-site",++ "source": "ftp",++ "file_count": 1,++ "total_bytes": 1,++ "manifest_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"++}nnew file mode 100644@@@ -0,0 +1,9 @@++{++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",++ "site": "alpha-site",++ "source": "git",++ "file_count": 3,++ "total_bytes": 901,++ "manifest_sha256": "0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"++}nnew file mode 100644@@@ -0,0 +1,9 @@++{++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",++ "site": "alpha-site",++ "source": "archive",++ "file_count": 12,++ "total_bytes": 48213,++ "manifest_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"++}nnew file mode 100644@@@ -0,0 +1,19 @@++{++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",++ "site": "alpha-site",++ "code": "deploy.invalid",++ "problems": [++ "file.bad_path",++ "file.bad_path",++ "file.bad_path",++ "file.bad_path",++ "file.bad_path",++ "file.bad_path",++ "file.bad_path",++ "file.bad_path",++ "file.bad_path",++ "file.bad_path",++ "file.bad_path"++ ]++}nnew file mode 100644@@@ -0,0 +1,10 @@++{++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",++ "site": "alpha-site",++ "code": "deploy.too_large",++ "problems": [++ "file.too_large"++ ],++ "content": "<html>"++}nnew file mode 100644@@@ -0,0 +1,7 @@++{++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",++ "site": "alpha-site",++ "code": "storage.space_full",++ "problems": []++}nnew file mode 100644@@@ -0,0 +1,9 @@++{++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",++ "site": "alpha-site",++ "code": "deploy.too_large",++ "problems": [++ "file.too_large"++ ]++}nnew file mode 100644@@@ -0,0 +1,15 @@++{++ "id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",++ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",++ "project_id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",++ "state": "ready",++ "active": true,++ "source": "tarball",++ "file_count": 37,++ "total_bytes": 482113,++ "new_bytes": 20411,++ "manifest_sha256": "a3f1c9e0b4d2e6f8a1c3e5f7091b2d4f6a8c0e2f4a6b8d0f2e4c6a8b0d2f4e6a",++ "failure_code": null,++ "created_by": "app:app_01JAD7A8B9C0D1E2F3G4H5J6K7",++ "created_at": "2026-09-24T09:40:12.000Z"++}nnew file mode 100644@@@ -0,0 +1,22 @@++{++ "id": "dpl_01JAD3D4E5F6G7H8J9K0M1N2P3",++ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",++ "project_id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",++ "state": "failed",++ "active": false,++ "source": "files",++ "file_count": 0,++ "total_bytes": 0,++ "new_bytes": 0,++ "manifest_sha256": null,++ "failure_code": "deploy.server_code",++ "created_by": "app:app_01JAD7A8B9C0D1E2F3G4H5J6K7",++ "created_at": "2026-09-24T09:40:12.000Z",++ "log": [++ {++ "level": "error",++ "message": "deploy.server_code: api/index.php is server-side code; Host serves static files only",++ "at": "2026-09-24T08:15:00.000Z"++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,21 @@++{++ "id": "dpl_01JAG7H8J9K0M1N2P3Q4R5S6T7",++ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",++ "project_id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",++ "state": "ready",++ "active": true,++ "source": "git",++ "git": {++ "provider": "github",++ "repo_url": "https://github.com/OpenVibers/example-site",++ "ref": "refs/heads/main",++ "commit_sha": "a3f1c9e0b4d2e6f8a1c3e5f7091b2d4f6a8c0e2f"++ },++ "file_count": 37,++ "total_bytes": 482113,++ "new_bytes": 20411,++ "manifest_sha256": "a3f1c9e0b4d2e6f8a1c3e5f7091b2d4f6a8c0e2f4a6b8d0f2e4c6a8b0d2f4e6a",++ "failure_code": null,++ "created_by": "app:app_01JAD7A8B9C0D1E2F3G4H5J6K7",++ "created_at": "2026-09-24T09:40:12.000Z"++}nnew file mode 100644@@@ -0,0 +1,15 @@++{++ "id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",++ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",++ "project_id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",++ "state": "ready",++ "active": true,++ "source": "archive",++ "file_count": 37,++ "total_bytes": 482113,++ "new_bytes": 20411,++ "manifest_sha256": "a3f1c9e0b4d2e6f8a1c3e5f7091b2d4f6a8c0e2f4a6b8d0f2e4c6a8b0d2f4e6a",++ "failure_code": null,++ "created_by": "app:app_01JAD7A8B9C0D1E2F3G4H5J6K7",++ "created_at": "2026-09-24T09:40:12.000Z"++}nnew file mode 100644@@@ -0,0 +1,3 @@++{++ "domain": "www.harbourradio.example"++}nnew file mode 100644@@@ -0,0 +1,3 @@++{++ "hostname": "www.harbourradio.example"++}nnew file mode 100644@@@ -0,0 +1 @@++{}nnew file mode 100644@@@ -0,0 +1,30 @@++{++ "domain": {++ "id": "dom_01JAD6D6E7F8G9H0J1K2M3N4P5",++ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",++ "hostname": "www.harbourradio.example",++ "kind": "apex",++ "status": "pending",++ "served": false,++ "verified_at": null,++ "last_checked_at": "2026-09-24T09:40:12.000Z",++ "last_error": "no TXT record at _openvibe-space.www.harbourradio.example yet",++ "instructions": {++ "verification": {++ "type": "TXT",++ "name": "_openvibe-space.www.harbourradio.example",++ "value": "openvibe-space-verification=5f0c2a9e1b7d4c3a8e6f0b2d4c6a8e0f1a3c5e7b",++ "note": "keep this record: Host re-checks it daily"++ },++ "routing": [++ {++ "type": "CNAME",++ "name": "www.harbourradio.example",++ "value": "harbour-radio.openvibe.website",++ "note": "for a subdomain such as www.example.org"++ }++ ],++ "tls": "after verification an operator issues the certificate (Let's Encrypt); HTTPS starts working once it is installed"++ }++ }++}nnew file mode 100644@@@ -0,0 +1,30 @@++{++ "domain": {++ "id": "dom_01JAD6D6E7F8G9H0J1K2M3N4P5",++ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",++ "hostname": "www.harbourradio.example",++ "kind": "custom",++ "status": "pending",++ "served": false,++ "verified_at": null,++ "last_checked_at": "2026-09-24T09:40:12.000Z",++ "last_error": "no TXT record at _openvibe-space.www.harbourradio.example yet",++ "instructions": {++ "verification": {++ "type": "TXT",++ "name": "_openvibe-space.www.harbourradio.example",++ "value": "openvibe-space-verification=5f0c2a9e1b7d4c3a8e6f0b2d4c6a8e0f1a3c5e7b",++ "note": "keep this record: Host re-checks it daily"++ },++ "routing": [++ {++ "type": "CNAME",++ "name": "www.harbourradio.example",++ "value": "harbour-radio.openvibe.website",++ "note": "for a subdomain such as www.example.org"++ }++ ],++ "tls": "after verification an operator issues the certificate (Let's Encrypt); HTTPS starts working once it is installed"++ }++ }++}nnew file mode 100644@@@ -0,0 +1,3 @@++{++ "deleted": true++}nnew file mode 100644@@@ -0,0 +1,43 @@++{++ "domains": [++ {++ "id": "dom_01JAD5D6E7F8G9H0J1K2M3N4P5",++ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",++ "hostname": "harbour-radio.openvibe.website",++ "kind": "default",++ "status": "verified",++ "served": true,++ "verified_at": "2026-09-24T08:15:00.000Z",++ "last_checked_at": null,++ "last_error": null++ },++ {++ "id": "dom_01JAD6D6E7F8G9H0J1K2M3N4P5",++ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",++ "hostname": "www.harbourradio.example",++ "kind": "custom",++ "status": "pending",++ "served": false,++ "verified_at": null,++ "last_checked_at": "2026-09-24T09:40:12.000Z",++ "last_error": "no TXT record at _openvibe-space.www.harbourradio.example yet",++ "instructions": {++ "verification": {++ "type": "TXT",++ "name": "_openvibe-space.www.harbourradio.example",++ "value": "openvibe-space-verification=5f0c2a9e1b7d4c3a8e6f0b2d4c6a8e0f1a3c5e7b",++ "note": "keep this record: Host re-checks it daily"++ },++ "routing": [++ {++ "type": "CNAME",++ "name": "www.harbourradio.example",++ "value": "harbour-radio.openvibe.website",++ "note": "for a subdomain such as www.example.org"++ }++ ],++ "tls": "after verification an operator issues the certificate (Let's Encrypt); HTTPS starts working once it is installed"++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,6 @@++{++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",++ "site": "Alpha Site",++ "hostname": "www.example.org"++}nnew file mode 100644@@@ -0,0 +1,5 @@++{++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",++ "site": "alpha-site"++}nnew file mode 100644@@@ -0,0 +1,6 @@++{++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",++ "site": "alpha-site",++ "hostname": "www.example.org"++}nnew file mode 100644@@@ -0,0 +1,11 @@++{++ "id": "dom_01JAD5D6E7F8G9H0J1K2M3N4P5",++ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",++ "hostname": "harbour-radio.openvibe.website",++ "kind": "default",++ "status": "active",++ "served": true,++ "verified_at": "2026-09-24T08:15:00.000Z",++ "last_checked_at": null,++ "last_error": null++}nnew file mode 100644@@@ -0,0 +1,28 @@++{++ "id": "dom_01JAD6D6E7F8G9H0J1K2M3N4P5",++ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",++ "hostname": "www.harbourradio.example",++ "kind": "custom",++ "status": "pending",++ "served": false,++ "verified_at": null,++ "last_checked_at": "2026-09-24T09:40:12.000Z",++ "last_error": "no TXT record at _openvibe-space.www.harbourradio.example yet",++ "instructions": {++ "verification": {++ "type": "TXT",++ "name": "_openvibe-space.www.harbourradio.example",++ "value": "openvibe-space-verification=5f0c2a9e1b7d4c3a8e6f0b2d4c6a8e0f1a3c5e7b",++ "note": "keep this record: Host re-checks it daily"++ },++ "routing": [++ {++ "type": "CNAME",++ "name": "www.harbourradio.example",++ "value": "harbour-radio.openvibe.website",++ "note": "for a subdomain such as www.example.org"++ }++ ],++ "tls": "after verification an operator issues the certificate (Let's Encrypt); HTTPS starts working once it is installed"++ }++}nnew file mode 100644@@@ -0,0 +1,11 @@++{++ "id": "dom_01JAD5D6E7F8G9H0J1K2M3N4P5",++ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",++ "hostname": "harbour-radio.openvibe.website",++ "kind": "default",++ "status": "verified",++ "served": true,++ "verified_at": "2026-09-24T08:15:00.000Z",++ "last_checked_at": null,++ "last_error": null++}nnew file mode 100644@@@ -0,0 +1,10 @@++{++ "id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",++ "name": "Harbour Radio",++ "environment": "staging",++ "network_project_id": null,++ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "role": "owner",++ "created_at": "2026-09-24T08:15:00.000Z",++ "takedown": null++}nnew file mode 100644@@@ -0,0 +1,10 @@++{++ "id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",++ "name": "Harbour Radio",++ "environment": "production",++ "network_project_id": null,++ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "role": "owner",++ "created_at": "2026-09-24T08:15:00.000Z",++ "takedown": null++}nnew file mode 100644@@@ -0,0 +1,25 @@++{++ "id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",++ "name": "Harbour Radio",++ "environment": "production",++ "network_project_id": null,++ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "role": "owner",++ "created_at": "2026-09-24T08:15:00.000Z",++ "takedown": null,++ "quota": {++ "storage_bytes": 1073741824,++ "deploys_per_day": 50,++ "max_files": 5000,++ "max_file_bytes": 26214400,++ "sites": 5,++ "custom_domains": 5++ },++ "usage": {++ "storage_bytes": 482113,++ "objects": 37,++ "deploys_last_24h": 3,++ "sites": 1,++ "custom_domains": 1++ }++}nnew file mode 100644@@@ -0,0 +1 @@++{ "redirects": [{ "from": "/a", "to": "/b", "status": 303 }] }nnew file mode 100644@@@ -0,0 +1 @@++{ "headers": { "X-Frame-Options": "SAMEORIGIN" }, "redirects": [{ "from": "/docs", "to": "/docs/" }], "spa": true }nnew file mode 100644@@@ -0,0 +1 @@++{}nnew file mode 100644@@@ -0,0 +1 @@++{ "config": { "headers": {}, "redirects": [] } }nnew file mode 100644@@@ -0,0 +1 @@++{ "config": { "headers": { "X-Frame-Options": "DENY" }, "redirects": [], "spa": false } }nnew file mode 100644@@@ -0,0 +1 @@++{ "headers": {}, "redirects": [{ "from": "/a", "to": "//evil.example/", "status": 301 }], "spa": false }nnew file mode 100644@@@ -0,0 +1 @@++{ "headers": { "X-Frame-Options": "DENY", "Access-Control-Allow-Origin": "*" }, "redirects": [{ "from": "/old", "to": "/new", "status": 301 }], "spa": true }nnew file mode 100644@@@ -0,0 +1 @@++{ "headers": {}, "redirects": [], "spa": false }nnew file mode 100644@@@ -0,0 +1,7 @@++{++ "name": 42,++ "role": "admin",++ "reason": "",++ "storage_bytes": -1,++ "note": 5++}nnew file mode 100644@@@ -0,0 +1,3 @@++{++ "note": "Page removed by the owner"++}nnew file mode 100644@@@ -0,0 +1,3 @@++{++ "role": "deployer"++}nnew file mode 100644@@@ -0,0 +1,4 @@++{++ "name": "Harbour Radio",++ "environment": "production"++}nnew file mode 100644@@@ -0,0 +1,4 @@++{++ "storage_bytes": 2147483648,++ "custom_domains": null++}nnew file mode 100644@@@ -0,0 +1 @@++{}nnew file mode 100644@@@ -0,0 +1,3 @@++{++ "name": "harbour-radio"++}nnew file mode 100644@@@ -0,0 +1,3 @@++{++ "reason": "Phishing page reported by two users"++}nnew file mode 100644@@@ -0,0 +1,3 @@++{++ "deleted": false++}nnew file mode 100644@@@ -0,0 +1,27 @@++{++ "project": {++ "id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",++ "name": "Harbour Radio",++ "environment": "production",++ "network_project_id": null,++ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "role": "owner",++ "created_at": "2026-09-24T08:15:00.000Z",++ "takedown": null,++ "quota": {++ "storage_bytes": 1073741824,++ "deploys_per_day": 50,++ "max_files": 5000,++ "max_file_bytes": 26214400,++ "sites": 5,++ "custom_domains": 5++ },++ "usage": {++ "storage_bytes": 482113,++ "objects": 37,++ "deploys_last_24h": 3,++ "sites": 1,++ "custom_domains": 1++ }++ }++}nnew file mode 100644@@@ -0,0 +1,3 @@++{++ "deleted": true++}nnew file mode 100644@@@ -0,0 +1,4 @@++{++ "deleted": true,++ "objects_removed": 4++}nnew file mode 100644@@@ -0,0 +1,3 @@++{++ "lifted": true++}nnew file mode 100644@@@ -0,0 +1,9 @@++{++ "members": [++ {++ "principal": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "role": "owner",++ "added_at": "2026-09-24T08:15:00.000Z"++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,52 @@++{++ "project": {++ "id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",++ "name": "Harbour Radio",++ "environment": "production",++ "network_project_id": null,++ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "role": "owner",++ "created_at": "2026-09-24T08:15:00.000Z",++ "takedown": null,++ "quota": {++ "storage_bytes": 1073741824,++ "deploys_per_day": 50,++ "max_files": 5000,++ "max_file_bytes": 26214400,++ "sites": 5,++ "custom_domains": 5++ },++ "usage": {++ "storage_bytes": 482113,++ "objects": 37,++ "deploys_last_24h": 3,++ "sites": 1,++ "custom_domains": 1++ }++ },++ "sites": [++ {++ "id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",++ "project_id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",++ "name": "harbour-radio",++ "hostname": "harbour-radio.openvibe.website",++ "url": "https://harbour-radio.openvibe.website",++ "active_deploy_id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",++ "takedown": null,++ "created_at": "2026-09-24T08:15:00.000Z",++ "updated_at": "2026-09-24T09:40:12.000Z"++ }++ ],++ "members": [++ {++ "principal": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "role": "owner",++ "added_at": "2026-09-24T08:15:00.000Z"++ },++ {++ "principal": "app:app_01JAD7A8B9C0D1E2F3G4H5J6K7",++ "role": "deployer",++ "added_at": "2026-09-24T08:15:00.000Z"++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,14 @@++{++ "projects": [++ {++ "id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",++ "name": "Harbour Radio",++ "environment": "production",++ "network_project_id": null,++ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "role": "owner",++ "created_at": "2026-09-24T08:15:00.000Z",++ "takedown": null++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,17 @@++{++ "quota": {++ "storage_bytes": 1073741824,++ "deploys_per_day": 50,++ "max_files": 5000,++ "max_file_bytes": 26214400,++ "sites": 5,++ "custom_domains": 5++ },++ "usage": {++ "storage_bytes": 482113,++ "objects": 37,++ "deploys_last_24h": 3,++ "sites": 1,++ "custom_domains": 1++ }++}nnew file mode 100644@@@ -0,0 +1,54 @@++{++ "site": {++ "id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",++ "project_id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",++ "name": "harbour-radio",++ "hostname": "harbour-radio.openvibe.website",++ "url": "https://harbour-radio.openvibe.website",++ "active_deploy_id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",++ "takedown": null,++ "created_at": "2026-09-24T08:15:00.000Z",++ "updated_at": "2026-09-24T09:40:12.000Z"++ },++ "domains": [++ {++ "id": "dom_01JAD5D6E7F8G9H0J1K2M3N4P5",++ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",++ "hostname": "harbour-radio.openvibe.website",++ "kind": "default",++ "status": "verified",++ "served": true,++ "verified_at": "2026-09-24T08:15:00.000Z",++ "last_checked_at": null,++ "last_error": null++ },++ {++ "id": "dom_01JAD6D6E7F8G9H0J1K2M3N4P5",++ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",++ "hostname": "www.harbourradio.example",++ "kind": "custom",++ "status": "pending",++ "served": false,++ "verified_at": null,++ "last_checked_at": "2026-09-24T09:40:12.000Z",++ "last_error": "no TXT record at _openvibe-space.www.harbourradio.example yet",++ "instructions": {++ "verification": {++ "type": "TXT",++ "name": "_openvibe-space.www.harbourradio.example",++ "value": "openvibe-space-verification=5f0c2a9e1b7d4c3a8e6f0b2d4c6a8e0f1a3c5e7b",++ "note": "keep this record: Host re-checks it daily"++ },++ "routing": [++ {++ "type": "CNAME",++ "name": "www.harbourradio.example",++ "value": "harbour-radio.openvibe.website",++ "note": "for a subdomain such as www.example.org"++ }++ ],++ "tls": "after verification an operator issues the certificate (Let's Encrypt); HTTPS starts working once it is installed"++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,15 @@++{++ "sites": [++ {++ "id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",++ "project_id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",++ "name": "harbour-radio",++ "hostname": "harbour-radio.openvibe.website",++ "url": "https://harbour-radio.openvibe.website",++ "active_deploy_id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",++ "takedown": null,++ "created_at": "2026-09-24T08:15:00.000Z",++ "updated_at": "2026-09-24T09:40:12.000Z"++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,7 @@++{++ "takedown": {++ "scope": "site",++ "reason": "Phishing page reported by two users",++ "since": "2026-09-24T09:40:12.000Z"++ }++}nnew file mode 100644@@@ -0,0 +1,11 @@++{++ "id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",++ "project_id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",++ "name": "Harbour-Radio",++ "hostname": "harbour-radio.openvibe.website",++ "url": "https://harbour-radio.openvibe.website",++ "active_deploy_id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",++ "takedown": null,++ "created_at": "2026-09-24T08:15:00.000Z",++ "updated_at": "2026-09-24T09:40:12.000Z"++}nnew file mode 100644@@@ -0,0 +1,11 @@++{++ "id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",++ "project_id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",++ "name": "harbour-radio",++ "hostname": "harbour-radio.openvibe.website",++ "url": "https://harbour-radio.openvibe.website",++ "active_deploy_id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",++ "takedown": null,++ "created_at": "2026-09-24T08:15:00.000Z",++ "updated_at": "2026-09-24T09:40:12.000Z"++}@@@ -77001,6 +77001,1648 @@}]}++ },++ "space.deploy-create-request": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/deploy-create-request.v1.json",++ "title": "SpaceDeployCreateRequest",++ "description": "space.deploy-create-request@1: bodies of space.deploy.create on OpenVibe.Space (a deployer or above of the project; an app principal added as deployer can deploy from CI). POST /api/v1/sites/:id/deploys uploads a deploy: the raw body is a tar or tar.gz archive (Content-Type application/gzip, application/x-gzip, application/x-tar or application/octet-stream), or multipart/form-data with archive=<one archive> or files=<file>… (each part's filename is its path); optional fields activate=1, root=<subdirectory to deploy> and strip=folder (removes a shared first segment) also work as query parameters. It is validated (path traversal, links, special and hidden files, server-side code, file types, sizes and quotas) and never executed; a refusal is a problem whose extra carries the failed deploy_id and its log (413 upload.too_large, quota.storage; 422 validation codes; 429 quota.deploys_per_day; 403 site.taken_down; 503 upload.busy; 507 storage.host_full). POST /api/v1/deploys/:id/activate { expected_active? } and POST /api/v1/sites/:id/rollback { deploy_id?, expected_active? } switch the site's pointer in one step (rollback without deploy_id goes to the deploy active before; 409 deploy.not_ready, site.active_changed when expected_active is stale, deploy.no_previous). GET /api/v1/sites/:id/deploys, /deploys/:id and /deploys/:id/log take no body.",++ "anyOf": [++ {++ "type": "string",++ "contentEncoding": "binary",++ "description": "The upload itself: the tar or tar.gz bytes, or the multipart/form-data body (as common.binary@1)."++ },++ {++ "type": "object",++ "properties": {++ "deploy_id": {++ "anyOf": [++ {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ {++ "type": "null"++ }++ ]++ },++ "expected_active": {++ "anyOf": [++ {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ {++ "type": "null"++ }++ ],++ "description": "The deploy you saw active (null: none); refused with 409 site.active_changed when it moved."++ }++ }++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++ }++ },++ "space.deploy-create-result": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/deploy-create-result.v1.json",++ "title": "SpaceDeployCreateResult",++ "description": "space.deploy-create-result@1: answers of space.deploy.create on OpenVibe.Space. GET /api/v1/sites/:id/deploys?limit= → { active_deploy_id, deploys, activations } (newest first; the last 20 pointer switches). POST /api/v1/sites/:id/deploys → 201 { deploy, activated, url } (the ready deploy with its validation log; activated when activate=1 switched the site to it). GET /api/v1/deploys/:id → { deploy } with its files. GET /api/v1/deploys/:id/log → { deploy_id, state, log }. POST /api/v1/deploys/:id/activate and /sites/:id/rollback → { active_deploy_id, previous_deploy_id, changed } (changed false when it was already active). Emits space.deploy.created|activated|failed.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "active_deploy_id",++ "deploys",++ "activations"++ ],++ "properties": {++ "active_deploy_id": {++ "anyOf": [++ {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ {++ "type": "null"++ }++ ]++ },++ "deploys": {++ "type": "array",++ "items": {++ "$ref": "deploy.v1.json"++ }++ },++ "activations": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "deploy_id",++ "previous_deploy_id",++ "kind",++ "actor",++ "at"++ ],++ "properties": {++ "deploy_id": {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "previous_deploy_id": {++ "anyOf": [++ {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ {++ "type": "null"++ }++ ]++ },++ "kind": {++ "enum": [++ "activate",++ "rollback"++ ]++ },++ "actor": {++ "type": "string"++ },++ "at": {++ "type": "string",++ "format": "date-time"++ }++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "deploy"++ ],++ "properties": {++ "deploy": {++ "$ref": "deploy.v1.json"++ },++ "activated": {++ "type": "boolean"++ },++ "url": {++ "type": "string"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "deploy_id",++ "state",++ "log"++ ],++ "properties": {++ "deploy_id": {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "state": {++ "enum": [++ "ready",++ "failed",++ "deleted"++ ]++ },++ "log": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "level",++ "message",++ "at"++ ],++ "properties": {++ "level": {++ "enum": [++ "info",++ "warn",++ "error"++ ]++ },++ "message": {++ "type": "string"++ },++ "at": {++ "type": "string",++ "format": "date-time"++ }++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "active_deploy_id",++ "previous_deploy_id",++ "changed"++ ],++ "properties": {++ "active_deploy_id": {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "previous_deploy_id": {++ "anyOf": [++ {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ {++ "type": "null"++ }++ ]++ },++ "changed": {++ "type": "boolean"++ }++ }++ }++ ]++ }++ },++ "space.deploy": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/deploy.v1.json",++ "title": "SpaceDeploy",++ "description": "space.deploy@1: one immutable static deploy on OpenVibe.Space (server/http/serialize.js deploy): ready (validated and stored, never executed) or failed (refused, with its failure code and log), whether the site serves it, its size and manifest hash. GET /api/v1/deploys/:id adds the files; the upload answer adds the log.",++ "type": "object",++ "required": [++ "id",++ "site_id",++ "project_id",++ "state",++ "active",++ "source",++ "file_count",++ "total_bytes",++ "new_bytes",++ "manifest_sha256",++ "failure_code",++ "created_by",++ "created_at"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "site_id": {++ "type": "string",++ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "state": {++ "enum": [++ "ready",++ "failed",++ "deleted"++ ]++ },++ "active": {++ "type": "boolean"++ },++ "source": {++ "enum": [++ "archive",++ "files",++ "preview",++ "git"++ ]++ },++ "git": {++ "type": "object",++ "required": [++ "provider",++ "repo_url",++ "ref",++ "commit_sha"++ ],++ "properties": {++ "provider": {++ "type": "string"++ },++ "repo_url": {++ "type": "string"++ },++ "ref": {++ "type": "string"++ },++ "commit_sha": {++ "type": "string",++ "pattern": "^[0-9a-f]{40}$"++ }++ },++ "description": "Present on source git: the commit this deploy was built from (public provenance, never a credential)."++ },++ "file_count": {++ "type": "integer",++ "minimum": 0++ },++ "total_bytes": {++ "type": "integer",++ "minimum": 0++ },++ "new_bytes": {++ "type": "integer",++ "minimum": 0,++ "description": "Bytes this deploy added to the project's store."++ },++ "manifest_sha256": {++ "type": [++ "string",++ "null"++ ],++ "pattern": "^[0-9a-f]{64}$"++ },++ "failure_code": {++ "type": [++ "string",++ "null"++ ]++ },++ "created_by": {++ "type": "string"++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "files": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "path",++ "sha256",++ "size",++ "content_type"++ ],++ "properties": {++ "path": {++ "type": "string"++ },++ "sha256": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$"++ },++ "size": {++ "type": "integer",++ "minimum": 0++ },++ "content_type": {++ "type": "string"++ }++ }++ }++ },++ "log": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "level",++ "message",++ "at"++ ],++ "properties": {++ "level": {++ "enum": [++ "info",++ "warn",++ "error"++ ]++ },++ "message": {++ "type": "string"++ },++ "at": {++ "type": "string",++ "format": "date-time"++ }++ }++ }++ }++ }++ }++ },++ "space.domain-manage-request": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/domain-manage-request.v1.json",++ "title": "SpaceDomainManageRequest",++ "description": "space.domain-manage-request@1: bodies of space.domain.manage on OpenVibe.Space (a maintainer or above of the project). POST /api/v1/sites/:id/domains { hostname } adds a custom domain, pending until its TXT record is verified: a fully qualified host name, lowercased (422 domain.invalid_hostname; 422 domain.reserved for any OpenVibe domain; 403 domain.sandbox in sandbox projects; 429 quota.custom_domains; 409 domain.exists, domain.taken). GET /api/v1/sites/:id/domains, POST /api/v1/domains/:id/verify (checks the TXT record now) and DELETE /api/v1/domains/:id (409 domain.default: the default domain goes only with the site) take no body.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "hostname"++ ],++ "properties": {++ "hostname": {++ "type": "string",++ "minLength": 1,++ "maxLength": 254,++ "description": "e.g. www.example.org (a trailing dot is dropped)."++ }++ }++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++ }++ },++ "space.domain-manage-result": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/domain-manage-result.v1.json",++ "title": "SpaceDomainManageResult",++ "description": "space.domain-manage-result@1: answers of space.domain.manage on OpenVibe.Space. GET /api/v1/sites/:id/domains → { domains } (the default domain first). POST /api/v1/sites/:id/domains → 201 { domain } with the DNS records to publish. POST /api/v1/domains/:id/verify → { domain } after the check (status verified and served once the TXT record matches, else last_error says what was found; emits space.domain.verified). DELETE /api/v1/domains/:id → { deleted: true }.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "domains"++ ],++ "properties": {++ "domains": {++ "type": "array",++ "items": {++ "$ref": "domain.v1.json"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "domain"++ ],++ "properties": {++ "domain": {++ "$ref": "domain.v1.json"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "deleted"++ ],++ "properties": {++ "deleted": {++ "const": true++ }++ }++ }++ ]++ }++ },++ "space.domain": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/domain.v1.json",++ "title": "SpaceDomain",++ "description": "space.domain@1: one domain of a static site on OpenVibe.Space (server/http/serialize.js domain): the default <site>.openvibe.website, or a custom domain that is served only once its DNS TXT record is verified (re-checked daily; it lapses when the record is gone). A custom domain carries the records its owner must publish (the TXT verification value is public by design and shown only to members); TLS certificates are issued by an operator.",++ "type": "object",++ "required": [++ "id",++ "site_id",++ "hostname",++ "kind",++ "status",++ "served",++ "verified_at",++ "last_checked_at",++ "last_error"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^dom_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "site_id": {++ "type": "string",++ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "hostname": {++ "type": "string"++ },++ "kind": {++ "enum": [++ "default",++ "custom"++ ]++ },++ "status": {++ "enum": [++ "pending",++ "verified",++ "failed",++ "lapsed"++ ]++ },++ "served": {++ "type": "boolean"++ },++ "verified_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "last_checked_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "last_error": {++ "type": [++ "string",++ "null"++ ]++ },++ "instructions": {++ "type": "object",++ "required": [++ "verification",++ "routing",++ "tls"++ ],++ "properties": {++ "verification": {++ "type": "object",++ "required": [++ "type",++ "name",++ "value"++ ],++ "properties": {++ "type": {++ "const": "TXT"++ },++ "name": {++ "type": "string",++ "description": "_openvibe-space.<hostname>"++ },++ "value": {++ "type": "string",++ "description": "openvibe-space-verification=<token>"++ },++ "note": {++ "type": "string"++ }++ }++ },++ "routing": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "type",++ "name",++ "value"++ ],++ "properties": {++ "type": {++ "type": "string"++ },++ "name": {++ "type": "string"++ },++ "value": {++ "type": "string"++ },++ "note": {++ "type": "string"++ }++ }++ },++ "description": "A CNAME for a subdomain; A/AAAA for an apex domain when the operator configured them."++ },++ "tls": {++ "type": "string"++ }++ }++ }++ }++ }++ },++ "space.project": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/project.v1.json",++ "title": "SpaceProject",++ "description": "space.project@1: one OpenVibe.Space tenant project (server/http/serialize.js project): owned by a person (usr_…), production or sandbox, with the caller's role in it and any staff takedown. POST /api/v1/projects and GET /api/v1/projects/:id add the effective quota and the usage.",++ "type": "object",++ "required": [++ "id",++ "name",++ "environment",++ "network_project_id",++ "owner",++ "role",++ "created_at",++ "takedown"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "name": {++ "type": "string",++ "minLength": 1,++ "maxLength": 80++ },++ "environment": {++ "enum": [++ "production",++ "sandbox"++ ]++ },++ "network_project_id": {++ "type": [++ "string",++ "null"++ ],++ "description": "The OpenVibe.Network project once Network has projects."++ },++ "owner": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "role": {++ "enum": [++ "owner",++ "maintainer",++ "deployer",++ "staff",++ null++ ],++ "description": "The caller's role (staff: a Network admin who is not a member)."++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "takedown": {++ "type": [++ "object",++ "null"++ ],++ "required": [++ "scope",++ "reason",++ "since"++ ],++ "description": "A staff takedown covering it: serving stops while the content is kept for review.",++ "properties": {++ "scope": {++ "enum": [++ "project",++ "site"++ ]++ },++ "reason": {++ "type": "string"++ },++ "since": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ },++ "quota": {++ "type": "object",++ "required": [++ "storage_bytes",++ "deploys_per_day",++ "max_files",++ "max_file_bytes",++ "sites",++ "custom_domains"++ ],++ "description": "The effective quota: the project's override where set, else its environment's default.",++ "properties": {++ "storage_bytes": {++ "type": "integer",++ "minimum": 0++ },++ "deploys_per_day": {++ "type": "integer",++ "minimum": 0++ },++ "max_files": {++ "type": "integer",++ "minimum": 0++ },++ "max_file_bytes": {++ "type": "integer",++ "minimum": 0++ },++ "sites": {++ "type": "integer",++ "minimum": 0++ },++ "custom_domains": {++ "type": "integer",++ "minimum": 0++ }++ }++ },++ "usage": {++ "type": "object",++ "required": [++ "storage_bytes",++ "objects",++ "deploys_last_24h",++ "sites",++ "custom_domains"++ ],++ "properties": {++ "storage_bytes": {++ "type": "integer",++ "minimum": 0++ },++ "objects": {++ "type": "integer",++ "minimum": 0++ },++ "deploys_last_24h": {++ "type": "integer",++ "minimum": 0++ },++ "sites": {++ "type": "integer",++ "minimum": 0++ },++ "custom_domains": {++ "type": "integer",++ "minimum": 0++ }++ }++ }++ }++ }++ },++ "space.site-manage-request": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/site-manage-request.v1.json",++ "title": "SpaceSiteManageRequest",++ "description": "space.site-manage-request@1: bodies of space.site.manage on OpenVibe.Space (judged by the acting principal's project role too; a sandbox token is refused on production projects). POST /api/v1/projects { name, environment?, network_project_id? } creates a project owned by the person named with X-OV-Subject (403 project.owner_must_be_user; 422 project.invalid_name over 80 characters, project.invalid_environment; 429 quota.projects; 409 project.network_project_taken). PUT /api/v1/projects/:id/quota { storage_bytes?, deploys_per_day?, max_files?, max_file_bytes?, sites?, custom_domains? } (a Network staff user: 403 auth.staff_only; fields left out keep their value). PUT /api/v1/projects/:id/members/:principal { role } (the owner: 422 member.invalid_role, 409 member.owner_fixed). POST /api/v1/projects/:id/sites { name } (a maintainer; lowercased and trimmed, one DNS label of 3–40 characters: 422 site.invalid_name, site.name_reserved; 409 site.name_taken, site.name_held; 429 quota.sites). POST /api/v1/projects/:id/takedown and /sites/:id/takedown { reason } (staff: stop serving, keep the content; 409 takedown.exists); DELETE on those { note? } lifts it. GET /api/v1/projects, /projects/:id, /projects/:id/quota, /projects/:id/sites and /sites/:id, and DELETE /api/v1/projects/:id, /projects/:id/members/:principal, /sites/:id and /deploys/:id take no body (deleting refuses taken-down content for non-staff: 409 site.taken_down; an active deploy: 409 deploy.active).",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "name"++ ],++ "properties": {++ "name": {++ "type": "string",++ "minLength": 1,++ "description": "1–80 characters once trimmed."++ },++ "environment": {++ "enum": [++ "production",++ "sandbox",++ null++ ],++ "description": "Default production; a sandbox credential creates sandbox projects only."++ },++ "network_project_id": {++ "anyOf": [++ {++ "type": "string",++ "pattern": "^([A-Za-z0-9_:-]{3,80})?$"++ },++ {++ "type": "null"++ }++ ]++ }++ }++ },++ {++ "type": "object",++ "properties": {++ "storage_bytes": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 0,++ "description": "A non-negative integer, or null for the environment default."++ },++ "deploys_per_day": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 0,++ "description": "A non-negative integer, or null for the environment default."++ },++ "max_files": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 0,++ "description": "A non-negative integer, or null for the environment default."++ },++ "max_file_bytes": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 0,++ "description": "A non-negative integer, or null for the environment default."++ },++ "sites": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 0,++ "description": "A non-negative integer, or null for the environment default."++ },++ "custom_domains": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 0,++ "description": "A non-negative integer, or null for the environment default."++ }++ }++ },++ {++ "type": "object",++ "required": [++ "role"++ ],++ "properties": {++ "role": {++ "enum": [++ "owner",++ "maintainer",++ "deployer"++ ],++ "description": "Only a person (usr_…) can be an owner."++ }++ }++ },++ {++ "type": "object",++ "required": [++ "name"++ ],++ "properties": {++ "name": {++ "type": "string",++ "pattern": "^\\s*[A-Za-z0-9](?:[A-Za-z0-9]|-(?=[A-Za-z0-9])){2,39}\\s*$",++ "description": "The site name (lowercased)."++ }++ }++ },++ {++ "type": "object",++ "required": [++ "reason"++ ],++ "properties": {++ "reason": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Members see it."++ }++ }++ },++ {++ "type": "object",++ "properties": {++ "note": {++ "type": [++ "string",++ "null"++ ],++ "description": "Cut to 500 characters."++ }++ }++ },++ {++ "$ref": "../common/no-body.v1.json"++ }++ ]++ }++ },++ "space.site-manage-result": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/site-manage-result.v1.json",++ "title": "SpaceSiteManageResult",++ "description": "space.site-manage-result@1: answers of space.site.manage on OpenVibe.Space. GET /api/v1/projects → { projects } (the caller's projects with their role). POST /api/v1/projects → 201 { project } (with quota and usage). GET /api/v1/projects/:id → { project, sites, members }. GET and PUT /api/v1/projects/:id/quota → { quota, usage }. PUT and DELETE /api/v1/projects/:id/members/:principal → { members }. GET /api/v1/projects/:id/sites → { sites }. POST /api/v1/projects/:id/sites → 201 { site }. GET /api/v1/sites/:id → { site, domains }. DELETE /api/v1/projects/:id and /sites/:id → { deleted: true } (serving stops at once; the objects are removed). DELETE /api/v1/deploys/:id → { deleted: true, objects_removed }. POST …/takedown → 201 { takedown }; DELETE …/takedown → { lifted: true }.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "projects"++ ],++ "properties": {++ "projects": {++ "type": "array",++ "items": {++ "$ref": "project.v1.json"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "project"++ ],++ "properties": {++ "project": {++ "$ref": "project.v1.json"++ },++ "sites": {++ "type": "array",++ "items": {++ "$ref": "site.v1.json"++ }++ },++ "members": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "principal",++ "role",++ "added_at"++ ],++ "properties": {++ "principal": {++ "type": "string",++ "description": "usr_…, app:app_… or svc:…"++ },++ "role": {++ "enum": [++ "owner",++ "maintainer",++ "deployer"++ ]++ },++ "added_at": {++ "type": "string",++ "format": "date-time"++ }++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "quota",++ "usage"++ ],++ "properties": {++ "quota": {++ "type": "object",++ "required": [++ "storage_bytes",++ "deploys_per_day",++ "max_files",++ "max_file_bytes",++ "sites",++ "custom_domains"++ ],++ "description": "The effective quota: the project's override where set, else its environment's default.",++ "properties": {++ "storage_bytes": {++ "type": "integer",++ "minimum": 0++ },++ "deploys_per_day": {++ "type": "integer",++ "minimum": 0++ },++ "max_files": {++ "type": "integer",++ "minimum": 0++ },++ "max_file_bytes": {++ "type": "integer",++ "minimum": 0++ },++ "sites": {++ "type": "integer",++ "minimum": 0++ },++ "custom_domains": {++ "type": "integer",++ "minimum": 0++ }++ }++ },++ "usage": {++ "type": "object",++ "required": [++ "storage_bytes",++ "objects",++ "deploys_last_24h",++ "sites",++ "custom_domains"++ ],++ "properties": {++ "storage_bytes": {++ "type": "integer",++ "minimum": 0++ },++ "objects": {++ "type": "integer",++ "minimum": 0++ },++ "deploys_last_24h": {++ "type": "integer",++ "minimum": 0++ },++ "sites": {++ "type": "integer",++ "minimum": 0++ },++ "custom_domains": {++ "type": "integer",++ "minimum": 0++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "members"++ ],++ "properties": {++ "members": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "principal",++ "role",++ "added_at"++ ],++ "properties": {++ "principal": {++ "type": "string",++ "description": "usr_…, app:app_… or svc:…"++ },++ "role": {++ "enum": [++ "owner",++ "maintainer",++ "deployer"++ ]++ },++ "added_at": {++ "type": "string",++ "format": "date-time"++ }++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "sites"++ ],++ "properties": {++ "sites": {++ "type": "array",++ "items": {++ "$ref": "site.v1.json"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "site"++ ],++ "properties": {++ "site": {++ "$ref": "site.v1.json"++ },++ "domains": {++ "type": "array",++ "items": {++ "$ref": "domain.v1.json"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "deleted"++ ],++ "properties": {++ "deleted": {++ "const": true++ },++ "objects_removed": {++ "type": "integer",++ "minimum": 0++ }++ }++ },++ {++ "type": "object",++ "required": [++ "takedown"++ ],++ "properties": {++ "takedown": {++ "type": "object",++ "required": [++ "scope",++ "reason",++ "since"++ ],++ "description": "The takedown now in force.",++ "properties": {++ "scope": {++ "enum": [++ "project",++ "site"++ ]++ },++ "reason": {++ "type": "string"++ },++ "since": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "lifted"++ ],++ "properties": {++ "lifted": {++ "const": true++ }++ }++ }++ ]++ }++ },++ "space.site": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/site.v1.json",++ "title": "SpaceSite",++ "description": "space.site@1: one static site on OpenVibe.Space (server/http/serialize.js site): its name (one DNS label), the default hostname and URL it is served at, the deploy it serves (one pointer; null until a deploy is activated) and any staff takedown.",++ "type": "object",++ "required": [++ "id",++ "project_id",++ "name",++ "hostname",++ "url",++ "active_deploy_id",++ "takedown",++ "created_at",++ "updated_at"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "name": {++ "type": "string",++ "pattern": "^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){2,39}$"++ },++ "hostname": {++ "type": "string"++ },++ "url": {++ "type": "string"++ },++ "active_deploy_id": {++ "anyOf": [++ {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ {++ "type": "null"++ }++ ]++ },++ "takedown": {++ "type": [++ "object",++ "null"++ ],++ "required": [++ "scope",++ "reason",++ "since"++ ],++ "description": "A staff takedown covering it: serving stops while the content is kept for review.",++ "properties": {++ "scope": {++ "enum": [++ "project",++ "site"++ ]++ },++ "reason": {++ "type": "string"++ },++ "since": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "updated_at": {++ "type": "string",++ "format": "date-time"++ }++ }++ }++ },++ "space.site-config": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/site-config.v1.json",++ "title": "SpaceSiteConfig",++ "description": "space.site-config@1: one Host site's serving configuration (plan T12 J3). headers are applied to every response of the site before the platform's own, which always win; the platform's security, routing, caching, scope and transport headers (Content-Security-Policy, Strict-Transport-Security, Set-Cookie, Cache-Control, Content-Type, Location, Service-Worker-Allowed, Alt-Svc and the rest of Host's reserved list) and every X-Forwarded-* header are refused. redirects are local paths only (no scheme, host, protocol-relative URL, backslash or control character, also after percent-decoding). spa serves /index.html for an extensionless path that has no file. A site with no configuration answers { headers: {}, redirects: [], spa: false }.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "headers",++ "redirects",++ "spa"++ ],++ "properties": {++ "headers": {++ "type": "object",++ "maxProperties": 50,++ "propertyNames": {++ "pattern": "^[A-Za-z0-9][A-Za-z0-9-]{0,63}$"++ },++ "additionalProperties": {++ "type": "string",++ "maxLength": 1024++ }++ },++ "redirects": {++ "type": "array",++ "maxItems": 200,++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "from",++ "to",++ "status"++ ],++ "properties": {++ "from": {++ "type": "string",++ "pattern": "^/(?!/)",++ "maxLength": 1024++ },++ "to": {++ "type": "string",++ "pattern": "^/(?!/)",++ "maxLength": 1024++ },++ "status": {++ "enum": [++ 301,++ 302,++ 307,++ 308++ ]++ }++ }++ }++ },++ "spa": {++ "type": "boolean"++ }++ }++ }++ },++ "space.site-config-request": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/site-config-request.v1.json",++ "title": "SpaceSiteConfigRequest",++ "description": "space.site-config-request@1: the body of PUT /api/v1/sites/:id/config on OpenVibe.Space (space.site.config; the acting principal must be a maintainer or owner of the site's project). { headers?, redirects?, spa? } replaces the whole configuration: a field left out is reset to its default. A redirect's status defaults to 301. Refusals are 422 problem+json: site_config.reserved_header, site_config.header_name, site_config.header_value (longer than 1024 characters or a control character), site_config.too_many (over 50 headers or 200 redirects), site_config.redirect (not a local path, a duplicate from, or a status outside 301/302/307/308), site_config.headers and site_config.redirects (wrong type). GET and DELETE /api/v1/sites/:id/config take no body (DELETE resets to the defaults).",++ "type": "object",++ "additionalProperties": false,++ "properties": {++ "headers": {++ "type": "object",++ "additionalProperties": {++ "type": "string"++ }++ },++ "redirects": {++ "type": "array",++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "from",++ "to"++ ],++ "properties": {++ "from": {++ "type": "string"++ },++ "to": {++ "type": "string"++ },++ "status": {++ "enum": [++ 301,++ 302,++ 307,++ 308++ ]++ }++ }++ }++ },++ "spa": {++ "type": "boolean"++ }++ }++ }++ },++ "space.site-config-result": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/space/site-config-result.v1.json",++ "title": "SpaceSiteConfigResult",++ "description": "space.site-config-result@1: every answer of space.site.config on OpenVibe.Space. GET, PUT and DELETE /api/v1/sites/:id/config → { config } (after a PUT, the configuration as stored; after a DELETE, the defaults).",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "config"++ ],++ "properties": {++ "config": {++ "$ref": "site-config.v1.json"++ }++ }++ }++ },++ "space.deploy.activated": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/space.deploy.activated.v1.json",++ "title": "SpaceDeployActivatedPayload",++ "description": "space.deploy.activated v1 (OpenVibe.Space server/domain/deploys.js switchPointer, Stage B tenant hosting). A tenant site's active deploy changed, written into Host's event outbox in the transaction that moves the pointer (activate or rollback). Envelope: subject { type: deploy, id: <deploy id> }, visibility internal, priority low, actor the person or app that switched it. A network service's release going live is space.release.published, not this.",++ "type": "object",++ "required": [++ "project_id",++ "site_id",++ "site",++ "deploy_id",++ "previous_deploy_id",++ "rollback"++ ],++ "additionalProperties": false,++ "properties": {++ "project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "site_id": {++ "type": "string",++ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "site": {++ "type": "string",++ "pattern": "^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){2,39}$",++ "description": "The site's name (<site>.openvibe.website)."++ },++ "deploy_id": {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "previous_deploy_id": {++ "type": [++ "string",++ "null"++ ],++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The deploy that served before; null for the site's first activation."++ },++ "rollback": {++ "type": "boolean",++ "description": "true when the switch was a rollback."++ }++ }++ }++ },++ "space.deploy.created": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/space.deploy.created.v1.json",++ "title": "SpaceDeployCreatedPayload",++ "description": "space.deploy.created v1 (OpenVibe.Space, subject deploy dpl_…, visibility internal): an upload or a Git ingest became an immutable, content-addressed deploy in the ready state. It is not yet serving: space.deploy.activated follows when it is made active. No file contents or paths.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "project_id",++ "site_id",++ "site",++ "source",++ "file_count",++ "total_bytes",++ "manifest_sha256"++ ],++ "properties": {++ "project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "site_id": {++ "type": "string",++ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "site": {++ "type": "string",++ "pattern": "^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){2,39}$",++ "description": "the site name (one DNS label; the site is served at <site>.openvibe.website)"++ },++ "source": {++ "enum": [++ "archive",++ "files",++ "preview",++ "git"++ ],++ "description": "how the deploy arrived: an archive or individual files, a private preview upload, or a Git ingest (always a preview)"++ },++ "file_count": {++ "type": "integer",++ "minimum": 0++ },++ "total_bytes": {++ "type": "integer",++ "minimum": 0++ },++ "manifest_sha256": {++ "type": "string",++ "pattern": "^[a-f0-9]{64}$",++ "description": "sha256 of the deploy manifest (the content address of the whole deploy)"++ }++ }++ }++ },++ "space.deploy.failed": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/space.deploy.failed.v1.json",++ "title": "SpaceDeployFailedPayload",++ "description": "space.deploy.failed v1 (OpenVibe.Space, subject deploy dpl_…, visibility internal): an upload was refused and recorded as a failed deploy with its log, so the owner can see why. Problem codes only, at most ten; never file contents.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "project_id",++ "site_id",++ "site",++ "code",++ "problems"++ ],++ "properties": {++ "project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "site_id": {++ "type": "string",++ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "site": {++ "type": "string",++ "pattern": "^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){2,39}$",++ "description": "the site name (one DNS label; the site is served at <site>.openvibe.website)"++ },++ "code": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)+$",++ "description": "the refusal problem code (for example deploy.too_large or storage.space_full)"++ },++ "problems": {++ "type": "array",++ "maxItems": 10,++ "items": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)*$"++ },++ "description": "the problem codes of the files that were refused"++ }++ }++ }++ },++ "space.domain.verified": {++ "version": "1.0.0",++ "owner": "space",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/space.domain.verified.v1.json",++ "title": "SpaceDomainVerifiedPayload",++ "description": "space.domain.verified v1 (OpenVibe.Space, subject domain dom_…, visibility internal): a custom domain's TXT record (_openvibe-space.<hostname>) was found, and the domain now serves the site.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "project_id",++ "site_id",++ "site",++ "hostname"++ ],++ "properties": {++ "project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "site_id": {++ "type": "string",++ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "site": {++ "type": "string",++ "pattern": "^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){2,39}$",++ "description": "the site name (one DNS label; the site is served at <site>.openvibe.website)"++ },++ "hostname": {++ "type": "string",++ "minLength": 4,++ "maxLength": 253,++ "description": "the custom domain, a fully qualified host name in lower case"++ }++ }++ }}}}@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Actor API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.AI API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Billing API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Blog API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Bot API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Chat API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Community API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Coupons API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Deals API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Events API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Food API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Games API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Host API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -169,6 +169,13 @@"operations": 13,"capabilities": 3},++ {++ "service": "space",++ "name": "OpenVibe.Space",++ "origin": "https://openvibe.space",++ "operations": 36,++ "capabilities": 5++ },{"service": "tips","name": "OpenVibe.Tips",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Inventory API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Live API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Media API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Network API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.News API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenRestream API",-- "version": "0.148.0",-- "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Reviews API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Run API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Search API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Services API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Sources API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"nnew file mode 100644@@@ -0,0 +1,3971 @@++{++ "openapi": "3.1.0",++ "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",++ "info": {++ "title": "OpenVibe.Space API",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "license": {++ "name": "MIT",++ "identifier": "MIT"++ }++ },++ "servers": [++ {++ "url": "https://openvibe.space"++ }++ ],++ "paths": {++ "/api/v1/deploys/{id}": {++ "delete": {++ "operationId": "delete_api_v1_deploys_id",++ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",++ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",++ "tags": [++ "space.site"++ ],++ "x-openvibe-capabilities": [++ "space.site.manage"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.site.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/space.site-manage-request.v1"++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "get": {++ "operationId": "get_api_v1_deploys_id",++ "summary": "Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys ",++ "description": "**space.deploy.create** (first-party): Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys and their upload logs, activate a deploy and roll a site back to any earlier ready deploy (one atomic pointer switch). Also judged by the acting principal's project role (deployer or above); an app principal added to a project as deployer can deploy from CI. A site with a Git source also accepts a CI-built deploy at /sites/:id/source/deploys naming the ref and full commit_sha; it is always a preview and records that commit as immutable provenance.",++ "tags": [++ "space.deploy"++ ],++ "x-openvibe-capabilities": [++ "space.deploy.create"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-deploy",++ "security": [++ {++ "openvibeToken": [++ "space.deploy.create"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/space.deploy-create-request.v1"++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.deploy-create-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/deploys/{id}/activate": {++ "post": {++ "operationId": "post_api_v1_deploys_id_activate",++ "summary": "Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys ",++ "description": "**space.deploy.create** (first-party): Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys and their upload logs, activate a deploy and roll a site back to any earlier ready deploy (one atomic pointer switch). Also judged by the acting principal's project role (deployer or above); an app principal added to a project as deployer can deploy from CI. A site with a Git source also accepts a CI-built deploy at /sites/:id/source/deploys naming the ref and full commit_sha; it is always a preview and records that commit as immutable provenance.",++ "tags": [++ "space.deploy"++ ],++ "x-openvibe-capabilities": [++ "space.deploy.create"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-deploy",++ "security": [++ {++ "openvibeToken": [++ "space.deploy.create"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.deploy-create-request.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.deploy-create-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/deploys/{id}/log": {++ "get": {++ "operationId": "get_api_v1_deploys_id_log",++ "summary": "Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys ",++ "description": "**space.deploy.create** (first-party): Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys and their upload logs, activate a deploy and roll a site back to any earlier ready deploy (one atomic pointer switch). Also judged by the acting principal's project role (deployer or above); an app principal added to a project as deployer can deploy from CI. A site with a Git source also accepts a CI-built deploy at /sites/:id/source/deploys naming the ref and full commit_sha; it is always a preview and records that commit as immutable provenance.",++ "tags": [++ "space.deploy"++ ],++ "x-openvibe-capabilities": [++ "space.deploy.create"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-deploy",++ "security": [++ {++ "openvibeToken": [++ "space.deploy.create"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/space.deploy-create-request.v1"++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.deploy-create-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/domains/{id}": {++ "delete": {++ "operationId": "delete_api_v1_domains_id",++ "summary": "Add a custom domain to a static site (never an OpenVibe domain; not in sandbox projects), read its DNS TXT verification and routing records, trigger a verification check, and remove it.",++ "description": "**space.domain.manage** (first-party): Add a custom domain to a static site (never an OpenVibe domain; not in sandbox projects), read its DNS TXT verification and routing records, trigger a verification check, and remove it. A custom domain is served only after its TXT record is verified; TLS certificates are issued by an operator and never pass through this capability.",++ "tags": [++ "space.domain"++ ],++ "x-openvibe-capabilities": [++ "space.domain.manage"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.domain.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/space.domain-manage-request.v1"++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.domain-manage-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/domains/{id}/verify": {++ "post": {++ "operationId": "post_api_v1_domains_id_verify",++ "summary": "Add a custom domain to a static site (never an OpenVibe domain; not in sandbox projects), read its DNS TXT verification and routing records, trigger a verification check, and remove it.",++ "description": "**space.domain.manage** (first-party): Add a custom domain to a static site (never an OpenVibe domain; not in sandbox projects), read its DNS TXT verification and routing records, trigger a verification check, and remove it. A custom domain is served only after its TXT record is verified; TLS certificates are issued by an operator and never pass through this capability.",++ "tags": [++ "space.domain"++ ],++ "x-openvibe-capabilities": [++ "space.domain.manage"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.domain.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.domain-manage-request.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.domain-manage-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/projects": {++ "get": {++ "operationId": "get_api_v1_projects",++ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",++ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",++ "tags": [++ "space.site"++ ],++ "x-openvibe-capabilities": [++ "space.site.manage"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.site.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/space.site-manage-request.v1"++ },++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "post": {++ "operationId": "post_api_v1_projects",++ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",++ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",++ "tags": [++ "space.site"++ ],++ "x-openvibe-capabilities": [++ "space.site.manage"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.site.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-request.v1"++ }++ }++ }++ },++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/projects/{id}": {++ "delete": {++ "operationId": "delete_api_v1_projects_id",++ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",++ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",++ "tags": [++ "space.site"++ ],++ "x-openvibe-capabilities": [++ "space.site.manage"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.site.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/space.site-manage-request.v1"++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "get": {++ "operationId": "get_api_v1_projects_id",++ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",++ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",++ "tags": [++ "space.site"++ ],++ "x-openvibe-capabilities": [++ "space.site.manage"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.site.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/space.site-manage-request.v1"++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/projects/{id}/members/{principal}": {++ "delete": {++ "operationId": "delete_api_v1_projects_id_members_principal",++ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",++ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",++ "tags": [++ "space.site"++ ],++ "x-openvibe-capabilities": [++ "space.site.manage"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.site.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/space.site-manage-request.v1"++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "principal",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "put": {++ "operationId": "put_api_v1_projects_id_members_principal",++ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",++ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",++ "tags": [++ "space.site"++ ],++ "x-openvibe-capabilities": [++ "space.site.manage"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.site.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-request.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "principal",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/projects/{id}/quota": {++ "get": {++ "operationId": "get_api_v1_projects_id_quota",++ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",++ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",++ "tags": [++ "space.site"++ ],++ "x-openvibe-capabilities": [++ "space.site.manage"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.site.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/space.site-manage-request.v1"++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "put": {++ "operationId": "put_api_v1_projects_id_quota",++ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",++ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",++ "tags": [++ "space.site"++ ],++ "x-openvibe-capabilities": [++ "space.site.manage"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.site.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-request.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/projects/{id}/sites": {++ "get": {++ "operationId": "get_api_v1_projects_id_sites",++ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",++ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",++ "tags": [++ "space.site"++ ],++ "x-openvibe-capabilities": [++ "space.site.manage"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.site.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/space.site-manage-request.v1"++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "post": {++ "operationId": "post_api_v1_projects_id_sites",++ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",++ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",++ "tags": [++ "space.site"++ ],++ "x-openvibe-capabilities": [++ "space.site.manage"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.site.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-request.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/projects/{id}/takedown": {++ "delete": {++ "operationId": "delete_api_v1_projects_id_takedown",++ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",++ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",++ "tags": [++ "space.site"++ ],++ "x-openvibe-capabilities": [++ "space.site.manage"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.site.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/space.site-manage-request.v1"++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "post": {++ "operationId": "post_api_v1_projects_id_takedown",++ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",++ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",++ "tags": [++ "space.site"++ ],++ "x-openvibe-capabilities": [++ "space.site.manage"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.site.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-request.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/resources": {++ "get": {++ "operationId": "get_api_v1_resources",++ "summary": "Read OpenVibe.Space's resource index (ADR-048): GET /api/v1/resources lists the resources Host owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:o",++ "description": "**space.resource.read** (first-party): Read OpenVibe.Space's resource index (ADR-048): GET /api/v1/resources lists the resources Host owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:ovrn reads one by its OVRN (common.resource-summary@1). Every authority serves these for OpenVibe.Services to fan out and merge; Host's own resources are its static sites (kind space.site, id sit_<ULID>), the immutable deploys of those sites (kind space.deploy, id dpl_<ULID>) and their default and custom domains (kind space.domain, id dom_<ULID>), each with the project_id of the Network project that owns it. Host lists no projects; only Network lists projects. Planned until Host ships the index.",++ "tags": [++ "space.resource"++ ],++ "x-openvibe-capabilities": [++ "space.resource.read"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "standard",++ "security": [++ {++ "openvibeToken": [++ "space.resource.read"++ ]++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.resource-list-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/resources/{ovrn}": {++ "get": {++ "operationId": "get_api_v1_resources_ovrn",++ "summary": "Read OpenVibe.Space's resource index (ADR-048): GET /api/v1/resources lists the resources Host owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:o",++ "description": "**space.resource.read** (first-party): Read OpenVibe.Space's resource index (ADR-048): GET /api/v1/resources lists the resources Host owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:ovrn reads one by its OVRN (common.resource-summary@1). Every authority serves these for OpenVibe.Services to fan out and merge; Host's own resources are its static sites (kind space.site, id sit_<ULID>), the immutable deploys of those sites (kind space.deploy, id dpl_<ULID>) and their default and custom domains (kind space.domain, id dom_<ULID>), each with the project_id of the Network project that owns it. Host lists no projects; only Network lists projects. Planned until Host ships the index.",++ "tags": [++ "space.resource"++ ],++ "x-openvibe-capabilities": [++ "space.resource.read"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "standard",++ "security": [++ {++ "openvibeToken": [++ "space.resource.read"++ ]++ }++ ],++ "parameters": [++ {++ "name": "ovrn",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/common.resource-list-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/sites/{id}": {++ "delete": {++ "operationId": "delete_api_v1_sites_id",++ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",++ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",++ "tags": [++ "space.site"++ ],++ "x-openvibe-capabilities": [++ "space.site.manage"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.site.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/space.site-manage-request.v1"++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "get": {++ "operationId": "get_api_v1_sites_id",++ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",++ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",++ "tags": [++ "space.site"++ ],++ "x-openvibe-capabilities": [++ "space.site.manage"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.site.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/space.site-manage-request.v1"++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/sites/{id}/config": {++ "delete": {++ "operationId": "delete_api_v1_sites_id_config",++ "summary": "Read, set and reset a Host site's serving configuration: response headers, local-only redirects and the SPA fallback (extensionless paths serve index.html).",++ "description": "**space.site.config** (first-party): Read, set and reset a Host site's serving configuration: response headers, local-only redirects and the SPA fallback (extensionless paths serve index.html). A capability holder must also hold the acting principal's project role (maintainer or owner to change, any role to read). Reserved headers (Content-Security-Policy, Strict-Transport-Security, Set-Cookie, X-Forwarded-*, caching, scope, transport and the platform's own) are refused, and a redirect target must be a local path, so a site can never become an open redirect.",++ "tags": [++ "space.site"++ ],++ "x-openvibe-capabilities": [++ "space.site.config"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.site.config"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/space.site-config-request.v1"++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "headers",++ "in": "query",++ "required": false,++ "schema": {++ "type": "object",++ "additionalProperties": {++ "type": "string"++ }++ }++ },++ {++ "name": "redirects",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "from",++ "to"++ ],++ "properties": {++ "from": {++ "type": "string"++ },++ "to": {++ "type": "string"++ },++ "status": {++ "enum": [++ 301,++ 302,++ 307,++ 308++ ]++ }++ }++ }++ }++ },++ {++ "name": "spa",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-config-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "get": {++ "operationId": "get_api_v1_sites_id_config",++ "summary": "Read, set and reset a Host site's serving configuration: response headers, local-only redirects and the SPA fallback (extensionless paths serve index.html).",++ "description": "**space.site.config** (first-party): Read, set and reset a Host site's serving configuration: response headers, local-only redirects and the SPA fallback (extensionless paths serve index.html). A capability holder must also hold the acting principal's project role (maintainer or owner to change, any role to read). Reserved headers (Content-Security-Policy, Strict-Transport-Security, Set-Cookie, X-Forwarded-*, caching, scope, transport and the platform's own) are refused, and a redirect target must be a local path, so a site can never become an open redirect.",++ "tags": [++ "space.site"++ ],++ "x-openvibe-capabilities": [++ "space.site.config"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.site.config"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/space.site-config-request.v1"++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "headers",++ "in": "query",++ "required": false,++ "schema": {++ "type": "object",++ "additionalProperties": {++ "type": "string"++ }++ }++ },++ {++ "name": "redirects",++ "in": "query",++ "required": false,++ "schema": {++ "type": "array",++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "from",++ "to"++ ],++ "properties": {++ "from": {++ "type": "string"++ },++ "to": {++ "type": "string"++ },++ "status": {++ "enum": [++ 301,++ 302,++ 307,++ 308++ ]++ }++ }++ }++ }++ },++ {++ "name": "spa",++ "in": "query",++ "required": false,++ "schema": {++ "type": "boolean"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-config-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "put": {++ "operationId": "put_api_v1_sites_id_config",++ "summary": "Read, set and reset a Host site's serving configuration: response headers, local-only redirects and the SPA fallback (extensionless paths serve index.html).",++ "description": "**space.site.config** (first-party): Read, set and reset a Host site's serving configuration: response headers, local-only redirects and the SPA fallback (extensionless paths serve index.html). A capability holder must also hold the acting principal's project role (maintainer or owner to change, any role to read). Reserved headers (Content-Security-Policy, Strict-Transport-Security, Set-Cookie, X-Forwarded-*, caching, scope, transport and the platform's own) are refused, and a redirect target must be a local path, so a site can never become an open redirect.",++ "tags": [++ "space.site"++ ],++ "x-openvibe-capabilities": [++ "space.site.config"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.site.config"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-config-request.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-config-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/sites/{id}/deploys": {++ "get": {++ "operationId": "get_api_v1_sites_id_deploys",++ "summary": "Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys ",++ "description": "**space.deploy.create** (first-party): Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys and their upload logs, activate a deploy and roll a site back to any earlier ready deploy (one atomic pointer switch). Also judged by the acting principal's project role (deployer or above); an app principal added to a project as deployer can deploy from CI. A site with a Git source also accepts a CI-built deploy at /sites/:id/source/deploys naming the ref and full commit_sha; it is always a preview and records that commit as immutable provenance.",++ "tags": [++ "space.deploy"++ ],++ "x-openvibe-capabilities": [++ "space.deploy.create"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-deploy",++ "security": [++ {++ "openvibeToken": [++ "space.deploy.create"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/space.deploy-create-request.v1"++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.deploy-create-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "post": {++ "operationId": "post_api_v1_sites_id_deploys",++ "summary": "Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys ",++ "description": "**space.deploy.create** (first-party): Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys and their upload logs, activate a deploy and roll a site back to any earlier ready deploy (one atomic pointer switch). Also judged by the acting principal's project role (deployer or above); an app principal added to a project as deployer can deploy from CI. A site with a Git source also accepts a CI-built deploy at /sites/:id/source/deploys naming the ref and full commit_sha; it is always a preview and records that commit as immutable provenance.",++ "tags": [++ "space.deploy"++ ],++ "x-openvibe-capabilities": [++ "space.deploy.create"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-deploy",++ "security": [++ {++ "openvibeToken": [++ "space.deploy.create"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.deploy-create-request.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.deploy-create-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/sites/{id}/domains": {++ "get": {++ "operationId": "get_api_v1_sites_id_domains",++ "summary": "Add a custom domain to a static site (never an OpenVibe domain; not in sandbox projects), read its DNS TXT verification and routing records, trigger a verification check, and remove it.",++ "description": "**space.domain.manage** (first-party): Add a custom domain to a static site (never an OpenVibe domain; not in sandbox projects), read its DNS TXT verification and routing records, trigger a verification check, and remove it. A custom domain is served only after its TXT record is verified; TLS certificates are issued by an operator and never pass through this capability.",++ "tags": [++ "space.domain"++ ],++ "x-openvibe-capabilities": [++ "space.domain.manage"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.domain.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/space.domain-manage-request.v1"++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.domain-manage-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "post": {++ "operationId": "post_api_v1_sites_id_domains",++ "summary": "Add a custom domain to a static site (never an OpenVibe domain; not in sandbox projects), read its DNS TXT verification and routing records, trigger a verification check, and remove it.",++ "description": "**space.domain.manage** (first-party): Add a custom domain to a static site (never an OpenVibe domain; not in sandbox projects), read its DNS TXT verification and routing records, trigger a verification check, and remove it. A custom domain is served only after its TXT record is verified; TLS certificates are issued by an operator and never pass through this capability.",++ "tags": [++ "space.domain"++ ],++ "x-openvibe-capabilities": [++ "space.domain.manage"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.domain.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.domain-manage-request.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.domain-manage-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/sites/{id}/rollback": {++ "post": {++ "operationId": "post_api_v1_sites_id_rollback",++ "summary": "Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys ",++ "description": "**space.deploy.create** (first-party): Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys and their upload logs, activate a deploy and roll a site back to any earlier ready deploy (one atomic pointer switch). Also judged by the acting principal's project role (deployer or above); an app principal added to a project as deployer can deploy from CI. A site with a Git source also accepts a CI-built deploy at /sites/:id/source/deploys naming the ref and full commit_sha; it is always a preview and records that commit as immutable provenance.",++ "tags": [++ "space.deploy"++ ],++ "x-openvibe-capabilities": [++ "space.deploy.create"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-deploy",++ "security": [++ {++ "openvibeToken": [++ "space.deploy.create"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.deploy-create-request.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.deploy-create-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/sites/{id}/source": {++ "delete": {++ "operationId": "delete_api_v1_sites_id_source",++ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",++ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",++ "tags": [++ "space.site"++ ],++ "x-openvibe-capabilities": [++ "space.site.manage"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.site.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/space.site-manage-request.v1"++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "get": {++ "operationId": "get_api_v1_sites_id_source",++ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",++ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",++ "tags": [++ "space.site"++ ],++ "x-openvibe-capabilities": [++ "space.site.manage"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.site.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/space.site-manage-request.v1"++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "put": {++ "operationId": "put_api_v1_sites_id_source",++ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",++ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",++ "tags": [++ "space.site"++ ],++ "x-openvibe-capabilities": [++ "space.site.manage"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.site.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-request.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/sites/{id}/source/deploys": {++ "post": {++ "operationId": "post_api_v1_sites_id_source_deploys",++ "summary": "Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys ",++ "description": "**space.deploy.create** (first-party): Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys and their upload logs, activate a deploy and roll a site back to any earlier ready deploy (one atomic pointer switch). Also judged by the acting principal's project role (deployer or above); an app principal added to a project as deployer can deploy from CI. A site with a Git source also accepts a CI-built deploy at /sites/:id/source/deploys naming the ref and full commit_sha; it is always a preview and records that commit as immutable provenance.",++ "tags": [++ "space.deploy"++ ],++ "x-openvibe-capabilities": [++ "space.deploy.create"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-deploy",++ "security": [++ {++ "openvibeToken": [++ "space.deploy.create"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.deploy-create-request.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.deploy-create-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/sites/{id}/takedown": {++ "delete": {++ "operationId": "delete_api_v1_sites_id_takedown",++ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",++ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",++ "tags": [++ "space.site"++ ],++ "x-openvibe-capabilities": [++ "space.site.manage"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.site.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/space.site-manage-request.v1"++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "post": {++ "operationId": "post_api_v1_sites_id_takedown",++ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",++ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",++ "tags": [++ "space.site"++ ],++ "x-openvibe-capabilities": [++ "space.site.manage"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "space-manage",++ "security": [++ {++ "openvibeToken": [++ "space.site.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-request.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/space.site-manage-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ }++ },++ "components": {++ "schemas": {++ "common.no-body.v1": {++ "title": "NoBody",++ "description": "common.no-body@1: the capability's request carries no body; its parameters are the path and query of the routes in implementedBy (an id, a page cursor). An input schema that says so explicitly, so every active capability names one (roadmap WS-C task 4).",++ "type": "object",++ "maxProperties": 0++ },++ "common.resource-list-result.v1": {++ "title": "ResourceListResult",++ "description": "A page of the resource index (roadmap WS-Z7).",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "resources",++ "next_cursor"++ ],++ "properties": {++ "resources": {++ "type": "array",++ "items": {++ "$ref": "#/components/schemas/common.resource-summary.v1"++ }++ },++ "next_cursor": {++ "type": [++ "string",++ "null"++ ]++ },++ "partial": {++ "type": "array",++ "description": "Authorities a merged index (OpenVibe.Services, ADR-048) could not read for this page: a slow or failing authority is omitted, never fails the page. Each entry names the authority and the problem code it answered. Added in 1.1.0; a single authority's own index leaves it out.",++ "items": {++ "type": "object",++ "required": [++ "service",++ "code"++ ],++ "additionalProperties": false,++ "properties": {++ "service": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{1,39}$"++ },++ "code": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)*$"++ }++ }++ }++ }++ }++ },++ "common.resource-summary.v1": {++ "title": "ResourceSummary",++ "description": "One resource of any service, as the resource index lists it (roadmap WS-Z7): every service answers GET /api/v1/resources with these, so OpenVibe.Services shows any resource without owning its data.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "id",++ "kind",++ "service",++ "state",++ "created_at"++ ],++ "properties": {++ "id": {++ "type": "string"++ },++ "kind": {++ "type": "string",++ "description": "service.kind, e.g. watch.watch, actor.actor, media.object, events.subscription"++ },++ "service": {++ "type": "string"++ },++ "project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "ovrn": {++ "type": "string",++ "maxLength": 200,++ "pattern": "^ovrn:[a-z][a-z0-9-]{0,31}:prj_[0-9A-HJKMNP-TV-Z]{26}:[a-z][a-z0-9-]{0,39}/[a-z]{3}_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "Optional resource name (ADR-034 section 2), the name grants, audit, events, usage records and bills use: ovrn:<service>:<project_id>:<type>/<id>, e.g. ovrn:watch:<project_id>:watch/<wch_id>, with the pattern of common.usage-recorded@1 resource. Its service, project and id are this summary's service, project_id and id. Added in 1.1.0."++ },++ "owner": {++ "$ref": "#/components/schemas/identity.subject-ref.v1"++ },++ "name": {++ "type": "string"++ },++ "state": {++ "type": "string"++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "updated_at": {++ "type": "string",++ "format": "date-time"++ },++ "usage": {++ "type": "object",++ "additionalProperties": {++ "type": "number",++ "minimum": 0++ },++ "properties": {},++ "description": "cost units by name (WS-Z1)"++ },++ "links": {++ "type": "object",++ "additionalProperties": false,++ "properties": {++ "self": {++ "type": "string",++ "format": "uri"++ },++ "console": {++ "type": "string",++ "format": "uri"++ },++ "docs": {++ "type": "string",++ "format": "uri"++ }++ }++ }++ }++ },++ "errors.problem.v1": {++ "title": "Problem",++ "description": "RFC 9457 problem details (application/problem+json) with a stable OpenVibe error code and trace identifiers. The legacy 'error' string is allowed during migration so existing clients keep working.",++ "type": "object",++ "required": [++ "type",++ "title",++ "status",++ "code"++ ],++ "additionalProperties": true,++ "properties": {++ "type": {++ "type": "string",++ "format": "uri-reference"++ },++ "title": {++ "type": "string",++ "minLength": 1++ },++ "status": {++ "type": "integer",++ "minimum": 400,++ "maximum": 599++ },++ "detail": {++ "type": "string"++ },++ "instance": {++ "type": "string",++ "format": "uri-reference"++ },++ "code": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)*$"++ },++ "request_id": {++ "type": "string"++ },++ "trace_id": {++ "type": "string",++ "pattern": "^[0-9a-f]{32}$"++ },++ "errors": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "message"++ ],++ "properties": {++ "path": {++ "type": "string"++ },++ "message": {++ "type": "string"++ }++ }++ }++ },++ "error": {++ "type": "string",++ "description": "Deprecated compatibility field: same text as title/detail for clients that read {error}."++ }++ }++ },++ "identity.subject-ref.v1": {++ "title": "SubjectRef",++ "description": "The one cross-platform reference to an actor. Issued by OpenVibe.Network. Never a service-local integer id.",++ "oneOf": [++ {++ "type": "object",++ "required": [++ "type",++ "id"++ ],++ "additionalProperties": false,++ "properties": {++ "type": {++ "const": "user"++ },++ "id": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ }++ },++ "description": "UserSubject"++ },++ {++ "type": "object",++ "required": [++ "type",++ "id"++ ],++ "additionalProperties": false,++ "properties": {++ "type": {++ "const": "guest"++ },++ "id": {++ "type": "string",++ "pattern": "^gst_[0-9A-HJKMNP-TV-Z]{26}$"++ }++ },++ "description": "GuestSubject"++ },++ {++ "type": "object",++ "required": [++ "type",++ "id"++ ],++ "additionalProperties": false,++ "properties": {++ "type": {++ "const": "app"++ },++ "id": {++ "type": "string",++ "pattern": "^app_[0-9A-HJKMNP-TV-Z]{26}$"++ }++ },++ "description": "AppSubject"++ },++ {++ "type": "object",++ "required": [++ "type",++ "id"++ ],++ "additionalProperties": false,++ "properties": {++ "type": {++ "const": "mod"++ },++ "id": {++ "type": "string",++ "pattern": "^mod_[0-9A-HJKMNP-TV-Z]{26}$"++ }++ },++ "description": "ModSubject"++ },++ {++ "type": "object",++ "required": [++ "type",++ "id"++ ],++ "additionalProperties": false,++ "properties": {++ "type": {++ "enum": [++ "service",++ "system"++ ]++ },++ "id": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9-]{1,39}$"++ }++ },++ "description": "PrincipalSubject"++ },++ {++ "type": "object",++ "required": [++ "type",++ "id"++ ],++ "additionalProperties": false,++ "properties": {++ "type": {++ "const": "agent"++ },++ "id": {++ "type": "string",++ "pattern": "^agt_[0-9A-HJKMNP-TV-Z]{26}$"++ }++ },++ "description": "AgentSubject: an agent (OpenVibe.Actor, or a developer app's agent) acting under grants its owner delegated (roadmap WS-Z2). In an event envelope, on_behalf_of names the owner."++ }++ ]++ },++ "space.deploy-create-request.v1": {++ "title": "SpaceDeployCreateRequest",++ "description": "space.deploy-create-request@1: bodies of space.deploy.create on OpenVibe.Space (a deployer or above of the project; an app principal added as deployer can deploy from CI). POST /api/v1/sites/:id/deploys uploads a deploy: the raw body is a tar or tar.gz archive (Content-Type application/gzip, application/x-gzip, application/x-tar or application/octet-stream), or multipart/form-data with archive=<one archive> or files=<file>… (each part's filename is its path); optional fields activate=1, root=<subdirectory to deploy> and strip=folder (removes a shared first segment) also work as query parameters. It is validated (path traversal, links, special and hidden files, server-side code, file types, sizes and quotas) and never executed; a refusal is a problem whose extra carries the failed deploy_id and its log (413 upload.too_large, quota.storage; 422 validation codes; 429 quota.deploys_per_day; 403 site.taken_down; 503 upload.busy; 507 storage.host_full). POST /api/v1/deploys/:id/activate { expected_active? } and POST /api/v1/sites/:id/rollback { deploy_id?, expected_active? } switch the site's pointer in one step (rollback without deploy_id goes to the deploy active before; 409 deploy.not_ready, site.active_changed when expected_active is stale, deploy.no_previous). GET /api/v1/sites/:id/deploys, /deploys/:id and /deploys/:id/log take no body.",++ "anyOf": [++ {++ "type": "string",++ "contentEncoding": "binary",++ "description": "The upload itself: the tar or tar.gz bytes, or the multipart/form-data body (as common.binary@1)."++ },++ {++ "type": "object",++ "properties": {++ "deploy_id": {++ "anyOf": [++ {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ {++ "type": "null"++ }++ ]++ },++ "expected_active": {++ "anyOf": [++ {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ {++ "type": "null"++ }++ ],++ "description": "The deploy you saw active (null: none); refused with 409 site.active_changed when it moved."++ }++ }++ },++ {++ "$ref": "#/components/schemas/common.no-body.v1"++ }++ ]++ },++ "space.deploy-create-result.v1": {++ "title": "SpaceDeployCreateResult",++ "description": "space.deploy-create-result@1: answers of space.deploy.create on OpenVibe.Space. GET /api/v1/sites/:id/deploys?limit= → { active_deploy_id, deploys, activations } (newest first; the last 20 pointer switches). POST /api/v1/sites/:id/deploys → 201 { deploy, activated, url } (the ready deploy with its validation log; activated when activate=1 switched the site to it). GET /api/v1/deploys/:id → { deploy } with its files. GET /api/v1/deploys/:id/log → { deploy_id, state, log }. POST /api/v1/deploys/:id/activate and /sites/:id/rollback → { active_deploy_id, previous_deploy_id, changed } (changed false when it was already active). Emits space.deploy.created|activated|failed.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "active_deploy_id",++ "deploys",++ "activations"++ ],++ "properties": {++ "active_deploy_id": {++ "anyOf": [++ {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ {++ "type": "null"++ }++ ]++ },++ "deploys": {++ "type": "array",++ "items": {++ "$ref": "#/components/schemas/space.deploy.v1"++ }++ },++ "activations": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "deploy_id",++ "previous_deploy_id",++ "kind",++ "actor",++ "at"++ ],++ "properties": {++ "deploy_id": {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "previous_deploy_id": {++ "anyOf": [++ {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ {++ "type": "null"++ }++ ]++ },++ "kind": {++ "enum": [++ "activate",++ "rollback"++ ]++ },++ "actor": {++ "type": "string"++ },++ "at": {++ "type": "string",++ "format": "date-time"++ }++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "deploy"++ ],++ "properties": {++ "deploy": {++ "$ref": "#/components/schemas/space.deploy.v1"++ },++ "activated": {++ "type": "boolean"++ },++ "url": {++ "type": "string"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "deploy_id",++ "state",++ "log"++ ],++ "properties": {++ "deploy_id": {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "state": {++ "enum": [++ "ready",++ "failed",++ "deleted"++ ]++ },++ "log": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "level",++ "message",++ "at"++ ],++ "properties": {++ "level": {++ "enum": [++ "info",++ "warn",++ "error"++ ]++ },++ "message": {++ "type": "string"++ },++ "at": {++ "type": "string",++ "format": "date-time"++ }++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "active_deploy_id",++ "previous_deploy_id",++ "changed"++ ],++ "properties": {++ "active_deploy_id": {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "previous_deploy_id": {++ "anyOf": [++ {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ {++ "type": "null"++ }++ ]++ },++ "changed": {++ "type": "boolean"++ }++ }++ }++ ]++ },++ "space.deploy.v1": {++ "title": "SpaceDeploy",++ "description": "space.deploy@1: one immutable static deploy on OpenVibe.Space (server/http/serialize.js deploy): ready (validated and stored, never executed) or failed (refused, with its failure code and log), whether the site serves it, its size and manifest hash. GET /api/v1/deploys/:id adds the files; the upload answer adds the log.",++ "type": "object",++ "required": [++ "id",++ "site_id",++ "project_id",++ "state",++ "active",++ "source",++ "file_count",++ "total_bytes",++ "new_bytes",++ "manifest_sha256",++ "failure_code",++ "created_by",++ "created_at"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "site_id": {++ "type": "string",++ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "state": {++ "enum": [++ "ready",++ "failed",++ "deleted"++ ]++ },++ "active": {++ "type": "boolean"++ },++ "source": {++ "enum": [++ "archive",++ "files",++ "preview",++ "git"++ ]++ },++ "git": {++ "type": "object",++ "required": [++ "provider",++ "repo_url",++ "ref",++ "commit_sha"++ ],++ "properties": {++ "provider": {++ "type": "string"++ },++ "repo_url": {++ "type": "string"++ },++ "ref": {++ "type": "string"++ },++ "commit_sha": {++ "type": "string",++ "pattern": "^[0-9a-f]{40}$"++ }++ },++ "description": "Present on source git: the commit this deploy was built from (public provenance, never a credential)."++ },++ "file_count": {++ "type": "integer",++ "minimum": 0++ },++ "total_bytes": {++ "type": "integer",++ "minimum": 0++ },++ "new_bytes": {++ "type": "integer",++ "minimum": 0,++ "description": "Bytes this deploy added to the project's store."++ },++ "manifest_sha256": {++ "type": [++ "string",++ "null"++ ],++ "pattern": "^[0-9a-f]{64}$"++ },++ "failure_code": {++ "type": [++ "string",++ "null"++ ]++ },++ "created_by": {++ "type": "string"++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "files": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "path",++ "sha256",++ "size",++ "content_type"++ ],++ "properties": {++ "path": {++ "type": "string"++ },++ "sha256": {++ "type": "string",++ "pattern": "^[0-9a-f]{64}$"++ },++ "size": {++ "type": "integer",++ "minimum": 0++ },++ "content_type": {++ "type": "string"++ }++ }++ }++ },++ "log": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "level",++ "message",++ "at"++ ],++ "properties": {++ "level": {++ "enum": [++ "info",++ "warn",++ "error"++ ]++ },++ "message": {++ "type": "string"++ },++ "at": {++ "type": "string",++ "format": "date-time"++ }++ }++ }++ }++ }++ },++ "space.domain-manage-request.v1": {++ "title": "SpaceDomainManageRequest",++ "description": "space.domain-manage-request@1: bodies of space.domain.manage on OpenVibe.Space (a maintainer or above of the project). POST /api/v1/sites/:id/domains { hostname } adds a custom domain, pending until its TXT record is verified: a fully qualified host name, lowercased (422 domain.invalid_hostname; 422 domain.reserved for any OpenVibe domain; 403 domain.sandbox in sandbox projects; 429 quota.custom_domains; 409 domain.exists, domain.taken). GET /api/v1/sites/:id/domains, POST /api/v1/domains/:id/verify (checks the TXT record now) and DELETE /api/v1/domains/:id (409 domain.default: the default domain goes only with the site) take no body.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "hostname"++ ],++ "properties": {++ "hostname": {++ "type": "string",++ "minLength": 1,++ "maxLength": 254,++ "description": "e.g. www.example.org (a trailing dot is dropped)."++ }++ }++ },++ {++ "$ref": "#/components/schemas/common.no-body.v1"++ }++ ]++ },++ "space.domain-manage-result.v1": {++ "title": "SpaceDomainManageResult",++ "description": "space.domain-manage-result@1: answers of space.domain.manage on OpenVibe.Space. GET /api/v1/sites/:id/domains → { domains } (the default domain first). POST /api/v1/sites/:id/domains → 201 { domain } with the DNS records to publish. POST /api/v1/domains/:id/verify → { domain } after the check (status verified and served once the TXT record matches, else last_error says what was found; emits space.domain.verified). DELETE /api/v1/domains/:id → { deleted: true }.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "domains"++ ],++ "properties": {++ "domains": {++ "type": "array",++ "items": {++ "$ref": "#/components/schemas/space.domain.v1"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "domain"++ ],++ "properties": {++ "domain": {++ "$ref": "#/components/schemas/space.domain.v1"++ }++ }++ },++ {++ "type": "object",++ "required": [++ "deleted"++ ],++ "properties": {++ "deleted": {++ "const": true++ }++ }++ }++ ]++ },++ "space.domain.v1": {++ "title": "SpaceDomain",++ "description": "space.domain@1: one domain of a static site on OpenVibe.Space (server/http/serialize.js domain): the default <site>.openvibe.website, or a custom domain that is served only once its DNS TXT record is verified (re-checked daily; it lapses when the record is gone). A custom domain carries the records its owner must publish (the TXT verification value is public by design and shown only to members); TLS certificates are issued by an operator.",++ "type": "object",++ "required": [++ "id",++ "site_id",++ "hostname",++ "kind",++ "status",++ "served",++ "verified_at",++ "last_checked_at",++ "last_error"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^dom_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "site_id": {++ "type": "string",++ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "hostname": {++ "type": "string"++ },++ "kind": {++ "enum": [++ "default",++ "custom"++ ]++ },++ "status": {++ "enum": [++ "pending",++ "verified",++ "failed",++ "lapsed"++ ]++ },++ "served": {++ "type": "boolean"++ },++ "verified_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "last_checked_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "last_error": {++ "type": [++ "string",++ "null"++ ]++ },++ "instructions": {++ "type": "object",++ "required": [++ "verification",++ "routing",++ "tls"++ ],++ "properties": {++ "verification": {++ "type": "object",++ "required": [++ "type",++ "name",++ "value"++ ],++ "properties": {++ "type": {++ "const": "TXT"++ },++ "name": {++ "type": "string",++ "description": "_openvibe-space.<hostname>"++ },++ "value": {++ "type": "string",++ "description": "openvibe-space-verification=<token>"++ },++ "note": {++ "type": "string"++ }++ }++ },++ "routing": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "type",++ "name",++ "value"++ ],++ "properties": {++ "type": {++ "type": "string"++ },++ "name": {++ "type": "string"++ },++ "value": {++ "type": "string"++ },++ "note": {++ "type": "string"++ }++ }++ },++ "description": "A CNAME for a subdomain; A/AAAA for an apex domain when the operator configured them."++ },++ "tls": {++ "type": "string"++ }++ }++ }++ }++ },++ "space.project.v1": {++ "title": "SpaceProject",++ "description": "space.project@1: one OpenVibe.Space tenant project (server/http/serialize.js project): owned by a person (usr_…), production or sandbox, with the caller's role in it and any staff takedown. POST /api/v1/projects and GET /api/v1/projects/:id add the effective quota and the usage.",++ "type": "object",++ "required": [++ "id",++ "name",++ "environment",++ "network_project_id",++ "owner",++ "role",++ "created_at",++ "takedown"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "name": {++ "type": "string",++ "minLength": 1,++ "maxLength": 80++ },++ "environment": {++ "enum": [++ "production",++ "sandbox"++ ]++ },++ "network_project_id": {++ "type": [++ "string",++ "null"++ ],++ "description": "The OpenVibe.Network project once Network has projects."++ },++ "owner": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "role": {++ "enum": [++ "owner",++ "maintainer",++ "deployer",++ "staff",++ null++ ],++ "description": "The caller's role (staff: a Network admin who is not a member)."++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "takedown": {++ "type": [++ "object",++ "null"++ ],++ "required": [++ "scope",++ "reason",++ "since"++ ],++ "description": "A staff takedown covering it: serving stops while the content is kept for review.",++ "properties": {++ "scope": {++ "enum": [++ "project",++ "site"++ ]++ },++ "reason": {++ "type": "string"++ },++ "since": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ },++ "quota": {++ "type": "object",++ "required": [++ "storage_bytes",++ "deploys_per_day",++ "max_files",++ "max_file_bytes",++ "sites",++ "custom_domains"++ ],++ "description": "The effective quota: the project's override where set, else its environment's default.",++ "properties": {++ "storage_bytes": {++ "type": "integer",++ "minimum": 0++ },++ "deploys_per_day": {++ "type": "integer",++ "minimum": 0++ },++ "max_files": {++ "type": "integer",++ "minimum": 0++ },++ "max_file_bytes": {++ "type": "integer",++ "minimum": 0++ },++ "sites": {++ "type": "integer",++ "minimum": 0++ },++ "custom_domains": {++ "type": "integer",++ "minimum": 0++ }++ }++ },++ "usage": {++ "type": "object",++ "required": [++ "storage_bytes",++ "objects",++ "deploys_last_24h",++ "sites",++ "custom_domains"++ ],++ "properties": {++ "storage_bytes": {++ "type": "integer",++ "minimum": 0++ },++ "objects": {++ "type": "integer",++ "minimum": 0++ },++ "deploys_last_24h": {++ "type": "integer",++ "minimum": 0++ },++ "sites": {++ "type": "integer",++ "minimum": 0++ },++ "custom_domains": {++ "type": "integer",++ "minimum": 0++ }++ }++ }++ }++ },++ "space.site-config-request.v1": {++ "title": "SpaceSiteConfigRequest",++ "description": "space.site-config-request@1: the body of PUT /api/v1/sites/:id/config on OpenVibe.Space (space.site.config; the acting principal must be a maintainer or owner of the site's project). { headers?, redirects?, spa? } replaces the whole configuration: a field left out is reset to its default. A redirect's status defaults to 301. Refusals are 422 problem+json: site_config.reserved_header, site_config.header_name, site_config.header_value (longer than 1024 characters or a control character), site_config.too_many (over 50 headers or 200 redirects), site_config.redirect (not a local path, a duplicate from, or a status outside 301/302/307/308), site_config.headers and site_config.redirects (wrong type). GET and DELETE /api/v1/sites/:id/config take no body (DELETE resets to the defaults).",++ "type": "object",++ "additionalProperties": false,++ "properties": {++ "headers": {++ "type": "object",++ "additionalProperties": {++ "type": "string"++ }++ },++ "redirects": {++ "type": "array",++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "from",++ "to"++ ],++ "properties": {++ "from": {++ "type": "string"++ },++ "to": {++ "type": "string"++ },++ "status": {++ "enum": [++ 301,++ 302,++ 307,++ 308++ ]++ }++ }++ }++ },++ "spa": {++ "type": "boolean"++ }++ }++ },++ "space.site-config-result.v1": {++ "title": "SpaceSiteConfigResult",++ "description": "space.site-config-result@1: every answer of space.site.config on OpenVibe.Space. GET, PUT and DELETE /api/v1/sites/:id/config → { config } (after a PUT, the configuration as stored; after a DELETE, the defaults).",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "config"++ ],++ "properties": {++ "config": {++ "$ref": "#/components/schemas/space.site-config.v1"++ }++ }++ },++ "space.site-config.v1": {++ "title": "SpaceSiteConfig",++ "description": "space.site-config@1: one Host site's serving configuration (plan T12 J3). headers are applied to every response of the site before the platform's own, which always win; the platform's security, routing, caching, scope and transport headers (Content-Security-Policy, Strict-Transport-Security, Set-Cookie, Cache-Control, Content-Type, Location, Service-Worker-Allowed, Alt-Svc and the rest of Host's reserved list) and every X-Forwarded-* header are refused. redirects are local paths only (no scheme, host, protocol-relative URL, backslash or control character, also after percent-decoding). spa serves /index.html for an extensionless path that has no file. A site with no configuration answers { headers: {}, redirects: [], spa: false }.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "headers",++ "redirects",++ "spa"++ ],++ "properties": {++ "headers": {++ "type": "object",++ "maxProperties": 50,++ "propertyNames": {++ "pattern": "^[A-Za-z0-9][A-Za-z0-9-]{0,63}$"++ },++ "additionalProperties": {++ "type": "string",++ "maxLength": 1024++ }++ },++ "redirects": {++ "type": "array",++ "maxItems": 200,++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "from",++ "to",++ "status"++ ],++ "properties": {++ "from": {++ "type": "string",++ "pattern": "^/(?!/)",++ "maxLength": 1024++ },++ "to": {++ "type": "string",++ "pattern": "^/(?!/)",++ "maxLength": 1024++ },++ "status": {++ "enum": [++ 301,++ 302,++ 307,++ 308++ ]++ }++ }++ }++ },++ "spa": {++ "type": "boolean"++ }++ }++ },++ "space.site-manage-request.v1": {++ "title": "SpaceSiteManageRequest",++ "description": "space.site-manage-request@1: bodies of space.site.manage on OpenVibe.Space (judged by the acting principal's project role too; a sandbox token is refused on production projects). POST /api/v1/projects { name, environment?, network_project_id? } creates a project owned by the person named with X-OV-Subject (403 project.owner_must_be_user; 422 project.invalid_name over 80 characters, project.invalid_environment; 429 quota.projects; 409 project.network_project_taken). PUT /api/v1/projects/:id/quota { storage_bytes?, deploys_per_day?, max_files?, max_file_bytes?, sites?, custom_domains? } (a Network staff user: 403 auth.staff_only; fields left out keep their value). PUT /api/v1/projects/:id/members/:principal { role } (the owner: 422 member.invalid_role, 409 member.owner_fixed). POST /api/v1/projects/:id/sites { name } (a maintainer; lowercased and trimmed, one DNS label of 3–40 characters: 422 site.invalid_name, site.name_reserved; 409 site.name_taken, site.name_held; 429 quota.sites). POST /api/v1/projects/:id/takedown and /sites/:id/takedown { reason } (staff: stop serving, keep the content; 409 takedown.exists); DELETE on those { note? } lifts it. GET /api/v1/projects, /projects/:id, /projects/:id/quota, /projects/:id/sites and /sites/:id, and DELETE /api/v1/projects/:id, /projects/:id/members/:principal, /sites/:id and /deploys/:id take no body (deleting refuses taken-down content for non-staff: 409 site.taken_down; an active deploy: 409 deploy.active).",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "name"++ ],++ "properties": {++ "name": {++ "type": "string",++ "minLength": 1,++ "description": "1–80 characters once trimmed."++ },++ "environment": {++ "enum": [++ "production",++ "sandbox",++ null++ ],++ "description": "Default production; a sandbox credential creates sandbox projects only."++ },++ "network_project_id": {++ "anyOf": [++ {++ "type": "string",++ "pattern": "^([A-Za-z0-9_:-]{3,80})?$"++ },++ {++ "type": "null"++ }++ ]++ }++ }++ },++ {++ "type": "object",++ "properties": {++ "storage_bytes": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 0,++ "description": "A non-negative integer, or null for the environment default."++ },++ "deploys_per_day": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 0,++ "description": "A non-negative integer, or null for the environment default."++ },++ "max_files": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 0,++ "description": "A non-negative integer, or null for the environment default."++ },++ "max_file_bytes": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 0,++ "description": "A non-negative integer, or null for the environment default."++ },++ "sites": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 0,++ "description": "A non-negative integer, or null for the environment default."++ },++ "custom_domains": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 0,++ "description": "A non-negative integer, or null for the environment default."++ }++ }++ },++ {++ "type": "object",++ "required": [++ "role"++ ],++ "properties": {++ "role": {++ "enum": [++ "owner",++ "maintainer",++ "deployer"++ ],++ "description": "Only a person (usr_…) can be an owner."++ }++ }++ },++ {++ "type": "object",++ "required": [++ "name"++ ],++ "properties": {++ "name": {++ "type": "string",++ "pattern": "^\\s*[A-Za-z0-9](?:[A-Za-z0-9]|-(?=[A-Za-z0-9])){2,39}\\s*$",++ "description": "The site name (lowercased)."++ }++ }++ },++ {++ "type": "object",++ "required": [++ "reason"++ ],++ "properties": {++ "reason": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500,++ "description": "Members see it."++ }++ }++ },++ {++ "type": "object",++ "properties": {++ "note": {++ "type": [++ "string",++ "null"++ ],++ "description": "Cut to 500 characters."++ }++ }++ },++ {++ "$ref": "#/components/schemas/common.no-body.v1"++ }++ ]++ },++ "space.site-manage-result.v1": {++ "title": "SpaceSiteManageResult",++ "description": "space.site-manage-result@1: answers of space.site.manage on OpenVibe.Space. GET /api/v1/projects → { projects } (the caller's projects with their role). POST /api/v1/projects → 201 { project } (with quota and usage). GET /api/v1/projects/:id → { project, sites, members }. GET and PUT /api/v1/projects/:id/quota → { quota, usage }. PUT and DELETE /api/v1/projects/:id/members/:principal → { members }. GET /api/v1/projects/:id/sites → { sites }. POST /api/v1/projects/:id/sites → 201 { site }. GET /api/v1/sites/:id → { site, domains }. DELETE /api/v1/projects/:id and /sites/:id → { deleted: true } (serving stops at once; the objects are removed). DELETE /api/v1/deploys/:id → { deleted: true, objects_removed }. POST …/takedown → 201 { takedown }; DELETE …/takedown → { lifted: true }.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "projects"++ ],++ "properties": {++ "projects": {++ "type": "array",++ "items": {++ "$ref": "#/components/schemas/space.project.v1"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "project"++ ],++ "properties": {++ "project": {++ "$ref": "#/components/schemas/space.project.v1"++ },++ "sites": {++ "type": "array",++ "items": {++ "$ref": "#/components/schemas/space.site.v1"++ }++ },++ "members": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "principal",++ "role",++ "added_at"++ ],++ "properties": {++ "principal": {++ "type": "string",++ "description": "usr_…, app:app_… or svc:…"++ },++ "role": {++ "enum": [++ "owner",++ "maintainer",++ "deployer"++ ]++ },++ "added_at": {++ "type": "string",++ "format": "date-time"++ }++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "quota",++ "usage"++ ],++ "properties": {++ "quota": {++ "type": "object",++ "required": [++ "storage_bytes",++ "deploys_per_day",++ "max_files",++ "max_file_bytes",++ "sites",++ "custom_domains"++ ],++ "description": "The effective quota: the project's override where set, else its environment's default.",++ "properties": {++ "storage_bytes": {++ "type": "integer",++ "minimum": 0++ },++ "deploys_per_day": {++ "type": "integer",++ "minimum": 0++ },++ "max_files": {++ "type": "integer",++ "minimum": 0++ },++ "max_file_bytes": {++ "type": "integer",++ "minimum": 0++ },++ "sites": {++ "type": "integer",++ "minimum": 0++ },++ "custom_domains": {++ "type": "integer",++ "minimum": 0++ }++ }++ },++ "usage": {++ "type": "object",++ "required": [++ "storage_bytes",++ "objects",++ "deploys_last_24h",++ "sites",++ "custom_domains"++ ],++ "properties": {++ "storage_bytes": {++ "type": "integer",++ "minimum": 0++ },++ "objects": {++ "type": "integer",++ "minimum": 0++ },++ "deploys_last_24h": {++ "type": "integer",++ "minimum": 0++ },++ "sites": {++ "type": "integer",++ "minimum": 0++ },++ "custom_domains": {++ "type": "integer",++ "minimum": 0++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "members"++ ],++ "properties": {++ "members": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "principal",++ "role",++ "added_at"++ ],++ "properties": {++ "principal": {++ "type": "string",++ "description": "usr_…, app:app_… or svc:…"++ },++ "role": {++ "enum": [++ "owner",++ "maintainer",++ "deployer"++ ]++ },++ "added_at": {++ "type": "string",++ "format": "date-time"++ }++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "sites"++ ],++ "properties": {++ "sites": {++ "type": "array",++ "items": {++ "$ref": "#/components/schemas/space.site.v1"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "site"++ ],++ "properties": {++ "site": {++ "$ref": "#/components/schemas/space.site.v1"++ },++ "domains": {++ "type": "array",++ "items": {++ "$ref": "#/components/schemas/space.domain.v1"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "deleted"++ ],++ "properties": {++ "deleted": {++ "const": true++ },++ "objects_removed": {++ "type": "integer",++ "minimum": 0++ }++ }++ },++ {++ "type": "object",++ "required": [++ "takedown"++ ],++ "properties": {++ "takedown": {++ "type": "object",++ "required": [++ "scope",++ "reason",++ "since"++ ],++ "description": "The takedown now in force.",++ "properties": {++ "scope": {++ "enum": [++ "project",++ "site"++ ]++ },++ "reason": {++ "type": "string"++ },++ "since": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "lifted"++ ],++ "properties": {++ "lifted": {++ "const": true++ }++ }++ }++ ]++ },++ "space.site.v1": {++ "title": "SpaceSite",++ "description": "space.site@1: one static site on OpenVibe.Space (server/http/serialize.js site): its name (one DNS label), the default hostname and URL it is served at, the deploy it serves (one pointer; null until a deploy is activated) and any staff takedown.",++ "type": "object",++ "required": [++ "id",++ "project_id",++ "name",++ "hostname",++ "url",++ "active_deploy_id",++ "takedown",++ "created_at",++ "updated_at"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "name": {++ "type": "string",++ "pattern": "^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){2,39}$"++ },++ "hostname": {++ "type": "string"++ },++ "url": {++ "type": "string"++ },++ "active_deploy_id": {++ "anyOf": [++ {++ "type": "string",++ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ {++ "type": "null"++ }++ ]++ },++ "takedown": {++ "type": [++ "object",++ "null"++ ],++ "required": [++ "scope",++ "reason",++ "since"++ ],++ "description": "A staff takedown covering it: serving stops while the content is kept for review.",++ "properties": {++ "scope": {++ "enum": [++ "project",++ "site"++ ]++ },++ "reason": {++ "type": "string"++ },++ "since": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "updated_at": {++ "type": "string",++ "format": "date-time"++ }++ }++ }++ },++ "securitySchemes": {++ "openvibeToken": {++ "type": "http",++ "scheme": "bearer",++ "bearerFormat": "JWT",++ "description": "An OpenVibe Network token for this service: a person's token, an app token (client credentials, audience of the service) or a first-party service token. Its `cap` claim must include the capability the operation names."++ }++ }++ }++}@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Tips API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Tools API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Trade API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.VIP API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Watch API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Wiki API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Work API",-- "version": "0.148.0",-- "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.149.0",++ "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -173071,3 +173071,516 @@ export type FoodRecipeResult =FoodRecipeSuggestion];};++++/** [email protected] (owner: space) */++/**++ * space.deploy-create-request@1: bodies of space.deploy.create on OpenVibe.Space (a deployer or above of the project; an app principal added as deployer can deploy from CI). POST /api/v1/sites/:id/deploys uploads a deploy: the raw body is a tar or tar.gz archive (Content-Type application/gzip, application/x-gzip, application/x-tar or application/octet-stream), or multipart/form-data with archive=<one archive> or files=<file>… (each part's filename is its path); optional fields activate=1, root=<subdirectory to deploy> and strip=folder (removes a shared first segment) also work as query parameters. It is validated (path traversal, links, special and hidden files, server-side code, file types, sizes and quotas) and never executed; a refusal is a problem whose extra carries the failed deploy_id and its log (413 upload.too_large, quota.storage; 422 validation codes; 429 quota.deploys_per_day; 403 site.taken_down; 503 upload.busy; 507 storage.host_full). POST /api/v1/deploys/:id/activate { expected_active? } and POST /api/v1/sites/:id/rollback { deploy_id?, expected_active? } switch the site's pointer in one step (rollback without deploy_id goes to the deploy active before; 409 deploy.not_ready, site.active_changed when expected_active is stale, deploy.no_previous). GET /api/v1/sites/:id/deploys, /deploys/:id and /deploys/:id/log take no body.++ */++export type SpaceDeployCreateRequest =++ | string++ | {++ deploy_id?: string | null;++ /**++ * The deploy you saw active (null: none); refused with 409 site.active_changed when it moved.++ */++ expected_active?: string | null;++ }++ | NoBody;++++/** [email protected] (owner: space) */++/**++ * space.deploy-create-result@1: answers of space.deploy.create on OpenVibe.Space. GET /api/v1/sites/:id/deploys?limit= → { active_deploy_id, deploys, activations } (newest first; the last 20 pointer switches). POST /api/v1/sites/:id/deploys → 201 { deploy, activated, url } (the ready deploy with its validation log; activated when activate=1 switched the site to it). GET /api/v1/deploys/:id → { deploy } with its files. GET /api/v1/deploys/:id/log → { deploy_id, state, log }. POST /api/v1/deploys/:id/activate and /sites/:id/rollback → { active_deploy_id, previous_deploy_id, changed } (changed false when it was already active). Emits space.deploy.created|activated|failed.++ */++export type SpaceDeployCreateResult =++ | {++ active_deploy_id: string | null;++ deploys: SpaceDeploy[];++ activations: {++ deploy_id: string;++ previous_deploy_id: string | null;++ kind: "activate" | "rollback";++ actor: string;++ at: string;++ }[];++ }++ | {++ deploy: SpaceDeploy;++ activated?: boolean;++ url?: string;++ }++ | {++ deploy_id: string;++ state: "ready" | "failed" | "deleted";++ log: {++ level: "info" | "warn" | "error";++ message: string;++ at: string;++ }[];++ }++ | {++ active_deploy_id: string;++ previous_deploy_id: string | null;++ changed: boolean;++ };++++/** [email protected] (owner: space) */++/**++ * space.deploy@1: one immutable static deploy on OpenVibe.Space (server/http/serialize.js deploy): ready (validated and stored, never executed) or failed (refused, with its failure code and log), whether the site serves it, its size and manifest hash. GET /api/v1/deploys/:id adds the files; the upload answer adds the log.++ */++export interface SpaceDeploy {++ id: string;++ site_id: string;++ project_id: string;++ state: "ready" | "failed" | "deleted";++ active: boolean;++ source: "archive" | "files" | "preview" | "git";++ /**++ * Present on source git: the commit this deploy was built from (public provenance, never a credential).++ */++ git?: {++ provider: string;++ repo_url: string;++ ref: string;++ commit_sha: string;++ };++ file_count: number;++ total_bytes: number;++ /**++ * Bytes this deploy added to the project's store.++ */++ new_bytes: number;++ manifest_sha256: string | null;++ failure_code: string | null;++ created_by: string;++ created_at: string;++ files?: {++ path: string;++ sha256: string;++ size: number;++ content_type: string;++ }[];++ log?: {++ level: "info" | "warn" | "error";++ message: string;++ at: string;++ }[];++}++++/** [email protected] (owner: space) */++/**++ * space.domain-manage-request@1: bodies of space.domain.manage on OpenVibe.Space (a maintainer or above of the project). POST /api/v1/sites/:id/domains { hostname } adds a custom domain, pending until its TXT record is verified: a fully qualified host name, lowercased (422 domain.invalid_hostname; 422 domain.reserved for any OpenVibe domain; 403 domain.sandbox in sandbox projects; 429 quota.custom_domains; 409 domain.exists, domain.taken). GET /api/v1/sites/:id/domains, POST /api/v1/domains/:id/verify (checks the TXT record now) and DELETE /api/v1/domains/:id (409 domain.default: the default domain goes only with the site) take no body.++ */++export type SpaceDomainManageRequest =++ | {++ /**++ * e.g. www.example.org (a trailing dot is dropped).++ */++ hostname: string;++ }++ | NoBody;++++/** [email protected] (owner: space) */++/**++ * space.domain-manage-result@1: answers of space.domain.manage on OpenVibe.Space. GET /api/v1/sites/:id/domains → { domains } (the default domain first). POST /api/v1/sites/:id/domains → 201 { domain } with the DNS records to publish. POST /api/v1/domains/:id/verify → { domain } after the check (status verified and served once the TXT record matches, else last_error says what was found; emits space.domain.verified). DELETE /api/v1/domains/:id → { deleted: true }.++ */++export type SpaceDomainManageResult =++ | {++ domains: SpaceDomain[];++ }++ | {++ domain: SpaceDomain;++ }++ | {++ deleted: true;++ };++++/** [email protected] (owner: space) */++/**++ * space.domain@1: one domain of a static site on OpenVibe.Space (server/http/serialize.js domain): the default <site>.openvibe.website, or a custom domain that is served only once its DNS TXT record is verified (re-checked daily; it lapses when the record is gone). A custom domain carries the records its owner must publish (the TXT verification value is public by design and shown only to members); TLS certificates are issued by an operator.++ */++export interface SpaceDomain {++ id: string;++ site_id: string;++ hostname: string;++ kind: "default" | "custom";++ status: "pending" | "verified" | "failed" | "lapsed";++ served: boolean;++ verified_at: string | null;++ last_checked_at: string | null;++ last_error: string | null;++ instructions?: {++ verification: {++ type: "TXT";++ /**++ * _openvibe-space.<hostname>++ */++ name: string;++ /**++ * openvibe-space-verification=<token>++ */++ value: string;++ note?: string;++ };++ /**++ * A CNAME for a subdomain; A/AAAA for an apex domain when the operator configured them.++ */++ routing: {++ type: string;++ name: string;++ value: string;++ note?: string;++ }[];++ tls: string;++ };++}++++/** [email protected] (owner: space) */++/**++ * space.project@1: one OpenVibe.Space tenant project (server/http/serialize.js project): owned by a person (usr_…), production or sandbox, with the caller's role in it and any staff takedown. POST /api/v1/projects and GET /api/v1/projects/:id add the effective quota and the usage.++ */++export interface SpaceProject {++ id: string;++ name: string;++ environment: "production" | "sandbox";++ /**++ * The OpenVibe.Network project once Network has projects.++ */++ network_project_id: string | null;++ owner: string;++ /**++ * The caller's role (staff: a Network admin who is not a member).++ */++ role: "owner" | "maintainer" | "deployer" | "staff" | null;++ created_at: string;++ /**++ * A staff takedown covering it: serving stops while the content is kept for review.++ */++ takedown: {++ scope: "project" | "site";++ reason: string;++ since: string | null;++ } | null;++ /**++ * The effective quota: the project's override where set, else its environment's default.++ */++ quota?: {++ storage_bytes: number;++ deploys_per_day: number;++ max_files: number;++ max_file_bytes: number;++ sites: number;++ custom_domains: number;++ };++ usage?: {++ storage_bytes: number;++ objects: number;++ deploys_last_24h: number;++ sites: number;++ custom_domains: number;++ };++}++++/** [email protected] (owner: space) */++/**++ * space.site-manage-request@1: bodies of space.site.manage on OpenVibe.Space (judged by the acting principal's project role too; a sandbox token is refused on production projects). POST /api/v1/projects { name, environment?, network_project_id? } creates a project owned by the person named with X-OV-Subject (403 project.owner_must_be_user; 422 project.invalid_name over 80 characters, project.invalid_environment; 429 quota.projects; 409 project.network_project_taken). PUT /api/v1/projects/:id/quota { storage_bytes?, deploys_per_day?, max_files?, max_file_bytes?, sites?, custom_domains? } (a Network staff user: 403 auth.staff_only; fields left out keep their value). PUT /api/v1/projects/:id/members/:principal { role } (the owner: 422 member.invalid_role, 409 member.owner_fixed). POST /api/v1/projects/:id/sites { name } (a maintainer; lowercased and trimmed, one DNS label of 3–40 characters: 422 site.invalid_name, site.name_reserved; 409 site.name_taken, site.name_held; 429 quota.sites). POST /api/v1/projects/:id/takedown and /sites/:id/takedown { reason } (staff: stop serving, keep the content; 409 takedown.exists); DELETE on those { note? } lifts it. GET /api/v1/projects, /projects/:id, /projects/:id/quota, /projects/:id/sites and /sites/:id, and DELETE /api/v1/projects/:id, /projects/:id/members/:principal, /sites/:id and /deploys/:id take no body (deleting refuses taken-down content for non-staff: 409 site.taken_down; an active deploy: 409 deploy.active).++ */++export type SpaceSiteManageRequest =++ | {++ /**++ * 1–80 characters once trimmed.++ */++ name: string;++ /**++ * Default production; a sandbox credential creates sandbox projects only.++ */++ environment?: "production" | "sandbox" | null;++ network_project_id?: string | null;++ }++ | {++ /**++ * A non-negative integer, or null for the environment default.++ */++ storage_bytes?: number | null;++ /**++ * A non-negative integer, or null for the environment default.++ */++ deploys_per_day?: number | null;++ /**++ * A non-negative integer, or null for the environment default.++ */++ max_files?: number | null;++ /**++ * A non-negative integer, or null for the environment default.++ */++ max_file_bytes?: number | null;++ /**++ * A non-negative integer, or null for the environment default.++ */++ sites?: number | null;++ /**++ * A non-negative integer, or null for the environment default.++ */++ custom_domains?: number | null;++ }++ | {++ /**++ * Only a person (usr_…) can be an owner.++ */++ role: "owner" | "maintainer" | "deployer";++ }++ | {++ /**++ * The site name (lowercased).++ */++ name: string;++ }++ | {++ /**++ * Members see it.++ */++ reason: string;++ }++ | {++ /**++ * Cut to 500 characters.++ */++ note?: string | null;++ }++ | NoBody;++++/** [email protected] (owner: space) */++/**++ * space.site-manage-result@1: answers of space.site.manage on OpenVibe.Space. GET /api/v1/projects → { projects } (the caller's projects with their role). POST /api/v1/projects → 201 { project } (with quota and usage). GET /api/v1/projects/:id → { project, sites, members }. GET and PUT /api/v1/projects/:id/quota → { quota, usage }. PUT and DELETE /api/v1/projects/:id/members/:principal → { members }. GET /api/v1/projects/:id/sites → { sites }. POST /api/v1/projects/:id/sites → 201 { site }. GET /api/v1/sites/:id → { site, domains }. DELETE /api/v1/projects/:id and /sites/:id → { deleted: true } (serving stops at once; the objects are removed). DELETE /api/v1/deploys/:id → { deleted: true, objects_removed }. POST …/takedown → 201 { takedown }; DELETE …/takedown → { lifted: true }.++ */++export type SpaceSiteManageResult =++ | {++ projects: SpaceProject[];++ }++ | {++ project: SpaceProject;++ sites?: SpaceSite[];++ members?: {++ /**++ * usr_…, app:app_… or svc:…++ */++ principal: string;++ role: "owner" | "maintainer" | "deployer";++ added_at: string;++ }[];++ }++ | {++ /**++ * The effective quota: the project's override where set, else its environment's default.++ */++ quota: {++ storage_bytes: number;++ deploys_per_day: number;++ max_files: number;++ max_file_bytes: number;++ sites: number;++ custom_domains: number;++ };++ usage: {++ storage_bytes: number;++ objects: number;++ deploys_last_24h: number;++ sites: number;++ custom_domains: number;++ };++ }++ | {++ members: {++ /**++ * usr_…, app:app_… or svc:…++ */++ principal: string;++ role: "owner" | "maintainer" | "deployer";++ added_at: string;++ }[];++ }++ | {++ sites: SpaceSite[];++ }++ | {++ site: SpaceSite;++ domains?: SpaceDomain[];++ }++ | {++ deleted: true;++ objects_removed?: number;++ }++ | {++ /**++ * The takedown now in force.++ */++ takedown: {++ scope: "project" | "site";++ reason: string;++ since: string | null;++ };++ }++ | {++ lifted: true;++ };++++/** [email protected] (owner: space) */++/**++ * space.site@1: one static site on OpenVibe.Space (server/http/serialize.js site): its name (one DNS label), the default hostname and URL it is served at, the deploy it serves (one pointer; null until a deploy is activated) and any staff takedown.++ */++export interface SpaceSite {++ id: string;++ project_id: string;++ name: string;++ hostname: string;++ url: string;++ active_deploy_id: string | null;++ /**++ * A staff takedown covering it: serving stops while the content is kept for review.++ */++ takedown: {++ scope: "project" | "site";++ reason: string;++ since: string | null;++ } | null;++ created_at: string;++ updated_at: string;++}++++/** [email protected] (owner: space) */++/**++ * space.site-config@1: one Host site's serving configuration (plan T12 J3). headers are applied to every response of the site before the platform's own, which always win; the platform's security, routing, caching, scope and transport headers (Content-Security-Policy, Strict-Transport-Security, Set-Cookie, Cache-Control, Content-Type, Location, Service-Worker-Allowed, Alt-Svc and the rest of Host's reserved list) and every X-Forwarded-* header are refused. redirects are local paths only (no scheme, host, protocol-relative URL, backslash or control character, also after percent-decoding). spa serves /index.html for an extensionless path that has no file. A site with no configuration answers { headers: {}, redirects: [], spa: false }.++ */++export interface SpaceSiteConfig {++ headers: {++ [k: string]: string | undefined;++ };++ /**++ * @maxItems 200++ */++ redirects: {++ from: string;++ to: string;++ status: 301 | 302 | 307 | 308;++ }[];++ spa: boolean;++}++++/** [email protected] (owner: space) */++/**++ * space.site-config-request@1: the body of PUT /api/v1/sites/:id/config on OpenVibe.Space (space.site.config; the acting principal must be a maintainer or owner of the site's project). { headers?, redirects?, spa? } replaces the whole configuration: a field left out is reset to its default. A redirect's status defaults to 301. Refusals are 422 problem+json: site_config.reserved_header, site_config.header_name, site_config.header_value (longer than 1024 characters or a control character), site_config.too_many (over 50 headers or 200 redirects), site_config.redirect (not a local path, a duplicate from, or a status outside 301/302/307/308), site_config.headers and site_config.redirects (wrong type). GET and DELETE /api/v1/sites/:id/config take no body (DELETE resets to the defaults).++ */++export interface SpaceSiteConfigRequest {++ headers?: {++ [k: string]: string | undefined;++ };++ redirects?: {++ from: string;++ to: string;++ status?: 301 | 302 | 307 | 308;++ }[];++ spa?: boolean;++}++++/** [email protected] (owner: space) */++/**++ * space.site-config-result@1: every answer of space.site.config on OpenVibe.Space. GET, PUT and DELETE /api/v1/sites/:id/config → { config } (after a PUT, the configuration as stored; after a DELETE, the defaults).++ */++export interface SpaceSiteConfigResult {++ config: SpaceSiteConfig;++}++++/** [email protected] (owner: space) */++/**++ * space.deploy.activated v1 (OpenVibe.Space server/domain/deploys.js switchPointer, Stage B tenant hosting). A tenant site's active deploy changed, written into Host's event outbox in the transaction that moves the pointer (activate or rollback). Envelope: subject { type: deploy, id: <deploy id> }, visibility internal, priority low, actor the person or app that switched it. A network service's release going live is space.release.published, not this.++ */++export interface SpaceDeployActivatedPayload {++ project_id: string;++ site_id: string;++ /**++ * The site's name (<site>.openvibe.website).++ */++ site: string;++ deploy_id: string;++ /**++ * The deploy that served before; null for the site's first activation.++ */++ previous_deploy_id: string | null;++ /**++ * true when the switch was a rollback.++ */++ rollback: boolean;++}++++/** [email protected] (owner: space) */++/**++ * space.deploy.created v1 (OpenVibe.Space, subject deploy dpl_…, visibility internal): an upload or a Git ingest became an immutable, content-addressed deploy in the ready state. It is not yet serving: space.deploy.activated follows when it is made active. No file contents or paths.++ */++export interface SpaceDeployCreatedPayload {++ project_id: string;++ site_id: string;++ /**++ * the site name (one DNS label; the site is served at <site>.openvibe.website)++ */++ site: string;++ /**++ * how the deploy arrived: an archive or individual files, a private preview upload, or a Git ingest (always a preview)++ */++ source: "archive" | "files" | "preview" | "git";++ file_count: number;++ total_bytes: number;++ /**++ * sha256 of the deploy manifest (the content address of the whole deploy)++ */++ manifest_sha256: string;++}++++/** [email protected] (owner: space) */++/**++ * space.deploy.failed v1 (OpenVibe.Space, subject deploy dpl_…, visibility internal): an upload was refused and recorded as a failed deploy with its log, so the owner can see why. Problem codes only, at most ten; never file contents.++ */++export interface SpaceDeployFailedPayload {++ project_id: string;++ site_id: string;++ /**++ * the site name (one DNS label; the site is served at <site>.openvibe.website)++ */++ site: string;++ /**++ * the refusal problem code (for example deploy.too_large or storage.space_full)++ */++ code: string;++ /**++ * the problem codes of the files that were refused++ *++ * @maxItems 10++ */++ problems:++ | []++ | [string]++ | [string, string]++ | [string, string, string]++ | [string, string, string, string]++ | [string, string, string, string, string]++ | [string, string, string, string, string, string]++ | [string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string, string];++}++++/** [email protected] (owner: space) */++/**++ * space.domain.verified v1 (OpenVibe.Space, subject domain dom_…, visibility internal): a custom domain's TXT record (_openvibe-space.<hostname>) was found, and the domain now serves the site.++ */++export interface SpaceDomainVerifiedPayload {++ project_id: string;++ site_id: string;++ /**++ * the site name (one DNS label; the site is served at <site>.openvibe.website)++ */++ site: string;++ /**++ * the custom domain, a fully qualified host name in lower case++ */++ hostname: string;++}nnew file mode 100644@@@ -0,0 +1,31 @@++{++ "id": "space.deploy.create",++ "version": "1.0",++ "owner": "space",++ "status": "active",++ "visibility": "first-party",++ "description": "Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys and their upload logs, activate a deploy and roll a site back to any earlier ready deploy (one atomic pointer switch). Also judged by the acting principal's project role (deployer or above); an app principal added to a project as deployer can deploy from CI. A site with a Git source also accepts a CI-built deploy at /sites/:id/source/deploys naming the ref and full commit_sha; it is always a preview and records that commit as immutable provenance.",++ "permissions": [++ "host:deploy"++ ],++ "resourceConstraints": [++ "project"++ ],++ "quotaClass": "space-deploy",++ "events": [++ "space.deploy.created",++ "space.deploy.activated",++ "space.deploy.failed"++ ],++ "implementedBy": [++ "GET /api/v1/sites/:id/deploys",++ "POST /api/v1/sites/:id/deploys",++ "POST /api/v1/sites/:id/source/deploys",++ "GET /api/v1/deploys/:id",++ "GET /api/v1/deploys/:id/log",++ "POST /api/v1/deploys/:id/activate",++ "POST /api/v1/sites/:id/rollback"++ ],++ "inputSchema": "space.deploy-create-request@1",++ "outputSchema": "space.deploy-create-result@1"++}nnew file mode 100644@@@ -0,0 +1,26 @@++{++ "id": "space.domain.manage",++ "version": "1.0",++ "owner": "space",++ "status": "active",++ "visibility": "first-party",++ "description": "Add a custom domain to a static site (never an OpenVibe domain; not in sandbox projects), read its DNS TXT verification and routing records, trigger a verification check, and remove it. A custom domain is served only after its TXT record is verified; TLS certificates are issued by an operator and never pass through this capability.",++ "permissions": [++ "space:write"++ ],++ "resourceConstraints": [++ "project"++ ],++ "quotaClass": "space-manage",++ "events": [++ "space.domain.verified"++ ],++ "implementedBy": [++ "GET /api/v1/sites/:id/domains",++ "POST /api/v1/sites/:id/domains",++ "POST /api/v1/domains/:id/verify",++ "DELETE /api/v1/domains/:id"++ ],++ "inputSchema": "space.domain-manage-request@1",++ "outputSchema": "space.domain-manage-result@1"++}nnew file mode 100644@@@ -0,0 +1,22 @@++{++ "id": "space.resource.read",++ "version": "1.0",++ "owner": "space",++ "status": "active",++ "visibility": "first-party",++ "description": "Read OpenVibe.Space's resource index (ADR-048): GET /api/v1/resources lists the resources Host owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:ovrn reads one by its OVRN (common.resource-summary@1). Every authority serves these for OpenVibe.Services to fan out and merge; Host's own resources are its static sites (kind space.site, id sit_<ULID>), the immutable deploys of those sites (kind space.deploy, id dpl_<ULID>) and their default and custom domains (kind space.domain, id dom_<ULID>), each with the project_id of the Network project that owns it. Host lists no projects; only Network lists projects. Planned until Host ships the index.",++ "permissions": [++ "resources:read"++ ],++ "resourceConstraints": [++ "none"++ ],++ "quotaClass": "standard",++ "events": [],++ "implementedBy": [++ "GET /api/v1/resources",++ "GET /api/v1/resources/:ovrn"++ ],++ "inputSchema": "common.no-body@1",++ "outputSchema": "common.resource-list-result@1"++}nnew file mode 100644@@@ -0,0 +1,23 @@++{++ "id": "space.site.config",++ "version": "1.0",++ "owner": "space",++ "status": "active",++ "visibility": "first-party",++ "description": "Read, set and reset a Host site's serving configuration: response headers, local-only redirects and the SPA fallback (extensionless paths serve index.html). A capability holder must also hold the acting principal's project role (maintainer or owner to change, any role to read). Reserved headers (Content-Security-Policy, Strict-Transport-Security, Set-Cookie, X-Forwarded-*, caching, scope, transport and the platform's own) are refused, and a redirect target must be a local path, so a site can never become an open redirect.",++ "permissions": [++ "space:write"++ ],++ "resourceConstraints": [++ "project"++ ],++ "quotaClass": "space-manage",++ "events": [],++ "implementedBy": [++ "GET /api/v1/sites/:id/config",++ "PUT /api/v1/sites/:id/config",++ "DELETE /api/v1/sites/:id/config"++ ],++ "inputSchema": "space.site-config-request@1",++ "outputSchema": "space.site-config-result@1"++}nnew file mode 100644@@@ -0,0 +1,40 @@++{++ "id": "space.site.manage",++ "version": "1.0",++ "owner": "space",++ "status": "active",++ "visibility": "first-party",++ "description": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",++ "permissions": [++ "space:write"++ ],++ "resourceConstraints": [++ "project"++ ],++ "quotaClass": "space-manage",++ "events": [],++ "implementedBy": [++ "GET /api/v1/projects",++ "POST /api/v1/projects",++ "GET /api/v1/projects/:id",++ "DELETE /api/v1/projects/:id",++ "GET /api/v1/projects/:id/quota",++ "PUT /api/v1/projects/:id/quota",++ "PUT /api/v1/projects/:id/members/:principal",++ "DELETE /api/v1/projects/:id/members/:principal",++ "GET /api/v1/projects/:id/sites",++ "POST /api/v1/projects/:id/sites",++ "GET /api/v1/sites/:id",++ "GET /api/v1/sites/:id/source",++ "PUT /api/v1/sites/:id/source",++ "DELETE /api/v1/sites/:id/source",++ "DELETE /api/v1/sites/:id",++ "DELETE /api/v1/deploys/:id",++ "POST /api/v1/projects/:id/takedown",++ "DELETE /api/v1/projects/:id/takedown",++ "POST /api/v1/sites/:id/takedown",++ "DELETE /api/v1/sites/:id/takedown"++ ],++ "inputSchema": "space.site-manage-request@1",++ "outputSchema": "space.site-manage-result@1"++}@@@ -18,36 +18,47 @@"site": {"name": "Space","icon": "space",-- "tagline": "Code hosting and spaces",-- "what": "OpenVibe's code hosting, and \"spaces\": apps that use your OpenVibe account and the SDK, coming next. The forum returned to OpenVibe.Community.",++ "tagline": "Static sites, hosted by OpenVibe",++ "what": "Static site hosting moved here from OpenVibe.Host: immutable deploys, one-step rollback, previews, Git ingest and verified custom domains, at <name>.openvibe.website. Open to OpenVibe staff until the public launch; dynamic pages and spaces come next. The forum is on OpenVibe.Community.","legalProfile": "ugc","position": 19,"tld": "space","accent": "#818cf8",-- "description": "OpenVibe.Space will host code and \"spaces\" — apps that use your OpenVibe account and the SDK — and both are still being built. Until they land it serves a home, and its former /s/* forum paths redirect permanently to openvibe.community.",++ "description": "OpenVibe.Space hosts static sites: every deploy is immutable and content-addressed, the active one rolls back in one step, previews are private to the project, a site can deploy from a public Git repository, and custom domains are served after a TXT check. Sites get a name under openvibe.website. Hosting is open to OpenVibe staff until the public launch, which waits for abuse handling and a designated DMCA agent. Dynamic pages and spaces (apps on your OpenVibe account) are next and not built yet. The former /s/* forum paths redirect permanently to openvibe.community.","pillars": [{-- "title": "Code hosting",-- "text": "The network's code, hosted here."++ "title": "Immutable deploys",++ "text": "Every deploy is kept, content-addressed, and the active one rolls back in one step."},{-- "title": "Spaces",-- "text": "Apps that use your OpenVibe account and the SDK."++ "title": "Previews and Git",++ "text": "Private previews for the project, and deploys from a public Git repository."},{-- "title": "Coming next",-- "text": "Neither is built yet; Space serves a home until they land."++ "title": "Your own address",++ "text": "A name under openvibe.website, or a custom domain verified by a TXT record."},{-- "title": "One account",-- "text": "Your OpenVibe identity, everywhere on the network."++ "title": "Staff first",++ "text": "Open to OpenVibe staff until abuse handling and a DMCA agent are in place."}],"keywords": "openvibe space, code hosting, openvibe spaces, openvibe account, openvibe sdk","launch": "Code hosting and spaces are still to be built; the forum moved back to OpenVibe.Community (2026-10-08)."},-- "capabilities": [],-- "eventsProduced": [],++ "capabilities": [++ "space.site.manage",++ "space.deploy.create",++ "space.domain.manage",++ "space.site.config",++ "space.resource.read"++ ],++ "eventsProduced": [++ "space.deploy.created",++ "space.deploy.activated",++ "space.deploy.failed",++ "space.domain.verified"++ ],"eventsConsumed": [],"namespacesOwned": ["space.*"@@@ -91,7 +102,7 @@"none": "no claims: one Space process runs and owns its database"}},-- "notes": "Space is OpenVibe's code hosting, and \"spaces\" — apps that use your OpenVibe account and the SDK — come next. The forum returned to OpenVibe.Community (owner decision 2026-10-08), so Space serves a home and its former /s/* forum paths redirect permanently to openvibe.community; it keeps no forum capability, contract or event.",++ "notes": "Space is OpenVibe's static site hosting since T20 step 2 (2026-10-12): the engine moved from OpenVibe.Host with its tables (Space migrations 0002-0005), under space.* capabilities and events. Tenant sites are served by Host header at <site>.openvibe.website and on TXT-verified custom domains; deploys are immutable and content-addressed with one-step rollback, previews, Git ingest from public repositories, per-site headers and redirects, takedowns and a resource index. SPACE_PUBLIC_HOSTING is off: only OpenVibe staff create projects, everyone else gets 403 space.not_launched. Consumes OpenVibe.Network (SSO, JWKS, client credentials) and OpenVibe.Media (objects, when the object store is media) and publishes through OpenVibe.Events (events.event.publish). The former forum paths /s/* redirect permanently to openvibe.community; the forum capabilities stay retired. The host.* hosting ids are retired once Host has dropped the engine.","health": "/api/health","ready": "/api/ready","internalOrigin": "http://127.0.0.1:4940"@@@ -1,6 +1,6 @@{"name": "openvibe-contracts",-- "version": "0.148.0",++ "version": "0.149.0","description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.","main": "index.js","types": "generated/typescript/index.d.ts",
@@ -4,6 +4,18 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
@@ -4,6 +4,18 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
the notes were in the tag and commit messages (`git tag -n1`).
the notes were in the tag and commit messages (`git tag -n1`).
+## 0.149.0 — 2026-10-12
+
+- The static hosting engine moved from OpenVibe.Host to OpenVibe.Space (plan T20 step 2), so its contracts are now
+ `space.*`: capabilities `space.site.manage`, `space.deploy.create`, `space.domain.manage`, `space.site.config` and
+ `space.resource.read`; schemas `space.project`, `space.site`, `space.deploy`, `space.domain`, `space.site-config` and
+ their request/result pairs; events `space.deploy.created`, `space.deploy.activated`, `space.deploy.failed` and
+ `space.domain.verified`, each with a payload schema (Host only ever had one for `deploy.activated`). Tenant sites are
+ `<site>.openvibe.website`; a custom domain is verified by `_openvibe-space.<hostname>`. The space manifest owns them
+ and its catalog text says what is built (staff-only until the public launch).
+- `space.site` uses the site id pattern the engine has always issued (`sit_`); `host.site` said `site_`.
+- The `host.*` hosting ids stay until OpenVibe.Host has removed the engine; a later release retires them.
+
## 0.148.0 — 2026-10-12
## 0.148.0 — 2026-10-12
- Catalog text (`site.what`) for rent, food, work and games names what shipped today: Rent's affordable housing from
- Catalog text (`site.what`) for rent, food, work and games names what shipped today: Rent's affordable housing from
@@ -6856,6 +6856,163 @@
@@ -6856,6 +6856,163 @@
"visibility": "public",
"visibility": "public",
"compatibility": "backward",
"compatibility": "backward",
"status": "active"
"status": "active"
+ },
+ {
+ "id": "space.deploy-create-request",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "space/deploy-create-request.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "space.deploy-create-result",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "space/deploy-create-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "space.deploy",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "space/deploy.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "space.domain-manage-request",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "space/domain-manage-request.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "space.domain-manage-result",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "space/domain-manage-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "space.domain",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "space/domain.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "space.project",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "space/project.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "space.site-manage-request",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "space/site-manage-request.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "space.site-manage-result",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "space/site-manage-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "space.site",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "space/site.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "space.site-config",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "space/site-config.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "space.site-config-request",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "space/site-config-request.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "space.site-config-result",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "space/site-config-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "space.deploy.activated",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "events/payloads/space.deploy.activated.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active",
+ "adr": "ADR-004"
+ },
+ {
+ "id": "space.deploy.created",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "events/payloads/space.deploy.created.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active",
+ "adr": "ADR-004"
+ },
+ {
+ "id": "space.deploy.failed",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "events/payloads/space.deploy.failed.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active",
+ "adr": "ADR-004"
+ },
+ {
+ "id": "space.domain.verified",
+ "version": "1.0.0",
+ "owner": "space",
+ "schema": "events/payloads/space.domain.verified.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active",
+ "adr": "ADR-004"
}
}
]
]
}
}
new file mode 100644
new file mode 100644
@@ -0,0 +1,47 @@
@@ -0,0 +1,47 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/space.deploy.activated.v1.json",
+ "title": "SpaceDeployActivatedPayload",
+ "description": "space.deploy.activated v1 (OpenVibe.Space server/domain/deploys.js switchPointer, Stage B tenant hosting). A tenant site's active deploy changed, written into Host's event outbox in the transaction that moves the pointer (activate or rollback). Envelope: subject { type: deploy, id: <deploy id> }, visibility internal, priority low, actor the person or app that switched it. A network service's release going live is space.release.published, not this.",
+ "type": "object",
+ "required": [
+ "project_id",
+ "site_id",
+ "site",
+ "deploy_id",
+ "previous_deploy_id",
+ "rollback"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "site_id": {
+ "type": "string",
+ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "site": {
+ "type": "string",
+ "pattern": "^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){2,39}$",
+ "description": "The site's name (<site>.openvibe.website)."
+ },
+ "deploy_id": {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "previous_deploy_id": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The deploy that served before; null for the site's first activation."
+ },
+ "rollback": {
+ "type": "boolean",
+ "description": "true when the switch was a rollback."
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/space.deploy.created.v1.json",
+ "title": "SpaceDeployCreatedPayload",
+ "description": "space.deploy.created v1 (OpenVibe.Space, subject deploy dpl_…, visibility internal): an upload or a Git ingest became an immutable, content-addressed deploy in the ready state. It is not yet serving: space.deploy.activated follows when it is made active. No file contents or paths.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "project_id",
+ "site_id",
+ "site",
+ "source",
+ "file_count",
+ "total_bytes",
+ "manifest_sha256"
+ ],
+ "properties": {
+ "project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "site_id": {
+ "type": "string",
+ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "site": {
+ "type": "string",
+ "pattern": "^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){2,39}$",
+ "description": "the site name (one DNS label; the site is served at <site>.openvibe.website)"
+ },
+ "source": {
+ "enum": [
+ "archive",
+ "files",
+ "preview",
+ "git"
+ ],
+ "description": "how the deploy arrived: an archive or individual files, a private preview upload, or a Git ingest (always a preview)"
+ },
+ "file_count": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "total_bytes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "manifest_sha256": {
+ "type": "string",
+ "pattern": "^[a-f0-9]{64}$",
+ "description": "sha256 of the deploy manifest (the content address of the whole deploy)"
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,44 @@
@@ -0,0 +1,44 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/space.deploy.failed.v1.json",
+ "title": "SpaceDeployFailedPayload",
+ "description": "space.deploy.failed v1 (OpenVibe.Space, subject deploy dpl_…, visibility internal): an upload was refused and recorded as a failed deploy with its log, so the owner can see why. Problem codes only, at most ten; never file contents.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "project_id",
+ "site_id",
+ "site",
+ "code",
+ "problems"
+ ],
+ "properties": {
+ "project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "site_id": {
+ "type": "string",
+ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "site": {
+ "type": "string",
+ "pattern": "^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){2,39}$",
+ "description": "the site name (one DNS label; the site is served at <site>.openvibe.website)"
+ },
+ "code": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)+$",
+ "description": "the refusal problem code (for example deploy.too_large or storage.space_full)"
+ },
+ "problems": {
+ "type": "array",
+ "maxItems": 10,
+ "items": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)*$"
+ },
+ "description": "the problem codes of the files that were refused"
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/space.domain.verified.v1.json",
+ "title": "SpaceDomainVerifiedPayload",
+ "description": "space.domain.verified v1 (OpenVibe.Space, subject domain dom_…, visibility internal): a custom domain's TXT record (_openvibe-space.<hostname>) was found, and the domain now serves the site.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "project_id",
+ "site_id",
+ "site",
+ "hostname"
+ ],
+ "properties": {
+ "project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "site_id": {
+ "type": "string",
+ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "site": {
+ "type": "string",
+ "pattern": "^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){2,39}$",
+ "description": "the site name (one DNS label; the site is served at <site>.openvibe.website)"
+ },
+ "hostname": {
+ "type": "string",
+ "minLength": 4,
+ "maxLength": 253,
+ "description": "the custom domain, a fully qualified host name in lower case"
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,44 @@
@@ -0,0 +1,44 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/deploy-create-request.v1.json",
+ "title": "SpaceDeployCreateRequest",
+ "description": "space.deploy-create-request@1: bodies of space.deploy.create on OpenVibe.Space (a deployer or above of the project; an app principal added as deployer can deploy from CI). POST /api/v1/sites/:id/deploys uploads a deploy: the raw body is a tar or tar.gz archive (Content-Type application/gzip, application/x-gzip, application/x-tar or application/octet-stream), or multipart/form-data with archive=<one archive> or files=<file>… (each part's filename is its path); optional fields activate=1, root=<subdirectory to deploy> and strip=folder (removes a shared first segment) also work as query parameters. It is validated (path traversal, links, special and hidden files, server-side code, file types, sizes and quotas) and never executed; a refusal is a problem whose extra carries the failed deploy_id and its log (413 upload.too_large, quota.storage; 422 validation codes; 429 quota.deploys_per_day; 403 site.taken_down; 503 upload.busy; 507 storage.host_full). POST /api/v1/deploys/:id/activate { expected_active? } and POST /api/v1/sites/:id/rollback { deploy_id?, expected_active? } switch the site's pointer in one step (rollback without deploy_id goes to the deploy active before; 409 deploy.not_ready, site.active_changed when expected_active is stale, deploy.no_previous). GET /api/v1/sites/:id/deploys, /deploys/:id and /deploys/:id/log take no body.",
+ "anyOf": [
+ {
+ "type": "string",
+ "contentEncoding": "binary",
+ "description": "The upload itself: the tar or tar.gz bytes, or the multipart/form-data body (as common.binary@1)."
+ },
+ {
+ "type": "object",
+ "properties": {
+ "deploy_id": {
+ "anyOf": [
+ {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "expected_active": {
+ "anyOf": [
+ {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The deploy you saw active (null: none); refused with 409 site.active_changed when it moved."
+ }
+ }
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,171 @@
@@ -0,0 +1,171 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/deploy-create-result.v1.json",
+ "title": "SpaceDeployCreateResult",
+ "description": "space.deploy-create-result@1: answers of space.deploy.create on OpenVibe.Space. GET /api/v1/sites/:id/deploys?limit= → { active_deploy_id, deploys, activations } (newest first; the last 20 pointer switches). POST /api/v1/sites/:id/deploys → 201 { deploy, activated, url } (the ready deploy with its validation log; activated when activate=1 switched the site to it). GET /api/v1/deploys/:id → { deploy } with its files. GET /api/v1/deploys/:id/log → { deploy_id, state, log }. POST /api/v1/deploys/:id/activate and /sites/:id/rollback → { active_deploy_id, previous_deploy_id, changed } (changed false when it was already active). Emits space.deploy.created|activated|failed.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "active_deploy_id",
+ "deploys",
+ "activations"
+ ],
+ "properties": {
+ "active_deploy_id": {
+ "anyOf": [
+ {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "deploys": {
+ "type": "array",
+ "items": {
+ "$ref": "deploy.v1.json"
+ }
+ },
+ "activations": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "deploy_id",
+ "previous_deploy_id",
+ "kind",
+ "actor",
+ "at"
+ ],
+ "properties": {
+ "deploy_id": {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "previous_deploy_id": {
+ "anyOf": [
+ {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "kind": {
+ "enum": [
+ "activate",
+ "rollback"
+ ]
+ },
+ "actor": {
+ "type": "string"
+ },
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "deploy"
+ ],
+ "properties": {
+ "deploy": {
+ "$ref": "deploy.v1.json"
+ },
+ "activated": {
+ "type": "boolean"
+ },
+ "url": {
+ "type": "string"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "deploy_id",
+ "state",
+ "log"
+ ],
+ "properties": {
+ "deploy_id": {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "state": {
+ "enum": [
+ "ready",
+ "failed",
+ "deleted"
+ ]
+ },
+ "log": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "level",
+ "message",
+ "at"
+ ],
+ "properties": {
+ "level": {
+ "enum": [
+ "info",
+ "warn",
+ "error"
+ ]
+ },
+ "message": {
+ "type": "string"
+ },
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "active_deploy_id",
+ "previous_deploy_id",
+ "changed"
+ ],
+ "properties": {
+ "active_deploy_id": {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "previous_deploy_id": {
+ "anyOf": [
+ {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "changed": {
+ "type": "boolean"
+ }
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,167 @@
@@ -0,0 +1,167 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/deploy.v1.json",
+ "title": "SpaceDeploy",
+ "description": "space.deploy@1: one immutable static deploy on OpenVibe.Space (server/http/serialize.js deploy): ready (validated and stored, never executed) or failed (refused, with its failure code and log), whether the site serves it, its size and manifest hash. GET /api/v1/deploys/:id adds the files; the upload answer adds the log.",
+ "type": "object",
+ "required": [
+ "id",
+ "site_id",
+ "project_id",
+ "state",
+ "active",
+ "source",
+ "file_count",
+ "total_bytes",
+ "new_bytes",
+ "manifest_sha256",
+ "failure_code",
+ "created_by",
+ "created_at"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "site_id": {
+ "type": "string",
+ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "state": {
+ "enum": [
+ "ready",
+ "failed",
+ "deleted"
+ ]
+ },
+ "active": {
+ "type": "boolean"
+ },
+ "source": {
+ "enum": [
+ "archive",
+ "files",
+ "preview",
+ "git"
+ ]
+ },
+ "git": {
+ "type": "object",
+ "required": [
+ "provider",
+ "repo_url",
+ "ref",
+ "commit_sha"
+ ],
+ "properties": {
+ "provider": {
+ "type": "string"
+ },
+ "repo_url": {
+ "type": "string"
+ },
+ "ref": {
+ "type": "string"
+ },
+ "commit_sha": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{40}$"
+ }
+ },
+ "description": "Present on source git: the commit this deploy was built from (public provenance, never a credential)."
+ },
+ "file_count": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "total_bytes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "new_bytes": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "Bytes this deploy added to the project's store."
+ },
+ "manifest_sha256": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "pattern": "^[0-9a-f]{64}$"
+ },
+ "failure_code": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "created_by": {
+ "type": "string"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "files": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "path",
+ "sha256",
+ "size",
+ "content_type"
+ ],
+ "properties": {
+ "path": {
+ "type": "string"
+ },
+ "sha256": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$"
+ },
+ "size": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "content_type": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "log": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "level",
+ "message",
+ "at"
+ ],
+ "properties": {
+ "level": {
+ "enum": [
+ "info",
+ "warn",
+ "error"
+ ]
+ },
+ "message": {
+ "type": "string"
+ },
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ }
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/domain-manage-request.v1.json",
+ "title": "SpaceDomainManageRequest",
+ "description": "space.domain-manage-request@1: bodies of space.domain.manage on OpenVibe.Space (a maintainer or above of the project). POST /api/v1/sites/:id/domains { hostname } adds a custom domain, pending until its TXT record is verified: a fully qualified host name, lowercased (422 domain.invalid_hostname; 422 domain.reserved for any OpenVibe domain; 403 domain.sandbox in sandbox projects; 429 quota.custom_domains; 409 domain.exists, domain.taken). GET /api/v1/sites/:id/domains, POST /api/v1/domains/:id/verify (checks the TXT record now) and DELETE /api/v1/domains/:id (409 domain.default: the default domain goes only with the site) take no body.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "hostname"
+ ],
+ "properties": {
+ "hostname": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 254,
+ "description": "e.g. www.example.org (a trailing dot is dropped)."
+ }
+ }
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,44 @@
@@ -0,0 +1,44 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/domain-manage-result.v1.json",
+ "title": "SpaceDomainManageResult",
+ "description": "space.domain-manage-result@1: answers of space.domain.manage on OpenVibe.Space. GET /api/v1/sites/:id/domains → { domains } (the default domain first). POST /api/v1/sites/:id/domains → 201 { domain } with the DNS records to publish. POST /api/v1/domains/:id/verify → { domain } after the check (status verified and served once the TXT record matches, else last_error says what was found; emits space.domain.verified). DELETE /api/v1/domains/:id → { deleted: true }.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "domains"
+ ],
+ "properties": {
+ "domains": {
+ "type": "array",
+ "items": {
+ "$ref": "domain.v1.json"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "domain"
+ ],
+ "properties": {
+ "domain": {
+ "$ref": "domain.v1.json"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "deleted"
+ ],
+ "properties": {
+ "deleted": {
+ "const": true
+ }
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,131 @@
@@ -0,0 +1,131 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/domain.v1.json",
+ "title": "SpaceDomain",
+ "description": "space.domain@1: one domain of a static site on OpenVibe.Space (server/http/serialize.js domain): the default <site>.openvibe.website, or a custom domain that is served only once its DNS TXT record is verified (re-checked daily; it lapses when the record is gone). A custom domain carries the records its owner must publish (the TXT verification value is public by design and shown only to members); TLS certificates are issued by an operator.",
+ "type": "object",
+ "required": [
+ "id",
+ "site_id",
+ "hostname",
+ "kind",
+ "status",
+ "served",
+ "verified_at",
+ "last_checked_at",
+ "last_error"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^dom_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "site_id": {
+ "type": "string",
+ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "hostname": {
+ "type": "string"
+ },
+ "kind": {
+ "enum": [
+ "default",
+ "custom"
+ ]
+ },
+ "status": {
+ "enum": [
+ "pending",
+ "verified",
+ "failed",
+ "lapsed"
+ ]
+ },
+ "served": {
+ "type": "boolean"
+ },
+ "verified_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "last_checked_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "last_error": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "instructions": {
+ "type": "object",
+ "required": [
+ "verification",
+ "routing",
+ "tls"
+ ],
+ "properties": {
+ "verification": {
+ "type": "object",
+ "required": [
+ "type",
+ "name",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "TXT"
+ },
+ "name": {
+ "type": "string",
+ "description": "_openvibe-space.<hostname>"
+ },
+ "value": {
+ "type": "string",
+ "description": "openvibe-space-verification=<token>"
+ },
+ "note": {
+ "type": "string"
+ }
+ }
+ },
+ "routing": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "type",
+ "name",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string"
+ },
+ "note": {
+ "type": "string"
+ }
+ }
+ },
+ "description": "A CNAME for a subdomain; A/AAAA for an apex domain when the operator configured them."
+ },
+ "tls": {
+ "type": "string"
+ }
+ }
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,159 @@
@@ -0,0 +1,159 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/project.v1.json",
+ "title": "SpaceProject",
+ "description": "space.project@1: one OpenVibe.Space tenant project (server/http/serialize.js project): owned by a person (usr_…), production or sandbox, with the caller's role in it and any staff takedown. POST /api/v1/projects and GET /api/v1/projects/:id add the effective quota and the usage.",
+ "type": "object",
+ "required": [
+ "id",
+ "name",
+ "environment",
+ "network_project_id",
+ "owner",
+ "role",
+ "created_at",
+ "takedown"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 80
+ },
+ "environment": {
+ "enum": [
+ "production",
+ "sandbox"
+ ]
+ },
+ "network_project_id": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "The OpenVibe.Network project once Network has projects."
+ },
+ "owner": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "role": {
+ "enum": [
+ "owner",
+ "maintainer",
+ "deployer",
+ "staff",
+ null
+ ],
+ "description": "The caller's role (staff: a Network admin who is not a member)."
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "takedown": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "required": [
+ "scope",
+ "reason",
+ "since"
+ ],
+ "description": "A staff takedown covering it: serving stops while the content is kept for review.",
+ "properties": {
+ "scope": {
+ "enum": [
+ "project",
+ "site"
+ ]
+ },
+ "reason": {
+ "type": "string"
+ },
+ "since": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ },
+ "quota": {
+ "type": "object",
+ "required": [
+ "storage_bytes",
+ "deploys_per_day",
+ "max_files",
+ "max_file_bytes",
+ "sites",
+ "custom_domains"
+ ],
+ "description": "The effective quota: the project's override where set, else its environment's default.",
+ "properties": {
+ "storage_bytes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "deploys_per_day": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "max_files": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "max_file_bytes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "sites": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "custom_domains": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ },
+ "usage": {
+ "type": "object",
+ "required": [
+ "storage_bytes",
+ "objects",
+ "deploys_last_24h",
+ "sites",
+ "custom_domains"
+ ],
+ "properties": {
+ "storage_bytes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "objects": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "deploys_last_24h": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "sites": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "custom_domains": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/site-config-request.v1.json",
+ "title": "SpaceSiteConfigRequest",
+ "description": "space.site-config-request@1: the body of PUT /api/v1/sites/:id/config on OpenVibe.Space (space.site.config; the acting principal must be a maintainer or owner of the site's project). { headers?, redirects?, spa? } replaces the whole configuration: a field left out is reset to its default. A redirect's status defaults to 301. Refusals are 422 problem+json: site_config.reserved_header, site_config.header_name, site_config.header_value (longer than 1024 characters or a control character), site_config.too_many (over 50 headers or 200 redirects), site_config.redirect (not a local path, a duplicate from, or a status outside 301/302/307/308), site_config.headers and site_config.redirects (wrong type). GET and DELETE /api/v1/sites/:id/config take no body (DELETE resets to the defaults).",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "headers": {
+ "type": "object",
+ "additionalProperties": { "type": "string" }
+ },
+ "redirects": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["from", "to"],
+ "properties": {
+ "from": { "type": "string" },
+ "to": { "type": "string" },
+ "status": { "enum": [301, 302, 307, 308] }
+ }
+ }
+ },
+ "spa": { "type": "boolean" }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/site-config-result.v1.json",
+ "title": "SpaceSiteConfigResult",
+ "description": "space.site-config-result@1: every answer of space.site.config on OpenVibe.Space. GET, PUT and DELETE /api/v1/sites/:id/config → { config } (after a PUT, the configuration as stored; after a DELETE, the defaults).",
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["config"],
+ "properties": {
+ "config": { "$ref": "site-config.v1.json" }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,32 @@
@@ -0,0 +1,32 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/site-config.v1.json",
+ "title": "SpaceSiteConfig",
+ "description": "space.site-config@1: one Host site's serving configuration (plan T12 J3). headers are applied to every response of the site before the platform's own, which always win; the platform's security, routing, caching, scope and transport headers (Content-Security-Policy, Strict-Transport-Security, Set-Cookie, Cache-Control, Content-Type, Location, Service-Worker-Allowed, Alt-Svc and the rest of Host's reserved list) and every X-Forwarded-* header are refused. redirects are local paths only (no scheme, host, protocol-relative URL, backslash or control character, also after percent-decoding). spa serves /index.html for an extensionless path that has no file. A site with no configuration answers { headers: {}, redirects: [], spa: false }.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["headers", "redirects", "spa"],
+ "properties": {
+ "headers": {
+ "type": "object",
+ "maxProperties": 50,
+ "propertyNames": { "pattern": "^[A-Za-z0-9][A-Za-z0-9-]{0,63}$" },
+ "additionalProperties": { "type": "string", "maxLength": 1024 }
+ },
+ "redirects": {
+ "type": "array",
+ "maxItems": 200,
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["from", "to", "status"],
+ "properties": {
+ "from": { "type": "string", "pattern": "^/(?!/)", "maxLength": 1024 },
+ "to": { "type": "string", "pattern": "^/(?!/)", "maxLength": 1024 },
+ "status": { "enum": [301, 302, 307, 308] }
+ }
+ }
+ },
+ "spa": { "type": "boolean" }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,151 @@
@@ -0,0 +1,151 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/site-manage-request.v1.json",
+ "title": "SpaceSiteManageRequest",
+ "description": "space.site-manage-request@1: bodies of space.site.manage on OpenVibe.Space (judged by the acting principal's project role too; a sandbox token is refused on production projects). POST /api/v1/projects { name, environment?, network_project_id? } creates a project owned by the person named with X-OV-Subject (403 project.owner_must_be_user; 422 project.invalid_name over 80 characters, project.invalid_environment; 429 quota.projects; 409 project.network_project_taken). PUT /api/v1/projects/:id/quota { storage_bytes?, deploys_per_day?, max_files?, max_file_bytes?, sites?, custom_domains? } (a Network staff user: 403 auth.staff_only; fields left out keep their value). PUT /api/v1/projects/:id/members/:principal { role } (the owner: 422 member.invalid_role, 409 member.owner_fixed). POST /api/v1/projects/:id/sites { name } (a maintainer; lowercased and trimmed, one DNS label of 3–40 characters: 422 site.invalid_name, site.name_reserved; 409 site.name_taken, site.name_held; 429 quota.sites). POST /api/v1/projects/:id/takedown and /sites/:id/takedown { reason } (staff: stop serving, keep the content; 409 takedown.exists); DELETE on those { note? } lifts it. GET /api/v1/projects, /projects/:id, /projects/:id/quota, /projects/:id/sites and /sites/:id, and DELETE /api/v1/projects/:id, /projects/:id/members/:principal, /sites/:id and /deploys/:id take no body (deleting refuses taken-down content for non-staff: 409 site.taken_down; an active deploy: 409 deploy.active).",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "1–80 characters once trimmed."
+ },
+ "environment": {
+ "enum": [
+ "production",
+ "sandbox",
+ null
+ ],
+ "description": "Default production; a sandbox credential creates sandbox projects only."
+ },
+ "network_project_id": {
+ "anyOf": [
+ {
+ "type": "string",
+ "pattern": "^([A-Za-z0-9_:-]{3,80})?$"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ }
+ }
+ },
+ {
+ "type": "object",
+ "properties": {
+ "storage_bytes": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 0,
+ "description": "A non-negative integer, or null for the environment default."
+ },
+ "deploys_per_day": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 0,
+ "description": "A non-negative integer, or null for the environment default."
+ },
+ "max_files": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 0,
+ "description": "A non-negative integer, or null for the environment default."
+ },
+ "max_file_bytes": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 0,
+ "description": "A non-negative integer, or null for the environment default."
+ },
+ "sites": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 0,
+ "description": "A non-negative integer, or null for the environment default."
+ },
+ "custom_domains": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 0,
+ "description": "A non-negative integer, or null for the environment default."
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "role"
+ ],
+ "properties": {
+ "role": {
+ "enum": [
+ "owner",
+ "maintainer",
+ "deployer"
+ ],
+ "description": "Only a person (usr_…) can be an owner."
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "pattern": "^\\s*[A-Za-z0-9](?:[A-Za-z0-9]|-(?=[A-Za-z0-9])){2,39}\\s*$",
+ "description": "The site name (lowercased)."
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "reason"
+ ],
+ "properties": {
+ "reason": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Members see it."
+ }
+ }
+ },
+ {
+ "type": "object",
+ "properties": {
+ "note": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "Cut to 500 characters."
+ }
+ }
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,274 @@
@@ -0,0 +1,274 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/site-manage-result.v1.json",
+ "title": "SpaceSiteManageResult",
+ "description": "space.site-manage-result@1: answers of space.site.manage on OpenVibe.Space. GET /api/v1/projects → { projects } (the caller's projects with their role). POST /api/v1/projects → 201 { project } (with quota and usage). GET /api/v1/projects/:id → { project, sites, members }. GET and PUT /api/v1/projects/:id/quota → { quota, usage }. PUT and DELETE /api/v1/projects/:id/members/:principal → { members }. GET /api/v1/projects/:id/sites → { sites }. POST /api/v1/projects/:id/sites → 201 { site }. GET /api/v1/sites/:id → { site, domains }. DELETE /api/v1/projects/:id and /sites/:id → { deleted: true } (serving stops at once; the objects are removed). DELETE /api/v1/deploys/:id → { deleted: true, objects_removed }. POST …/takedown → 201 { takedown }; DELETE …/takedown → { lifted: true }.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "projects"
+ ],
+ "properties": {
+ "projects": {
+ "type": "array",
+ "items": {
+ "$ref": "project.v1.json"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "project"
+ ],
+ "properties": {
+ "project": {
+ "$ref": "project.v1.json"
+ },
+ "sites": {
+ "type": "array",
+ "items": {
+ "$ref": "site.v1.json"
+ }
+ },
+ "members": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "principal",
+ "role",
+ "added_at"
+ ],
+ "properties": {
+ "principal": {
+ "type": "string",
+ "description": "usr_…, app:app_… or svc:…"
+ },
+ "role": {
+ "enum": [
+ "owner",
+ "maintainer",
+ "deployer"
+ ]
+ },
+ "added_at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "quota",
+ "usage"
+ ],
+ "properties": {
+ "quota": {
+ "type": "object",
+ "required": [
+ "storage_bytes",
+ "deploys_per_day",
+ "max_files",
+ "max_file_bytes",
+ "sites",
+ "custom_domains"
+ ],
+ "description": "The effective quota: the project's override where set, else its environment's default.",
+ "properties": {
+ "storage_bytes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "deploys_per_day": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "max_files": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "max_file_bytes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "sites": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "custom_domains": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ },
+ "usage": {
+ "type": "object",
+ "required": [
+ "storage_bytes",
+ "objects",
+ "deploys_last_24h",
+ "sites",
+ "custom_domains"
+ ],
+ "properties": {
+ "storage_bytes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "objects": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "deploys_last_24h": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "sites": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "custom_domains": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "members"
+ ],
+ "properties": {
+ "members": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "principal",
+ "role",
+ "added_at"
+ ],
+ "properties": {
+ "principal": {
+ "type": "string",
+ "description": "usr_…, app:app_… or svc:…"
+ },
+ "role": {
+ "enum": [
+ "owner",
+ "maintainer",
+ "deployer"
+ ]
+ },
+ "added_at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "sites"
+ ],
+ "properties": {
+ "sites": {
+ "type": "array",
+ "items": {
+ "$ref": "site.v1.json"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "site"
+ ],
+ "properties": {
+ "site": {
+ "$ref": "site.v1.json"
+ },
+ "domains": {
+ "type": "array",
+ "items": {
+ "$ref": "domain.v1.json"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "deleted"
+ ],
+ "properties": {
+ "deleted": {
+ "const": true
+ },
+ "objects_removed": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "takedown"
+ ],
+ "properties": {
+ "takedown": {
+ "type": "object",
+ "required": [
+ "scope",
+ "reason",
+ "since"
+ ],
+ "description": "The takedown now in force.",
+ "properties": {
+ "scope": {
+ "enum": [
+ "project",
+ "site"
+ ]
+ },
+ "reason": {
+ "type": "string"
+ },
+ "since": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "lifted"
+ ],
+ "properties": {
+ "lifted": {
+ "const": true
+ }
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,87 @@
@@ -0,0 +1,87 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/site.v1.json",
+ "title": "SpaceSite",
+ "description": "space.site@1: one static site on OpenVibe.Space (server/http/serialize.js site): its name (one DNS label), the default hostname and URL it is served at, the deploy it serves (one pointer; null until a deploy is activated) and any staff takedown.",
+ "type": "object",
+ "required": [
+ "id",
+ "project_id",
+ "name",
+ "hostname",
+ "url",
+ "active_deploy_id",
+ "takedown",
+ "created_at",
+ "updated_at"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "name": {
+ "type": "string",
+ "pattern": "^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){2,39}$"
+ },
+ "hostname": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string"
+ },
+ "active_deploy_id": {
+ "anyOf": [
+ {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "takedown": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "required": [
+ "scope",
+ "reason",
+ "since"
+ ],
+ "description": "A staff takedown covering it: serving stops while the content is kept for review.",
+ "properties": {
+ "scope": {
+ "enum": [
+ "project",
+ "site"
+ ]
+ },
+ "reason": {
+ "type": "string"
+ },
+ "since": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
+{
+ "deploy_id": 42,
+ "expected_active": 7
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
+{
+ "expected_active": "dpl_01JAD3D4E5F6G7H8J9K0M1N2P3"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{}
new file mode 100644
new file mode 100644
@@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
+{
+ "deploy_id": "dpl_01JAD3D4E5F6G7H8J9K0M1N2P3",
+ "expected_active": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
+{
+ "active_deploy_id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",
+ "deploys": [],
+ "activations": [
+ {
+ "deploy_id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",
+ "previous_deploy_id": null,
+ "kind": "deploy",
+ "actor": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "at": "2026-09-24T09:40:12.000Z"
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
+{
+ "active_deploy_id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",
+ "deploys": [
+ {
+ "id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",
+ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",
+ "project_id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",
+ "state": "ready",
+ "active": true,
+ "source": "archive",
+ "file_count": 37,
+ "total_bytes": 482113,
+ "new_bytes": 20411,
+ "manifest_sha256": "a3f1c9e0b4d2e6f8a1c3e5f7091b2d4f6a8c0e2f4a6b8d0f2e4c6a8b0d2f4e6a",
+ "failure_code": null,
+ "created_by": "app:app_01JAD7A8B9C0D1E2F3G4H5J6K7",
+ "created_at": "2026-09-24T09:40:12.000Z"
+ }
+ ],
+ "activations": [
+ {
+ "deploy_id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",
+ "previous_deploy_id": "dpl_01JAD3D4E5F6G7H8J9K0M1N2P3",
+ "kind": "activate",
+ "actor": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "at": "2026-09-24T09:40:12.000Z"
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
+{
+ "deploy_id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",
+ "state": "ready",
+ "log": [
+ {
+ "level": "info",
+ "message": "ready; not active until you activate it",
+ "at": "2026-09-24T09:40:12.000Z"
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
+{
+ "deploy": {
+ "id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",
+ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",
+ "project_id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",
+ "state": "ready",
+ "active": true,
+ "source": "archive",
+ "file_count": 37,
+ "total_bytes": 482113,
+ "new_bytes": 20411,
+ "manifest_sha256": "a3f1c9e0b4d2e6f8a1c3e5f7091b2d4f6a8c0e2f4a6b8d0f2e4c6a8b0d2f4e6a",
+ "failure_code": null,
+ "created_by": "app:app_01JAD7A8B9C0D1E2F3G4H5J6K7",
+ "created_at": "2026-09-24T09:40:12.000Z",
+ "files": [
+ {
+ "path": "index.html",
+ "sha256": "a3f1c9e0b4d2e6f8a1c3e5f7091b2d4f6a8c0e2f4a6b8d0f2e4c6a8b0d2f4e6a",
+ "size": 5120,
+ "content_type": "text/html; charset=utf-8"
+ }
+ ]
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
+{
+ "active_deploy_id": "dpl_01JAD3D4E5F6G7H8J9K0M1N2P3",
+ "previous_deploy_id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",
+ "changed": true
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,31 @@
@@ -0,0 +1,31 @@
+{
+ "deploy": {
+ "id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",
+ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",
+ "project_id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",
+ "state": "ready",
+ "active": true,
+ "source": "archive",
+ "file_count": 37,
+ "total_bytes": 482113,
+ "new_bytes": 20411,
+ "manifest_sha256": "a3f1c9e0b4d2e6f8a1c3e5f7091b2d4f6a8c0e2f4a6b8d0f2e4c6a8b0d2f4e6a",
+ "failure_code": null,
+ "created_by": "app:app_01JAD7A8B9C0D1E2F3G4H5J6K7",
+ "created_at": "2026-09-24T09:40:12.000Z",
+ "log": [
+ {
+ "level": "info",
+ "message": "validated 37 files, 470.8 KiB (paths, file types, sizes)",
+ "at": "2026-09-24T09:40:12.000Z"
+ },
+ {
+ "level": "info",
+ "message": "activated: the site now serves this deploy",
+ "at": "2026-09-24T09:40:12.000Z"
+ }
+ ]
+ },
+ "activated": true,
+ "url": "https://harbour-radio.openvibe.website"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
+{
+ "service": "live",
+ "release": "d4e849f2",
+ "commit": null,
+ "origin": "https://openvibe.live",
+ "deployed_at": "2026-09-26T08:00:05.000Z"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
+{
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",
+ "site": "my-site",
+ "deploy_id": "dpl_01JAB2C3D4E5F6G7H8J9K0MNPS",
+ "rollback": false
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
+{
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",
+ "site": "my-site",
+ "deploy_id": "dpl_01JAB2C3D4E5F6G7H8J9K0MNPT",
+ "previous_deploy_id": "dpl_01JAB2C3D4E5F6G7H8J9K0MNPS",
+ "rollback": true
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
+{
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",
+ "site": "my-site",
+ "deploy_id": "dpl_01JAB2C3D4E5F6G7H8J9K0MNPS",
+ "previous_deploy_id": null,
+ "rollback": false
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
+{
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "site_id": "site_01JAB2C3D4E5F6G7H8J9K0MNPR",
+ "site": "alpha-site",
+ "source": "files",
+ "file_count": 1,
+ "total_bytes": 1,
+ "manifest_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
+{
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",
+ "site": "alpha-site",
+ "source": "ftp",
+ "file_count": 1,
+ "total_bytes": 1,
+ "manifest_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
+{
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",
+ "site": "alpha-site",
+ "source": "git",
+ "file_count": 3,
+ "total_bytes": 901,
+ "manifest_sha256": "0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
+{
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",
+ "site": "alpha-site",
+ "source": "archive",
+ "file_count": 12,
+ "total_bytes": 48213,
+ "manifest_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
+{
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",
+ "site": "alpha-site",
+ "code": "deploy.invalid",
+ "problems": [
+ "file.bad_path",
+ "file.bad_path",
+ "file.bad_path",
+ "file.bad_path",
+ "file.bad_path",
+ "file.bad_path",
+ "file.bad_path",
+ "file.bad_path",
+ "file.bad_path",
+ "file.bad_path",
+ "file.bad_path"
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
+{
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",
+ "site": "alpha-site",
+ "code": "deploy.too_large",
+ "problems": [
+ "file.too_large"
+ ],
+ "content": "<html>"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
+{
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",
+ "site": "alpha-site",
+ "code": "storage.space_full",
+ "problems": []
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
+{
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",
+ "site": "alpha-site",
+ "code": "deploy.too_large",
+ "problems": [
+ "file.too_large"
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
+{
+ "id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",
+ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",
+ "project_id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",
+ "state": "ready",
+ "active": true,
+ "source": "tarball",
+ "file_count": 37,
+ "total_bytes": 482113,
+ "new_bytes": 20411,
+ "manifest_sha256": "a3f1c9e0b4d2e6f8a1c3e5f7091b2d4f6a8c0e2f4a6b8d0f2e4c6a8b0d2f4e6a",
+ "failure_code": null,
+ "created_by": "app:app_01JAD7A8B9C0D1E2F3G4H5J6K7",
+ "created_at": "2026-09-24T09:40:12.000Z"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
+{
+ "id": "dpl_01JAD3D4E5F6G7H8J9K0M1N2P3",
+ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",
+ "project_id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",
+ "state": "failed",
+ "active": false,
+ "source": "files",
+ "file_count": 0,
+ "total_bytes": 0,
+ "new_bytes": 0,
+ "manifest_sha256": null,
+ "failure_code": "deploy.server_code",
+ "created_by": "app:app_01JAD7A8B9C0D1E2F3G4H5J6K7",
+ "created_at": "2026-09-24T09:40:12.000Z",
+ "log": [
+ {
+ "level": "error",
+ "message": "deploy.server_code: api/index.php is server-side code; Host serves static files only",
+ "at": "2026-09-24T08:15:00.000Z"
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
+{
+ "id": "dpl_01JAG7H8J9K0M1N2P3Q4R5S6T7",
+ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",
+ "project_id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",
+ "state": "ready",
+ "active": true,
+ "source": "git",
+ "git": {
+ "provider": "github",
+ "repo_url": "https://github.com/OpenVibers/example-site",
+ "ref": "refs/heads/main",
+ "commit_sha": "a3f1c9e0b4d2e6f8a1c3e5f7091b2d4f6a8c0e2f"
+ },
+ "file_count": 37,
+ "total_bytes": 482113,
+ "new_bytes": 20411,
+ "manifest_sha256": "a3f1c9e0b4d2e6f8a1c3e5f7091b2d4f6a8c0e2f4a6b8d0f2e4c6a8b0d2f4e6a",
+ "failure_code": null,
+ "created_by": "app:app_01JAD7A8B9C0D1E2F3G4H5J6K7",
+ "created_at": "2026-09-24T09:40:12.000Z"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
+{
+ "id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",
+ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",
+ "project_id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",
+ "state": "ready",
+ "active": true,
+ "source": "archive",
+ "file_count": 37,
+ "total_bytes": 482113,
+ "new_bytes": 20411,
+ "manifest_sha256": "a3f1c9e0b4d2e6f8a1c3e5f7091b2d4f6a8c0e2f4a6b8d0f2e4c6a8b0d2f4e6a",
+ "failure_code": null,
+ "created_by": "app:app_01JAD7A8B9C0D1E2F3G4H5J6K7",
+ "created_at": "2026-09-24T09:40:12.000Z"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
+{
+ "domain": "www.harbourradio.example"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
+{
+ "hostname": "www.harbourradio.example"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{}
new file mode 100644
new file mode 100644
@@ -0,0 +1,30 @@
@@ -0,0 +1,30 @@
+{
+ "domain": {
+ "id": "dom_01JAD6D6E7F8G9H0J1K2M3N4P5",
+ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",
+ "hostname": "www.harbourradio.example",
+ "kind": "apex",
+ "status": "pending",
+ "served": false,
+ "verified_at": null,
+ "last_checked_at": "2026-09-24T09:40:12.000Z",
+ "last_error": "no TXT record at _openvibe-space.www.harbourradio.example yet",
+ "instructions": {
+ "verification": {
+ "type": "TXT",
+ "name": "_openvibe-space.www.harbourradio.example",
+ "value": "openvibe-space-verification=5f0c2a9e1b7d4c3a8e6f0b2d4c6a8e0f1a3c5e7b",
+ "note": "keep this record: Host re-checks it daily"
+ },
+ "routing": [
+ {
+ "type": "CNAME",
+ "name": "www.harbourradio.example",
+ "value": "harbour-radio.openvibe.website",
+ "note": "for a subdomain such as www.example.org"
+ }
+ ],
+ "tls": "after verification an operator issues the certificate (Let's Encrypt); HTTPS starts working once it is installed"
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,30 @@
@@ -0,0 +1,30 @@
+{
+ "domain": {
+ "id": "dom_01JAD6D6E7F8G9H0J1K2M3N4P5",
+ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",
+ "hostname": "www.harbourradio.example",
+ "kind": "custom",
+ "status": "pending",
+ "served": false,
+ "verified_at": null,
+ "last_checked_at": "2026-09-24T09:40:12.000Z",
+ "last_error": "no TXT record at _openvibe-space.www.harbourradio.example yet",
+ "instructions": {
+ "verification": {
+ "type": "TXT",
+ "name": "_openvibe-space.www.harbourradio.example",
+ "value": "openvibe-space-verification=5f0c2a9e1b7d4c3a8e6f0b2d4c6a8e0f1a3c5e7b",
+ "note": "keep this record: Host re-checks it daily"
+ },
+ "routing": [
+ {
+ "type": "CNAME",
+ "name": "www.harbourradio.example",
+ "value": "harbour-radio.openvibe.website",
+ "note": "for a subdomain such as www.example.org"
+ }
+ ],
+ "tls": "after verification an operator issues the certificate (Let's Encrypt); HTTPS starts working once it is installed"
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
+{
+ "deleted": true
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,43 @@
@@ -0,0 +1,43 @@
+{
+ "domains": [
+ {
+ "id": "dom_01JAD5D6E7F8G9H0J1K2M3N4P5",
+ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",
+ "hostname": "harbour-radio.openvibe.website",
+ "kind": "default",
+ "status": "verified",
+ "served": true,
+ "verified_at": "2026-09-24T08:15:00.000Z",
+ "last_checked_at": null,
+ "last_error": null
+ },
+ {
+ "id": "dom_01JAD6D6E7F8G9H0J1K2M3N4P5",
+ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",
+ "hostname": "www.harbourradio.example",
+ "kind": "custom",
+ "status": "pending",
+ "served": false,
+ "verified_at": null,
+ "last_checked_at": "2026-09-24T09:40:12.000Z",
+ "last_error": "no TXT record at _openvibe-space.www.harbourradio.example yet",
+ "instructions": {
+ "verification": {
+ "type": "TXT",
+ "name": "_openvibe-space.www.harbourradio.example",
+ "value": "openvibe-space-verification=5f0c2a9e1b7d4c3a8e6f0b2d4c6a8e0f1a3c5e7b",
+ "note": "keep this record: Host re-checks it daily"
+ },
+ "routing": [
+ {
+ "type": "CNAME",
+ "name": "www.harbourradio.example",
+ "value": "harbour-radio.openvibe.website",
+ "note": "for a subdomain such as www.example.org"
+ }
+ ],
+ "tls": "after verification an operator issues the certificate (Let's Encrypt); HTTPS starts working once it is installed"
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
+{
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",
+ "site": "Alpha Site",
+ "hostname": "www.example.org"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
+{
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",
+ "site": "alpha-site"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
+{
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "site_id": "sit_01JAB2C3D4E5F6G7H8J9K0MNPR",
+ "site": "alpha-site",
+ "hostname": "www.example.org"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
+{
+ "id": "dom_01JAD5D6E7F8G9H0J1K2M3N4P5",
+ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",
+ "hostname": "harbour-radio.openvibe.website",
+ "kind": "default",
+ "status": "active",
+ "served": true,
+ "verified_at": "2026-09-24T08:15:00.000Z",
+ "last_checked_at": null,
+ "last_error": null
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
+{
+ "id": "dom_01JAD6D6E7F8G9H0J1K2M3N4P5",
+ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",
+ "hostname": "www.harbourradio.example",
+ "kind": "custom",
+ "status": "pending",
+ "served": false,
+ "verified_at": null,
+ "last_checked_at": "2026-09-24T09:40:12.000Z",
+ "last_error": "no TXT record at _openvibe-space.www.harbourradio.example yet",
+ "instructions": {
+ "verification": {
+ "type": "TXT",
+ "name": "_openvibe-space.www.harbourradio.example",
+ "value": "openvibe-space-verification=5f0c2a9e1b7d4c3a8e6f0b2d4c6a8e0f1a3c5e7b",
+ "note": "keep this record: Host re-checks it daily"
+ },
+ "routing": [
+ {
+ "type": "CNAME",
+ "name": "www.harbourradio.example",
+ "value": "harbour-radio.openvibe.website",
+ "note": "for a subdomain such as www.example.org"
+ }
+ ],
+ "tls": "after verification an operator issues the certificate (Let's Encrypt); HTTPS starts working once it is installed"
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
+{
+ "id": "dom_01JAD5D6E7F8G9H0J1K2M3N4P5",
+ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",
+ "hostname": "harbour-radio.openvibe.website",
+ "kind": "default",
+ "status": "verified",
+ "served": true,
+ "verified_at": "2026-09-24T08:15:00.000Z",
+ "last_checked_at": null,
+ "last_error": null
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
+{
+ "id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",
+ "name": "Harbour Radio",
+ "environment": "staging",
+ "network_project_id": null,
+ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "role": "owner",
+ "created_at": "2026-09-24T08:15:00.000Z",
+ "takedown": null
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
+{
+ "id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",
+ "name": "Harbour Radio",
+ "environment": "production",
+ "network_project_id": null,
+ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "role": "owner",
+ "created_at": "2026-09-24T08:15:00.000Z",
+ "takedown": null
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
+{
+ "id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",
+ "name": "Harbour Radio",
+ "environment": "production",
+ "network_project_id": null,
+ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "role": "owner",
+ "created_at": "2026-09-24T08:15:00.000Z",
+ "takedown": null,
+ "quota": {
+ "storage_bytes": 1073741824,
+ "deploys_per_day": 50,
+ "max_files": 5000,
+ "max_file_bytes": 26214400,
+ "sites": 5,
+ "custom_domains": 5
+ },
+ "usage": {
+ "storage_bytes": 482113,
+ "objects": 37,
+ "deploys_last_24h": 3,
+ "sites": 1,
+ "custom_domains": 1
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "redirects": [{ "from": "/a", "to": "/b", "status": 303 }] }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "headers": { "X-Frame-Options": "SAMEORIGIN" }, "redirects": [{ "from": "/docs", "to": "/docs/" }], "spa": true }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "config": { "headers": {}, "redirects": [] } }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "config": { "headers": { "X-Frame-Options": "DENY" }, "redirects": [], "spa": false } }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "headers": {}, "redirects": [{ "from": "/a", "to": "//evil.example/", "status": 301 }], "spa": false }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "headers": { "X-Frame-Options": "DENY", "Access-Control-Allow-Origin": "*" }, "redirects": [{ "from": "/old", "to": "/new", "status": 301 }], "spa": true }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "headers": {}, "redirects": [], "spa": false }
new file mode 100644
new file mode 100644
@@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
+{
+ "name": 42,
+ "role": "admin",
+ "reason": "",
+ "storage_bytes": -1,
+ "note": 5
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
+{
+ "note": "Page removed by the owner"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
+{
+ "role": "deployer"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
+{
+ "name": "Harbour Radio",
+ "environment": "production"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
+{
+ "storage_bytes": 2147483648,
+ "custom_domains": null
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{}
new file mode 100644
new file mode 100644
@@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
+{
+ "name": "harbour-radio"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
+{
+ "reason": "Phishing page reported by two users"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
+{
+ "deleted": false
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,27 @@
@@ -0,0 +1,27 @@
+{
+ "project": {
+ "id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",
+ "name": "Harbour Radio",
+ "environment": "production",
+ "network_project_id": null,
+ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "role": "owner",
+ "created_at": "2026-09-24T08:15:00.000Z",
+ "takedown": null,
+ "quota": {
+ "storage_bytes": 1073741824,
+ "deploys_per_day": 50,
+ "max_files": 5000,
+ "max_file_bytes": 26214400,
+ "sites": 5,
+ "custom_domains": 5
+ },
+ "usage": {
+ "storage_bytes": 482113,
+ "objects": 37,
+ "deploys_last_24h": 3,
+ "sites": 1,
+ "custom_domains": 1
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
+{
+ "deleted": true
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
+{
+ "deleted": true,
+ "objects_removed": 4
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
+{
+ "lifted": true
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
+{
+ "members": [
+ {
+ "principal": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "role": "owner",
+ "added_at": "2026-09-24T08:15:00.000Z"
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,52 @@
@@ -0,0 +1,52 @@
+{
+ "project": {
+ "id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",
+ "name": "Harbour Radio",
+ "environment": "production",
+ "network_project_id": null,
+ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "role": "owner",
+ "created_at": "2026-09-24T08:15:00.000Z",
+ "takedown": null,
+ "quota": {
+ "storage_bytes": 1073741824,
+ "deploys_per_day": 50,
+ "max_files": 5000,
+ "max_file_bytes": 26214400,
+ "sites": 5,
+ "custom_domains": 5
+ },
+ "usage": {
+ "storage_bytes": 482113,
+ "objects": 37,
+ "deploys_last_24h": 3,
+ "sites": 1,
+ "custom_domains": 1
+ }
+ },
+ "sites": [
+ {
+ "id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",
+ "project_id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",
+ "name": "harbour-radio",
+ "hostname": "harbour-radio.openvibe.website",
+ "url": "https://harbour-radio.openvibe.website",
+ "active_deploy_id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",
+ "takedown": null,
+ "created_at": "2026-09-24T08:15:00.000Z",
+ "updated_at": "2026-09-24T09:40:12.000Z"
+ }
+ ],
+ "members": [
+ {
+ "principal": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "role": "owner",
+ "added_at": "2026-09-24T08:15:00.000Z"
+ },
+ {
+ "principal": "app:app_01JAD7A8B9C0D1E2F3G4H5J6K7",
+ "role": "deployer",
+ "added_at": "2026-09-24T08:15:00.000Z"
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
+{
+ "projects": [
+ {
+ "id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",
+ "name": "Harbour Radio",
+ "environment": "production",
+ "network_project_id": null,
+ "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "role": "owner",
+ "created_at": "2026-09-24T08:15:00.000Z",
+ "takedown": null
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
+{
+ "quota": {
+ "storage_bytes": 1073741824,
+ "deploys_per_day": 50,
+ "max_files": 5000,
+ "max_file_bytes": 26214400,
+ "sites": 5,
+ "custom_domains": 5
+ },
+ "usage": {
+ "storage_bytes": 482113,
+ "objects": 37,
+ "deploys_last_24h": 3,
+ "sites": 1,
+ "custom_domains": 1
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
+{
+ "site": {
+ "id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",
+ "project_id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",
+ "name": "harbour-radio",
+ "hostname": "harbour-radio.openvibe.website",
+ "url": "https://harbour-radio.openvibe.website",
+ "active_deploy_id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",
+ "takedown": null,
+ "created_at": "2026-09-24T08:15:00.000Z",
+ "updated_at": "2026-09-24T09:40:12.000Z"
+ },
+ "domains": [
+ {
+ "id": "dom_01JAD5D6E7F8G9H0J1K2M3N4P5",
+ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",
+ "hostname": "harbour-radio.openvibe.website",
+ "kind": "default",
+ "status": "verified",
+ "served": true,
+ "verified_at": "2026-09-24T08:15:00.000Z",
+ "last_checked_at": null,
+ "last_error": null
+ },
+ {
+ "id": "dom_01JAD6D6E7F8G9H0J1K2M3N4P5",
+ "site_id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",
+ "hostname": "www.harbourradio.example",
+ "kind": "custom",
+ "status": "pending",
+ "served": false,
+ "verified_at": null,
+ "last_checked_at": "2026-09-24T09:40:12.000Z",
+ "last_error": "no TXT record at _openvibe-space.www.harbourradio.example yet",
+ "instructions": {
+ "verification": {
+ "type": "TXT",
+ "name": "_openvibe-space.www.harbourradio.example",
+ "value": "openvibe-space-verification=5f0c2a9e1b7d4c3a8e6f0b2d4c6a8e0f1a3c5e7b",
+ "note": "keep this record: Host re-checks it daily"
+ },
+ "routing": [
+ {
+ "type": "CNAME",
+ "name": "www.harbourradio.example",
+ "value": "harbour-radio.openvibe.website",
+ "note": "for a subdomain such as www.example.org"
+ }
+ ],
+ "tls": "after verification an operator issues the certificate (Let's Encrypt); HTTPS starts working once it is installed"
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
+{
+ "sites": [
+ {
+ "id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",
+ "project_id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",
+ "name": "harbour-radio",
+ "hostname": "harbour-radio.openvibe.website",
+ "url": "https://harbour-radio.openvibe.website",
+ "active_deploy_id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",
+ "takedown": null,
+ "created_at": "2026-09-24T08:15:00.000Z",
+ "updated_at": "2026-09-24T09:40:12.000Z"
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
+{
+ "takedown": {
+ "scope": "site",
+ "reason": "Phishing page reported by two users",
+ "since": "2026-09-24T09:40:12.000Z"
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
+{
+ "id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",
+ "project_id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",
+ "name": "Harbour-Radio",
+ "hostname": "harbour-radio.openvibe.website",
+ "url": "https://harbour-radio.openvibe.website",
+ "active_deploy_id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",
+ "takedown": null,
+ "created_at": "2026-09-24T08:15:00.000Z",
+ "updated_at": "2026-09-24T09:40:12.000Z"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
+{
+ "id": "sit_01JAD2S3T4V5W6X7Y8Z9A0B1C2",
+ "project_id": "prj_01JAD1P2R3J4K5M6N7P8Q9R0S1",
+ "name": "harbour-radio",
+ "hostname": "harbour-radio.openvibe.website",
+ "url": "https://harbour-radio.openvibe.website",
+ "active_deploy_id": "dpl_01JAD4D4E5F6G7H8J9K0M1N2P3",
+ "takedown": null,
+ "created_at": "2026-09-24T08:15:00.000Z",
+ "updated_at": "2026-09-24T09:40:12.000Z"
+}
@@ -77001,6 +77001,1648 @@
@@ -77001,6 +77001,1648 @@
}
}
]
]
}
}
+ },
+ "space.deploy-create-request": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/deploy-create-request.v1.json",
+ "title": "SpaceDeployCreateRequest",
+ "description": "space.deploy-create-request@1: bodies of space.deploy.create on OpenVibe.Space (a deployer or above of the project; an app principal added as deployer can deploy from CI). POST /api/v1/sites/:id/deploys uploads a deploy: the raw body is a tar or tar.gz archive (Content-Type application/gzip, application/x-gzip, application/x-tar or application/octet-stream), or multipart/form-data with archive=<one archive> or files=<file>… (each part's filename is its path); optional fields activate=1, root=<subdirectory to deploy> and strip=folder (removes a shared first segment) also work as query parameters. It is validated (path traversal, links, special and hidden files, server-side code, file types, sizes and quotas) and never executed; a refusal is a problem whose extra carries the failed deploy_id and its log (413 upload.too_large, quota.storage; 422 validation codes; 429 quota.deploys_per_day; 403 site.taken_down; 503 upload.busy; 507 storage.host_full). POST /api/v1/deploys/:id/activate { expected_active? } and POST /api/v1/sites/:id/rollback { deploy_id?, expected_active? } switch the site's pointer in one step (rollback without deploy_id goes to the deploy active before; 409 deploy.not_ready, site.active_changed when expected_active is stale, deploy.no_previous). GET /api/v1/sites/:id/deploys, /deploys/:id and /deploys/:id/log take no body.",
+ "anyOf": [
+ {
+ "type": "string",
+ "contentEncoding": "binary",
+ "description": "The upload itself: the tar or tar.gz bytes, or the multipart/form-data body (as common.binary@1)."
+ },
+ {
+ "type": "object",
+ "properties": {
+ "deploy_id": {
+ "anyOf": [
+ {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "expected_active": {
+ "anyOf": [
+ {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The deploy you saw active (null: none); refused with 409 site.active_changed when it moved."
+ }
+ }
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+ }
+ },
+ "space.deploy-create-result": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/deploy-create-result.v1.json",
+ "title": "SpaceDeployCreateResult",
+ "description": "space.deploy-create-result@1: answers of space.deploy.create on OpenVibe.Space. GET /api/v1/sites/:id/deploys?limit= → { active_deploy_id, deploys, activations } (newest first; the last 20 pointer switches). POST /api/v1/sites/:id/deploys → 201 { deploy, activated, url } (the ready deploy with its validation log; activated when activate=1 switched the site to it). GET /api/v1/deploys/:id → { deploy } with its files. GET /api/v1/deploys/:id/log → { deploy_id, state, log }. POST /api/v1/deploys/:id/activate and /sites/:id/rollback → { active_deploy_id, previous_deploy_id, changed } (changed false when it was already active). Emits space.deploy.created|activated|failed.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "active_deploy_id",
+ "deploys",
+ "activations"
+ ],
+ "properties": {
+ "active_deploy_id": {
+ "anyOf": [
+ {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "deploys": {
+ "type": "array",
+ "items": {
+ "$ref": "deploy.v1.json"
+ }
+ },
+ "activations": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "deploy_id",
+ "previous_deploy_id",
+ "kind",
+ "actor",
+ "at"
+ ],
+ "properties": {
+ "deploy_id": {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "previous_deploy_id": {
+ "anyOf": [
+ {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "kind": {
+ "enum": [
+ "activate",
+ "rollback"
+ ]
+ },
+ "actor": {
+ "type": "string"
+ },
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "deploy"
+ ],
+ "properties": {
+ "deploy": {
+ "$ref": "deploy.v1.json"
+ },
+ "activated": {
+ "type": "boolean"
+ },
+ "url": {
+ "type": "string"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "deploy_id",
+ "state",
+ "log"
+ ],
+ "properties": {
+ "deploy_id": {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "state": {
+ "enum": [
+ "ready",
+ "failed",
+ "deleted"
+ ]
+ },
+ "log": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "level",
+ "message",
+ "at"
+ ],
+ "properties": {
+ "level": {
+ "enum": [
+ "info",
+ "warn",
+ "error"
+ ]
+ },
+ "message": {
+ "type": "string"
+ },
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "active_deploy_id",
+ "previous_deploy_id",
+ "changed"
+ ],
+ "properties": {
+ "active_deploy_id": {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "previous_deploy_id": {
+ "anyOf": [
+ {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "changed": {
+ "type": "boolean"
+ }
+ }
+ }
+ ]
+ }
+ },
+ "space.deploy": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/deploy.v1.json",
+ "title": "SpaceDeploy",
+ "description": "space.deploy@1: one immutable static deploy on OpenVibe.Space (server/http/serialize.js deploy): ready (validated and stored, never executed) or failed (refused, with its failure code and log), whether the site serves it, its size and manifest hash. GET /api/v1/deploys/:id adds the files; the upload answer adds the log.",
+ "type": "object",
+ "required": [
+ "id",
+ "site_id",
+ "project_id",
+ "state",
+ "active",
+ "source",
+ "file_count",
+ "total_bytes",
+ "new_bytes",
+ "manifest_sha256",
+ "failure_code",
+ "created_by",
+ "created_at"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "site_id": {
+ "type": "string",
+ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "state": {
+ "enum": [
+ "ready",
+ "failed",
+ "deleted"
+ ]
+ },
+ "active": {
+ "type": "boolean"
+ },
+ "source": {
+ "enum": [
+ "archive",
+ "files",
+ "preview",
+ "git"
+ ]
+ },
+ "git": {
+ "type": "object",
+ "required": [
+ "provider",
+ "repo_url",
+ "ref",
+ "commit_sha"
+ ],
+ "properties": {
+ "provider": {
+ "type": "string"
+ },
+ "repo_url": {
+ "type": "string"
+ },
+ "ref": {
+ "type": "string"
+ },
+ "commit_sha": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{40}$"
+ }
+ },
+ "description": "Present on source git: the commit this deploy was built from (public provenance, never a credential)."
+ },
+ "file_count": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "total_bytes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "new_bytes": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "Bytes this deploy added to the project's store."
+ },
+ "manifest_sha256": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "pattern": "^[0-9a-f]{64}$"
+ },
+ "failure_code": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "created_by": {
+ "type": "string"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "files": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "path",
+ "sha256",
+ "size",
+ "content_type"
+ ],
+ "properties": {
+ "path": {
+ "type": "string"
+ },
+ "sha256": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$"
+ },
+ "size": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "content_type": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "log": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "level",
+ "message",
+ "at"
+ ],
+ "properties": {
+ "level": {
+ "enum": [
+ "info",
+ "warn",
+ "error"
+ ]
+ },
+ "message": {
+ "type": "string"
+ },
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "space.domain-manage-request": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/domain-manage-request.v1.json",
+ "title": "SpaceDomainManageRequest",
+ "description": "space.domain-manage-request@1: bodies of space.domain.manage on OpenVibe.Space (a maintainer or above of the project). POST /api/v1/sites/:id/domains { hostname } adds a custom domain, pending until its TXT record is verified: a fully qualified host name, lowercased (422 domain.invalid_hostname; 422 domain.reserved for any OpenVibe domain; 403 domain.sandbox in sandbox projects; 429 quota.custom_domains; 409 domain.exists, domain.taken). GET /api/v1/sites/:id/domains, POST /api/v1/domains/:id/verify (checks the TXT record now) and DELETE /api/v1/domains/:id (409 domain.default: the default domain goes only with the site) take no body.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "hostname"
+ ],
+ "properties": {
+ "hostname": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 254,
+ "description": "e.g. www.example.org (a trailing dot is dropped)."
+ }
+ }
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+ }
+ },
+ "space.domain-manage-result": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/domain-manage-result.v1.json",
+ "title": "SpaceDomainManageResult",
+ "description": "space.domain-manage-result@1: answers of space.domain.manage on OpenVibe.Space. GET /api/v1/sites/:id/domains → { domains } (the default domain first). POST /api/v1/sites/:id/domains → 201 { domain } with the DNS records to publish. POST /api/v1/domains/:id/verify → { domain } after the check (status verified and served once the TXT record matches, else last_error says what was found; emits space.domain.verified). DELETE /api/v1/domains/:id → { deleted: true }.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "domains"
+ ],
+ "properties": {
+ "domains": {
+ "type": "array",
+ "items": {
+ "$ref": "domain.v1.json"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "domain"
+ ],
+ "properties": {
+ "domain": {
+ "$ref": "domain.v1.json"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "deleted"
+ ],
+ "properties": {
+ "deleted": {
+ "const": true
+ }
+ }
+ }
+ ]
+ }
+ },
+ "space.domain": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/domain.v1.json",
+ "title": "SpaceDomain",
+ "description": "space.domain@1: one domain of a static site on OpenVibe.Space (server/http/serialize.js domain): the default <site>.openvibe.website, or a custom domain that is served only once its DNS TXT record is verified (re-checked daily; it lapses when the record is gone). A custom domain carries the records its owner must publish (the TXT verification value is public by design and shown only to members); TLS certificates are issued by an operator.",
+ "type": "object",
+ "required": [
+ "id",
+ "site_id",
+ "hostname",
+ "kind",
+ "status",
+ "served",
+ "verified_at",
+ "last_checked_at",
+ "last_error"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^dom_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "site_id": {
+ "type": "string",
+ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "hostname": {
+ "type": "string"
+ },
+ "kind": {
+ "enum": [
+ "default",
+ "custom"
+ ]
+ },
+ "status": {
+ "enum": [
+ "pending",
+ "verified",
+ "failed",
+ "lapsed"
+ ]
+ },
+ "served": {
+ "type": "boolean"
+ },
+ "verified_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "last_checked_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "last_error": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "instructions": {
+ "type": "object",
+ "required": [
+ "verification",
+ "routing",
+ "tls"
+ ],
+ "properties": {
+ "verification": {
+ "type": "object",
+ "required": [
+ "type",
+ "name",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "TXT"
+ },
+ "name": {
+ "type": "string",
+ "description": "_openvibe-space.<hostname>"
+ },
+ "value": {
+ "type": "string",
+ "description": "openvibe-space-verification=<token>"
+ },
+ "note": {
+ "type": "string"
+ }
+ }
+ },
+ "routing": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "type",
+ "name",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string"
+ },
+ "note": {
+ "type": "string"
+ }
+ }
+ },
+ "description": "A CNAME for a subdomain; A/AAAA for an apex domain when the operator configured them."
+ },
+ "tls": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ },
+ "space.project": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/project.v1.json",
+ "title": "SpaceProject",
+ "description": "space.project@1: one OpenVibe.Space tenant project (server/http/serialize.js project): owned by a person (usr_…), production or sandbox, with the caller's role in it and any staff takedown. POST /api/v1/projects and GET /api/v1/projects/:id add the effective quota and the usage.",
+ "type": "object",
+ "required": [
+ "id",
+ "name",
+ "environment",
+ "network_project_id",
+ "owner",
+ "role",
+ "created_at",
+ "takedown"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 80
+ },
+ "environment": {
+ "enum": [
+ "production",
+ "sandbox"
+ ]
+ },
+ "network_project_id": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "The OpenVibe.Network project once Network has projects."
+ },
+ "owner": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "role": {
+ "enum": [
+ "owner",
+ "maintainer",
+ "deployer",
+ "staff",
+ null
+ ],
+ "description": "The caller's role (staff: a Network admin who is not a member)."
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "takedown": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "required": [
+ "scope",
+ "reason",
+ "since"
+ ],
+ "description": "A staff takedown covering it: serving stops while the content is kept for review.",
+ "properties": {
+ "scope": {
+ "enum": [
+ "project",
+ "site"
+ ]
+ },
+ "reason": {
+ "type": "string"
+ },
+ "since": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ },
+ "quota": {
+ "type": "object",
+ "required": [
+ "storage_bytes",
+ "deploys_per_day",
+ "max_files",
+ "max_file_bytes",
+ "sites",
+ "custom_domains"
+ ],
+ "description": "The effective quota: the project's override where set, else its environment's default.",
+ "properties": {
+ "storage_bytes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "deploys_per_day": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "max_files": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "max_file_bytes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "sites": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "custom_domains": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ },
+ "usage": {
+ "type": "object",
+ "required": [
+ "storage_bytes",
+ "objects",
+ "deploys_last_24h",
+ "sites",
+ "custom_domains"
+ ],
+ "properties": {
+ "storage_bytes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "objects": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "deploys_last_24h": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "sites": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "custom_domains": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ }
+ }
+ }
+ },
+ "space.site-manage-request": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/site-manage-request.v1.json",
+ "title": "SpaceSiteManageRequest",
+ "description": "space.site-manage-request@1: bodies of space.site.manage on OpenVibe.Space (judged by the acting principal's project role too; a sandbox token is refused on production projects). POST /api/v1/projects { name, environment?, network_project_id? } creates a project owned by the person named with X-OV-Subject (403 project.owner_must_be_user; 422 project.invalid_name over 80 characters, project.invalid_environment; 429 quota.projects; 409 project.network_project_taken). PUT /api/v1/projects/:id/quota { storage_bytes?, deploys_per_day?, max_files?, max_file_bytes?, sites?, custom_domains? } (a Network staff user: 403 auth.staff_only; fields left out keep their value). PUT /api/v1/projects/:id/members/:principal { role } (the owner: 422 member.invalid_role, 409 member.owner_fixed). POST /api/v1/projects/:id/sites { name } (a maintainer; lowercased and trimmed, one DNS label of 3–40 characters: 422 site.invalid_name, site.name_reserved; 409 site.name_taken, site.name_held; 429 quota.sites). POST /api/v1/projects/:id/takedown and /sites/:id/takedown { reason } (staff: stop serving, keep the content; 409 takedown.exists); DELETE on those { note? } lifts it. GET /api/v1/projects, /projects/:id, /projects/:id/quota, /projects/:id/sites and /sites/:id, and DELETE /api/v1/projects/:id, /projects/:id/members/:principal, /sites/:id and /deploys/:id take no body (deleting refuses taken-down content for non-staff: 409 site.taken_down; an active deploy: 409 deploy.active).",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "1–80 characters once trimmed."
+ },
+ "environment": {
+ "enum": [
+ "production",
+ "sandbox",
+ null
+ ],
+ "description": "Default production; a sandbox credential creates sandbox projects only."
+ },
+ "network_project_id": {
+ "anyOf": [
+ {
+ "type": "string",
+ "pattern": "^([A-Za-z0-9_:-]{3,80})?$"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ }
+ }
+ },
+ {
+ "type": "object",
+ "properties": {
+ "storage_bytes": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 0,
+ "description": "A non-negative integer, or null for the environment default."
+ },
+ "deploys_per_day": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 0,
+ "description": "A non-negative integer, or null for the environment default."
+ },
+ "max_files": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 0,
+ "description": "A non-negative integer, or null for the environment default."
+ },
+ "max_file_bytes": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 0,
+ "description": "A non-negative integer, or null for the environment default."
+ },
+ "sites": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 0,
+ "description": "A non-negative integer, or null for the environment default."
+ },
+ "custom_domains": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 0,
+ "description": "A non-negative integer, or null for the environment default."
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "role"
+ ],
+ "properties": {
+ "role": {
+ "enum": [
+ "owner",
+ "maintainer",
+ "deployer"
+ ],
+ "description": "Only a person (usr_…) can be an owner."
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "pattern": "^\\s*[A-Za-z0-9](?:[A-Za-z0-9]|-(?=[A-Za-z0-9])){2,39}\\s*$",
+ "description": "The site name (lowercased)."
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "reason"
+ ],
+ "properties": {
+ "reason": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Members see it."
+ }
+ }
+ },
+ {
+ "type": "object",
+ "properties": {
+ "note": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "Cut to 500 characters."
+ }
+ }
+ },
+ {
+ "$ref": "../common/no-body.v1.json"
+ }
+ ]
+ }
+ },
+ "space.site-manage-result": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/site-manage-result.v1.json",
+ "title": "SpaceSiteManageResult",
+ "description": "space.site-manage-result@1: answers of space.site.manage on OpenVibe.Space. GET /api/v1/projects → { projects } (the caller's projects with their role). POST /api/v1/projects → 201 { project } (with quota and usage). GET /api/v1/projects/:id → { project, sites, members }. GET and PUT /api/v1/projects/:id/quota → { quota, usage }. PUT and DELETE /api/v1/projects/:id/members/:principal → { members }. GET /api/v1/projects/:id/sites → { sites }. POST /api/v1/projects/:id/sites → 201 { site }. GET /api/v1/sites/:id → { site, domains }. DELETE /api/v1/projects/:id and /sites/:id → { deleted: true } (serving stops at once; the objects are removed). DELETE /api/v1/deploys/:id → { deleted: true, objects_removed }. POST …/takedown → 201 { takedown }; DELETE …/takedown → { lifted: true }.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "projects"
+ ],
+ "properties": {
+ "projects": {
+ "type": "array",
+ "items": {
+ "$ref": "project.v1.json"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "project"
+ ],
+ "properties": {
+ "project": {
+ "$ref": "project.v1.json"
+ },
+ "sites": {
+ "type": "array",
+ "items": {
+ "$ref": "site.v1.json"
+ }
+ },
+ "members": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "principal",
+ "role",
+ "added_at"
+ ],
+ "properties": {
+ "principal": {
+ "type": "string",
+ "description": "usr_…, app:app_… or svc:…"
+ },
+ "role": {
+ "enum": [
+ "owner",
+ "maintainer",
+ "deployer"
+ ]
+ },
+ "added_at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "quota",
+ "usage"
+ ],
+ "properties": {
+ "quota": {
+ "type": "object",
+ "required": [
+ "storage_bytes",
+ "deploys_per_day",
+ "max_files",
+ "max_file_bytes",
+ "sites",
+ "custom_domains"
+ ],
+ "description": "The effective quota: the project's override where set, else its environment's default.",
+ "properties": {
+ "storage_bytes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "deploys_per_day": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "max_files": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "max_file_bytes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "sites": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "custom_domains": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ },
+ "usage": {
+ "type": "object",
+ "required": [
+ "storage_bytes",
+ "objects",
+ "deploys_last_24h",
+ "sites",
+ "custom_domains"
+ ],
+ "properties": {
+ "storage_bytes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "objects": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "deploys_last_24h": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "sites": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "custom_domains": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "members"
+ ],
+ "properties": {
+ "members": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "principal",
+ "role",
+ "added_at"
+ ],
+ "properties": {
+ "principal": {
+ "type": "string",
+ "description": "usr_…, app:app_… or svc:…"
+ },
+ "role": {
+ "enum": [
+ "owner",
+ "maintainer",
+ "deployer"
+ ]
+ },
+ "added_at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "sites"
+ ],
+ "properties": {
+ "sites": {
+ "type": "array",
+ "items": {
+ "$ref": "site.v1.json"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "site"
+ ],
+ "properties": {
+ "site": {
+ "$ref": "site.v1.json"
+ },
+ "domains": {
+ "type": "array",
+ "items": {
+ "$ref": "domain.v1.json"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "deleted"
+ ],
+ "properties": {
+ "deleted": {
+ "const": true
+ },
+ "objects_removed": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "takedown"
+ ],
+ "properties": {
+ "takedown": {
+ "type": "object",
+ "required": [
+ "scope",
+ "reason",
+ "since"
+ ],
+ "description": "The takedown now in force.",
+ "properties": {
+ "scope": {
+ "enum": [
+ "project",
+ "site"
+ ]
+ },
+ "reason": {
+ "type": "string"
+ },
+ "since": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "lifted"
+ ],
+ "properties": {
+ "lifted": {
+ "const": true
+ }
+ }
+ }
+ ]
+ }
+ },
+ "space.site": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/site.v1.json",
+ "title": "SpaceSite",
+ "description": "space.site@1: one static site on OpenVibe.Space (server/http/serialize.js site): its name (one DNS label), the default hostname and URL it is served at, the deploy it serves (one pointer; null until a deploy is activated) and any staff takedown.",
+ "type": "object",
+ "required": [
+ "id",
+ "project_id",
+ "name",
+ "hostname",
+ "url",
+ "active_deploy_id",
+ "takedown",
+ "created_at",
+ "updated_at"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "name": {
+ "type": "string",
+ "pattern": "^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){2,39}$"
+ },
+ "hostname": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string"
+ },
+ "active_deploy_id": {
+ "anyOf": [
+ {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "takedown": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "required": [
+ "scope",
+ "reason",
+ "since"
+ ],
+ "description": "A staff takedown covering it: serving stops while the content is kept for review.",
+ "properties": {
+ "scope": {
+ "enum": [
+ "project",
+ "site"
+ ]
+ },
+ "reason": {
+ "type": "string"
+ },
+ "since": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ }
+ },
+ "space.site-config": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/site-config.v1.json",
+ "title": "SpaceSiteConfig",
+ "description": "space.site-config@1: one Host site's serving configuration (plan T12 J3). headers are applied to every response of the site before the platform's own, which always win; the platform's security, routing, caching, scope and transport headers (Content-Security-Policy, Strict-Transport-Security, Set-Cookie, Cache-Control, Content-Type, Location, Service-Worker-Allowed, Alt-Svc and the rest of Host's reserved list) and every X-Forwarded-* header are refused. redirects are local paths only (no scheme, host, protocol-relative URL, backslash or control character, also after percent-decoding). spa serves /index.html for an extensionless path that has no file. A site with no configuration answers { headers: {}, redirects: [], spa: false }.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "headers",
+ "redirects",
+ "spa"
+ ],
+ "properties": {
+ "headers": {
+ "type": "object",
+ "maxProperties": 50,
+ "propertyNames": {
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9-]{0,63}$"
+ },
+ "additionalProperties": {
+ "type": "string",
+ "maxLength": 1024
+ }
+ },
+ "redirects": {
+ "type": "array",
+ "maxItems": 200,
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "from",
+ "to",
+ "status"
+ ],
+ "properties": {
+ "from": {
+ "type": "string",
+ "pattern": "^/(?!/)",
+ "maxLength": 1024
+ },
+ "to": {
+ "type": "string",
+ "pattern": "^/(?!/)",
+ "maxLength": 1024
+ },
+ "status": {
+ "enum": [
+ 301,
+ 302,
+ 307,
+ 308
+ ]
+ }
+ }
+ }
+ },
+ "spa": {
+ "type": "boolean"
+ }
+ }
+ }
+ },
+ "space.site-config-request": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/site-config-request.v1.json",
+ "title": "SpaceSiteConfigRequest",
+ "description": "space.site-config-request@1: the body of PUT /api/v1/sites/:id/config on OpenVibe.Space (space.site.config; the acting principal must be a maintainer or owner of the site's project). { headers?, redirects?, spa? } replaces the whole configuration: a field left out is reset to its default. A redirect's status defaults to 301. Refusals are 422 problem+json: site_config.reserved_header, site_config.header_name, site_config.header_value (longer than 1024 characters or a control character), site_config.too_many (over 50 headers or 200 redirects), site_config.redirect (not a local path, a duplicate from, or a status outside 301/302/307/308), site_config.headers and site_config.redirects (wrong type). GET and DELETE /api/v1/sites/:id/config take no body (DELETE resets to the defaults).",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "headers": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "redirects": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "from",
+ "to"
+ ],
+ "properties": {
+ "from": {
+ "type": "string"
+ },
+ "to": {
+ "type": "string"
+ },
+ "status": {
+ "enum": [
+ 301,
+ 302,
+ 307,
+ 308
+ ]
+ }
+ }
+ }
+ },
+ "spa": {
+ "type": "boolean"
+ }
+ }
+ }
+ },
+ "space.site-config-result": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/space/site-config-result.v1.json",
+ "title": "SpaceSiteConfigResult",
+ "description": "space.site-config-result@1: every answer of space.site.config on OpenVibe.Space. GET, PUT and DELETE /api/v1/sites/:id/config → { config } (after a PUT, the configuration as stored; after a DELETE, the defaults).",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "config"
+ ],
+ "properties": {
+ "config": {
+ "$ref": "site-config.v1.json"
+ }
+ }
+ }
+ },
+ "space.deploy.activated": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/space.deploy.activated.v1.json",
+ "title": "SpaceDeployActivatedPayload",
+ "description": "space.deploy.activated v1 (OpenVibe.Space server/domain/deploys.js switchPointer, Stage B tenant hosting). A tenant site's active deploy changed, written into Host's event outbox in the transaction that moves the pointer (activate or rollback). Envelope: subject { type: deploy, id: <deploy id> }, visibility internal, priority low, actor the person or app that switched it. A network service's release going live is space.release.published, not this.",
+ "type": "object",
+ "required": [
+ "project_id",
+ "site_id",
+ "site",
+ "deploy_id",
+ "previous_deploy_id",
+ "rollback"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "site_id": {
+ "type": "string",
+ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "site": {
+ "type": "string",
+ "pattern": "^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){2,39}$",
+ "description": "The site's name (<site>.openvibe.website)."
+ },
+ "deploy_id": {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "previous_deploy_id": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The deploy that served before; null for the site's first activation."
+ },
+ "rollback": {
+ "type": "boolean",
+ "description": "true when the switch was a rollback."
+ }
+ }
+ }
+ },
+ "space.deploy.created": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/space.deploy.created.v1.json",
+ "title": "SpaceDeployCreatedPayload",
+ "description": "space.deploy.created v1 (OpenVibe.Space, subject deploy dpl_…, visibility internal): an upload or a Git ingest became an immutable, content-addressed deploy in the ready state. It is not yet serving: space.deploy.activated follows when it is made active. No file contents or paths.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "project_id",
+ "site_id",
+ "site",
+ "source",
+ "file_count",
+ "total_bytes",
+ "manifest_sha256"
+ ],
+ "properties": {
+ "project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "site_id": {
+ "type": "string",
+ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "site": {
+ "type": "string",
+ "pattern": "^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){2,39}$",
+ "description": "the site name (one DNS label; the site is served at <site>.openvibe.website)"
+ },
+ "source": {
+ "enum": [
+ "archive",
+ "files",
+ "preview",
+ "git"
+ ],
+ "description": "how the deploy arrived: an archive or individual files, a private preview upload, or a Git ingest (always a preview)"
+ },
+ "file_count": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "total_bytes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "manifest_sha256": {
+ "type": "string",
+ "pattern": "^[a-f0-9]{64}$",
+ "description": "sha256 of the deploy manifest (the content address of the whole deploy)"
+ }
+ }
+ }
+ },
+ "space.deploy.failed": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/space.deploy.failed.v1.json",
+ "title": "SpaceDeployFailedPayload",
+ "description": "space.deploy.failed v1 (OpenVibe.Space, subject deploy dpl_…, visibility internal): an upload was refused and recorded as a failed deploy with its log, so the owner can see why. Problem codes only, at most ten; never file contents.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "project_id",
+ "site_id",
+ "site",
+ "code",
+ "problems"
+ ],
+ "properties": {
+ "project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "site_id": {
+ "type": "string",
+ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "site": {
+ "type": "string",
+ "pattern": "^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){2,39}$",
+ "description": "the site name (one DNS label; the site is served at <site>.openvibe.website)"
+ },
+ "code": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)+$",
+ "description": "the refusal problem code (for example deploy.too_large or storage.space_full)"
+ },
+ "problems": {
+ "type": "array",
+ "maxItems": 10,
+ "items": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)*$"
+ },
+ "description": "the problem codes of the files that were refused"
+ }
+ }
+ }
+ },
+ "space.domain.verified": {
+ "version": "1.0.0",
+ "owner": "space",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/space.domain.verified.v1.json",
+ "title": "SpaceDomainVerifiedPayload",
+ "description": "space.domain.verified v1 (OpenVibe.Space, subject domain dom_…, visibility internal): a custom domain's TXT record (_openvibe-space.<hostname>) was found, and the domain now serves the site.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "project_id",
+ "site_id",
+ "site",
+ "hostname"
+ ],
+ "properties": {
+ "project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "site_id": {
+ "type": "string",
+ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "site": {
+ "type": "string",
+ "pattern": "^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){2,39}$",
+ "description": "the site name (one DNS label; the site is served at <site>.openvibe.website)"
+ },
+ "hostname": {
+ "type": "string",
+ "minLength": 4,
+ "maxLength": 253,
+ "description": "the custom domain, a fully qualified host name in lower case"
+ }
+ }
+ }
}
}
}
}
}
}
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Actor API",
"title": "OpenVibe.Actor API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.AI API",
"title": "OpenVibe.AI API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Billing API",
"title": "OpenVibe.Billing API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Blog API",
"title": "OpenVibe.Blog API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Bot API",
"title": "OpenVibe.Bot API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Chat API",
"title": "OpenVibe.Chat API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Community API",
"title": "OpenVibe.Community API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Coupons API",
"title": "OpenVibe.Coupons API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Deals API",
"title": "OpenVibe.Deals API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Events API",
"title": "OpenVibe.Events API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Food API",
"title": "OpenVibe.Food API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Games API",
"title": "OpenVibe.Games API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Host API",
"title": "OpenVibe.Host API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -169,6 +169,13 @@
@@ -169,6 +169,13 @@
"operations": 13,
"operations": 13,
"capabilities": 3
"capabilities": 3
},
},
+ {
+ "service": "space",
+ "name": "OpenVibe.Space",
+ "origin": "https://openvibe.space",
+ "operations": 36,
+ "capabilities": 5
+ },
{
{
"service": "tips",
"service": "tips",
"name": "OpenVibe.Tips",
"name": "OpenVibe.Tips",
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Inventory API",
"title": "OpenVibe.Inventory API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Live API",
"title": "OpenVibe.Live API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Media API",
"title": "OpenVibe.Media API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Network API",
"title": "OpenVibe.Network API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.News API",
"title": "OpenVibe.News API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenRestream API",
"title": "OpenRestream API",
- "version": "0.148.0",
- "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Reviews API",
"title": "OpenVibe.Reviews API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Run API",
"title": "OpenVibe.Run API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Search API",
"title": "OpenVibe.Search API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Services API",
"title": "OpenVibe.Services API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Sources API",
"title": "OpenVibe.Sources API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
new file mode 100644
new file mode 100644
@@ -0,0 +1,3971 @@
@@ -0,0 +1,3971 @@
+{
+ "openapi": "3.1.0",
+ "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
+ "info": {
+ "title": "OpenVibe.Space API",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "license": {
+ "name": "MIT",
+ "identifier": "MIT"
+ }
+ },
+ "servers": [
+ {
+ "url": "https://openvibe.space"
+ }
+ ],
+ "paths": {
+ "/api/v1/deploys/{id}": {
+ "delete": {
+ "operationId": "delete_api_v1_deploys_id",
+ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",
+ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",
+ "tags": [
+ "space.site"
+ ],
+ "x-openvibe-capabilities": [
+ "space.site.manage"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.site.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/space.site-manage-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "get": {
+ "operationId": "get_api_v1_deploys_id",
+ "summary": "Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys ",
+ "description": "**space.deploy.create** (first-party): Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys and their upload logs, activate a deploy and roll a site back to any earlier ready deploy (one atomic pointer switch). Also judged by the acting principal's project role (deployer or above); an app principal added to a project as deployer can deploy from CI. A site with a Git source also accepts a CI-built deploy at /sites/:id/source/deploys naming the ref and full commit_sha; it is always a preview and records that commit as immutable provenance.",
+ "tags": [
+ "space.deploy"
+ ],
+ "x-openvibe-capabilities": [
+ "space.deploy.create"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-deploy",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.deploy.create"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/space.deploy-create-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.deploy-create-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/deploys/{id}/activate": {
+ "post": {
+ "operationId": "post_api_v1_deploys_id_activate",
+ "summary": "Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys ",
+ "description": "**space.deploy.create** (first-party): Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys and their upload logs, activate a deploy and roll a site back to any earlier ready deploy (one atomic pointer switch). Also judged by the acting principal's project role (deployer or above); an app principal added to a project as deployer can deploy from CI. A site with a Git source also accepts a CI-built deploy at /sites/:id/source/deploys naming the ref and full commit_sha; it is always a preview and records that commit as immutable provenance.",
+ "tags": [
+ "space.deploy"
+ ],
+ "x-openvibe-capabilities": [
+ "space.deploy.create"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-deploy",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.deploy.create"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.deploy-create-request.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.deploy-create-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/deploys/{id}/log": {
+ "get": {
+ "operationId": "get_api_v1_deploys_id_log",
+ "summary": "Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys ",
+ "description": "**space.deploy.create** (first-party): Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys and their upload logs, activate a deploy and roll a site back to any earlier ready deploy (one atomic pointer switch). Also judged by the acting principal's project role (deployer or above); an app principal added to a project as deployer can deploy from CI. A site with a Git source also accepts a CI-built deploy at /sites/:id/source/deploys naming the ref and full commit_sha; it is always a preview and records that commit as immutable provenance.",
+ "tags": [
+ "space.deploy"
+ ],
+ "x-openvibe-capabilities": [
+ "space.deploy.create"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-deploy",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.deploy.create"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/space.deploy-create-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.deploy-create-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/domains/{id}": {
+ "delete": {
+ "operationId": "delete_api_v1_domains_id",
+ "summary": "Add a custom domain to a static site (never an OpenVibe domain; not in sandbox projects), read its DNS TXT verification and routing records, trigger a verification check, and remove it.",
+ "description": "**space.domain.manage** (first-party): Add a custom domain to a static site (never an OpenVibe domain; not in sandbox projects), read its DNS TXT verification and routing records, trigger a verification check, and remove it. A custom domain is served only after its TXT record is verified; TLS certificates are issued by an operator and never pass through this capability.",
+ "tags": [
+ "space.domain"
+ ],
+ "x-openvibe-capabilities": [
+ "space.domain.manage"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.domain.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/space.domain-manage-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.domain-manage-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/domains/{id}/verify": {
+ "post": {
+ "operationId": "post_api_v1_domains_id_verify",
+ "summary": "Add a custom domain to a static site (never an OpenVibe domain; not in sandbox projects), read its DNS TXT verification and routing records, trigger a verification check, and remove it.",
+ "description": "**space.domain.manage** (first-party): Add a custom domain to a static site (never an OpenVibe domain; not in sandbox projects), read its DNS TXT verification and routing records, trigger a verification check, and remove it. A custom domain is served only after its TXT record is verified; TLS certificates are issued by an operator and never pass through this capability.",
+ "tags": [
+ "space.domain"
+ ],
+ "x-openvibe-capabilities": [
+ "space.domain.manage"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.domain.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.domain-manage-request.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.domain-manage-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/projects": {
+ "get": {
+ "operationId": "get_api_v1_projects",
+ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",
+ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",
+ "tags": [
+ "space.site"
+ ],
+ "x-openvibe-capabilities": [
+ "space.site.manage"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.site.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/space.site-manage-request.v1"
+ },
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "operationId": "post_api_v1_projects",
+ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",
+ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",
+ "tags": [
+ "space.site"
+ ],
+ "x-openvibe-capabilities": [
+ "space.site.manage"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.site.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-request.v1"
+ }
+ }
+ }
+ },
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/projects/{id}": {
+ "delete": {
+ "operationId": "delete_api_v1_projects_id",
+ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",
+ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",
+ "tags": [
+ "space.site"
+ ],
+ "x-openvibe-capabilities": [
+ "space.site.manage"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.site.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/space.site-manage-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "get": {
+ "operationId": "get_api_v1_projects_id",
+ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",
+ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",
+ "tags": [
+ "space.site"
+ ],
+ "x-openvibe-capabilities": [
+ "space.site.manage"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.site.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/space.site-manage-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/projects/{id}/members/{principal}": {
+ "delete": {
+ "operationId": "delete_api_v1_projects_id_members_principal",
+ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",
+ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",
+ "tags": [
+ "space.site"
+ ],
+ "x-openvibe-capabilities": [
+ "space.site.manage"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.site.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/space.site-manage-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "principal",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "put": {
+ "operationId": "put_api_v1_projects_id_members_principal",
+ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",
+ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",
+ "tags": [
+ "space.site"
+ ],
+ "x-openvibe-capabilities": [
+ "space.site.manage"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.site.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-request.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "principal",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/projects/{id}/quota": {
+ "get": {
+ "operationId": "get_api_v1_projects_id_quota",
+ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",
+ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",
+ "tags": [
+ "space.site"
+ ],
+ "x-openvibe-capabilities": [
+ "space.site.manage"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.site.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/space.site-manage-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "put": {
+ "operationId": "put_api_v1_projects_id_quota",
+ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",
+ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",
+ "tags": [
+ "space.site"
+ ],
+ "x-openvibe-capabilities": [
+ "space.site.manage"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.site.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-request.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/projects/{id}/sites": {
+ "get": {
+ "operationId": "get_api_v1_projects_id_sites",
+ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",
+ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",
+ "tags": [
+ "space.site"
+ ],
+ "x-openvibe-capabilities": [
+ "space.site.manage"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.site.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/space.site-manage-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "operationId": "post_api_v1_projects_id_sites",
+ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",
+ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",
+ "tags": [
+ "space.site"
+ ],
+ "x-openvibe-capabilities": [
+ "space.site.manage"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.site.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-request.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/projects/{id}/takedown": {
+ "delete": {
+ "operationId": "delete_api_v1_projects_id_takedown",
+ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",
+ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",
+ "tags": [
+ "space.site"
+ ],
+ "x-openvibe-capabilities": [
+ "space.site.manage"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.site.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/space.site-manage-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "operationId": "post_api_v1_projects_id_takedown",
+ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",
+ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",
+ "tags": [
+ "space.site"
+ ],
+ "x-openvibe-capabilities": [
+ "space.site.manage"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.site.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-request.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/resources": {
+ "get": {
+ "operationId": "get_api_v1_resources",
+ "summary": "Read OpenVibe.Space's resource index (ADR-048): GET /api/v1/resources lists the resources Host owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:o",
+ "description": "**space.resource.read** (first-party): Read OpenVibe.Space's resource index (ADR-048): GET /api/v1/resources lists the resources Host owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:ovrn reads one by its OVRN (common.resource-summary@1). Every authority serves these for OpenVibe.Services to fan out and merge; Host's own resources are its static sites (kind space.site, id sit_<ULID>), the immutable deploys of those sites (kind space.deploy, id dpl_<ULID>) and their default and custom domains (kind space.domain, id dom_<ULID>), each with the project_id of the Network project that owns it. Host lists no projects; only Network lists projects. Planned until Host ships the index.",
+ "tags": [
+ "space.resource"
+ ],
+ "x-openvibe-capabilities": [
+ "space.resource.read"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "standard",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.resource.read"
+ ]
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.resource-list-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/resources/{ovrn}": {
+ "get": {
+ "operationId": "get_api_v1_resources_ovrn",
+ "summary": "Read OpenVibe.Space's resource index (ADR-048): GET /api/v1/resources lists the resources Host owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:o",
+ "description": "**space.resource.read** (first-party): Read OpenVibe.Space's resource index (ADR-048): GET /api/v1/resources lists the resources Host owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:ovrn reads one by its OVRN (common.resource-summary@1). Every authority serves these for OpenVibe.Services to fan out and merge; Host's own resources are its static sites (kind space.site, id sit_<ULID>), the immutable deploys of those sites (kind space.deploy, id dpl_<ULID>) and their default and custom domains (kind space.domain, id dom_<ULID>), each with the project_id of the Network project that owns it. Host lists no projects; only Network lists projects. Planned until Host ships the index.",
+ "tags": [
+ "space.resource"
+ ],
+ "x-openvibe-capabilities": [
+ "space.resource.read"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "standard",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.resource.read"
+ ]
+ }
+ ],
+ "parameters": [
+ {
+ "name": "ovrn",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/common.resource-list-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/sites/{id}": {
+ "delete": {
+ "operationId": "delete_api_v1_sites_id",
+ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",
+ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",
+ "tags": [
+ "space.site"
+ ],
+ "x-openvibe-capabilities": [
+ "space.site.manage"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.site.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/space.site-manage-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "get": {
+ "operationId": "get_api_v1_sites_id",
+ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",
+ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",
+ "tags": [
+ "space.site"
+ ],
+ "x-openvibe-capabilities": [
+ "space.site.manage"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.site.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/space.site-manage-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/sites/{id}/config": {
+ "delete": {
+ "operationId": "delete_api_v1_sites_id_config",
+ "summary": "Read, set and reset a Host site's serving configuration: response headers, local-only redirects and the SPA fallback (extensionless paths serve index.html).",
+ "description": "**space.site.config** (first-party): Read, set and reset a Host site's serving configuration: response headers, local-only redirects and the SPA fallback (extensionless paths serve index.html). A capability holder must also hold the acting principal's project role (maintainer or owner to change, any role to read). Reserved headers (Content-Security-Policy, Strict-Transport-Security, Set-Cookie, X-Forwarded-*, caching, scope, transport and the platform's own) are refused, and a redirect target must be a local path, so a site can never become an open redirect.",
+ "tags": [
+ "space.site"
+ ],
+ "x-openvibe-capabilities": [
+ "space.site.config"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.site.config"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/space.site-config-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "headers",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ },
+ {
+ "name": "redirects",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "from",
+ "to"
+ ],
+ "properties": {
+ "from": {
+ "type": "string"
+ },
+ "to": {
+ "type": "string"
+ },
+ "status": {
+ "enum": [
+ 301,
+ 302,
+ 307,
+ 308
+ ]
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "spa",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-config-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "get": {
+ "operationId": "get_api_v1_sites_id_config",
+ "summary": "Read, set and reset a Host site's serving configuration: response headers, local-only redirects and the SPA fallback (extensionless paths serve index.html).",
+ "description": "**space.site.config** (first-party): Read, set and reset a Host site's serving configuration: response headers, local-only redirects and the SPA fallback (extensionless paths serve index.html). A capability holder must also hold the acting principal's project role (maintainer or owner to change, any role to read). Reserved headers (Content-Security-Policy, Strict-Transport-Security, Set-Cookie, X-Forwarded-*, caching, scope, transport and the platform's own) are refused, and a redirect target must be a local path, so a site can never become an open redirect.",
+ "tags": [
+ "space.site"
+ ],
+ "x-openvibe-capabilities": [
+ "space.site.config"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.site.config"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/space.site-config-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "headers",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ },
+ {
+ "name": "redirects",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "from",
+ "to"
+ ],
+ "properties": {
+ "from": {
+ "type": "string"
+ },
+ "to": {
+ "type": "string"
+ },
+ "status": {
+ "enum": [
+ 301,
+ 302,
+ 307,
+ 308
+ ]
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "spa",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-config-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "put": {
+ "operationId": "put_api_v1_sites_id_config",
+ "summary": "Read, set and reset a Host site's serving configuration: response headers, local-only redirects and the SPA fallback (extensionless paths serve index.html).",
+ "description": "**space.site.config** (first-party): Read, set and reset a Host site's serving configuration: response headers, local-only redirects and the SPA fallback (extensionless paths serve index.html). A capability holder must also hold the acting principal's project role (maintainer or owner to change, any role to read). Reserved headers (Content-Security-Policy, Strict-Transport-Security, Set-Cookie, X-Forwarded-*, caching, scope, transport and the platform's own) are refused, and a redirect target must be a local path, so a site can never become an open redirect.",
+ "tags": [
+ "space.site"
+ ],
+ "x-openvibe-capabilities": [
+ "space.site.config"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.site.config"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-config-request.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-config-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/sites/{id}/deploys": {
+ "get": {
+ "operationId": "get_api_v1_sites_id_deploys",
+ "summary": "Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys ",
+ "description": "**space.deploy.create** (first-party): Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys and their upload logs, activate a deploy and roll a site back to any earlier ready deploy (one atomic pointer switch). Also judged by the acting principal's project role (deployer or above); an app principal added to a project as deployer can deploy from CI. A site with a Git source also accepts a CI-built deploy at /sites/:id/source/deploys naming the ref and full commit_sha; it is always a preview and records that commit as immutable provenance.",
+ "tags": [
+ "space.deploy"
+ ],
+ "x-openvibe-capabilities": [
+ "space.deploy.create"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-deploy",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.deploy.create"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/space.deploy-create-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.deploy-create-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "operationId": "post_api_v1_sites_id_deploys",
+ "summary": "Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys ",
+ "description": "**space.deploy.create** (first-party): Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys and their upload logs, activate a deploy and roll a site back to any earlier ready deploy (one atomic pointer switch). Also judged by the acting principal's project role (deployer or above); an app principal added to a project as deployer can deploy from CI. A site with a Git source also accepts a CI-built deploy at /sites/:id/source/deploys naming the ref and full commit_sha; it is always a preview and records that commit as immutable provenance.",
+ "tags": [
+ "space.deploy"
+ ],
+ "x-openvibe-capabilities": [
+ "space.deploy.create"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-deploy",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.deploy.create"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.deploy-create-request.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.deploy-create-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/sites/{id}/domains": {
+ "get": {
+ "operationId": "get_api_v1_sites_id_domains",
+ "summary": "Add a custom domain to a static site (never an OpenVibe domain; not in sandbox projects), read its DNS TXT verification and routing records, trigger a verification check, and remove it.",
+ "description": "**space.domain.manage** (first-party): Add a custom domain to a static site (never an OpenVibe domain; not in sandbox projects), read its DNS TXT verification and routing records, trigger a verification check, and remove it. A custom domain is served only after its TXT record is verified; TLS certificates are issued by an operator and never pass through this capability.",
+ "tags": [
+ "space.domain"
+ ],
+ "x-openvibe-capabilities": [
+ "space.domain.manage"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.domain.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/space.domain-manage-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.domain-manage-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "operationId": "post_api_v1_sites_id_domains",
+ "summary": "Add a custom domain to a static site (never an OpenVibe domain; not in sandbox projects), read its DNS TXT verification and routing records, trigger a verification check, and remove it.",
+ "description": "**space.domain.manage** (first-party): Add a custom domain to a static site (never an OpenVibe domain; not in sandbox projects), read its DNS TXT verification and routing records, trigger a verification check, and remove it. A custom domain is served only after its TXT record is verified; TLS certificates are issued by an operator and never pass through this capability.",
+ "tags": [
+ "space.domain"
+ ],
+ "x-openvibe-capabilities": [
+ "space.domain.manage"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.domain.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.domain-manage-request.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.domain-manage-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/sites/{id}/rollback": {
+ "post": {
+ "operationId": "post_api_v1_sites_id_rollback",
+ "summary": "Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys ",
+ "description": "**space.deploy.create** (first-party): Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys and their upload logs, activate a deploy and roll a site back to any earlier ready deploy (one atomic pointer switch). Also judged by the acting principal's project role (deployer or above); an app principal added to a project as deployer can deploy from CI. A site with a Git source also accepts a CI-built deploy at /sites/:id/source/deploys naming the ref and full commit_sha; it is always a preview and records that commit as immutable provenance.",
+ "tags": [
+ "space.deploy"
+ ],
+ "x-openvibe-capabilities": [
+ "space.deploy.create"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-deploy",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.deploy.create"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.deploy-create-request.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.deploy-create-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/sites/{id}/source": {
+ "delete": {
+ "operationId": "delete_api_v1_sites_id_source",
+ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",
+ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",
+ "tags": [
+ "space.site"
+ ],
+ "x-openvibe-capabilities": [
+ "space.site.manage"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.site.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/space.site-manage-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "get": {
+ "operationId": "get_api_v1_sites_id_source",
+ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",
+ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",
+ "tags": [
+ "space.site"
+ ],
+ "x-openvibe-capabilities": [
+ "space.site.manage"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.site.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/space.site-manage-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "put": {
+ "operationId": "put_api_v1_sites_id_source",
+ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",
+ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",
+ "tags": [
+ "space.site"
+ ],
+ "x-openvibe-capabilities": [
+ "space.site.manage"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.site.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-request.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/sites/{id}/source/deploys": {
+ "post": {
+ "operationId": "post_api_v1_sites_id_source_deploys",
+ "summary": "Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys ",
+ "description": "**space.deploy.create** (first-party): Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys and their upload logs, activate a deploy and roll a site back to any earlier ready deploy (one atomic pointer switch). Also judged by the acting principal's project role (deployer or above); an app principal added to a project as deployer can deploy from CI. A site with a Git source also accepts a CI-built deploy at /sites/:id/source/deploys naming the ref and full commit_sha; it is always a preview and records that commit as immutable provenance.",
+ "tags": [
+ "space.deploy"
+ ],
+ "x-openvibe-capabilities": [
+ "space.deploy.create"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-deploy",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.deploy.create"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.deploy-create-request.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.deploy-create-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/sites/{id}/takedown": {
+ "delete": {
+ "operationId": "delete_api_v1_sites_id_takedown",
+ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",
+ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",
+ "tags": [
+ "space.site"
+ ],
+ "x-openvibe-capabilities": [
+ "space.site.manage"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.site.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/space.site-manage-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "operationId": "post_api_v1_sites_id_takedown",
+ "summary": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner",
+ "description": "**space.site.manage** (first-party): Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",
+ "tags": [
+ "space.site"
+ ],
+ "x-openvibe-capabilities": [
+ "space.site.manage"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "space-manage",
+ "security": [
+ {
+ "openvibeToken": [
+ "space.site.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-request.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/space.site-manage-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "common.no-body.v1": {
+ "title": "NoBody",
+ "description": "common.no-body@1: the capability's request carries no body; its parameters are the path and query of the routes in implementedBy (an id, a page cursor). An input schema that says so explicitly, so every active capability names one (roadmap WS-C task 4).",
+ "type": "object",
+ "maxProperties": 0
+ },
+ "common.resource-list-result.v1": {
+ "title": "ResourceListResult",
+ "description": "A page of the resource index (roadmap WS-Z7).",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "resources",
+ "next_cursor"
+ ],
+ "properties": {
+ "resources": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/common.resource-summary.v1"
+ }
+ },
+ "next_cursor": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "partial": {
+ "type": "array",
+ "description": "Authorities a merged index (OpenVibe.Services, ADR-048) could not read for this page: a slow or failing authority is omitted, never fails the page. Each entry names the authority and the problem code it answered. Added in 1.1.0; a single authority's own index leaves it out.",
+ "items": {
+ "type": "object",
+ "required": [
+ "service",
+ "code"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "service": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{1,39}$"
+ },
+ "code": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)*$"
+ }
+ }
+ }
+ }
+ }
+ },
+ "common.resource-summary.v1": {
+ "title": "ResourceSummary",
+ "description": "One resource of any service, as the resource index lists it (roadmap WS-Z7): every service answers GET /api/v1/resources with these, so OpenVibe.Services shows any resource without owning its data.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id",
+ "kind",
+ "service",
+ "state",
+ "created_at"
+ ],
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "kind": {
+ "type": "string",
+ "description": "service.kind, e.g. watch.watch, actor.actor, media.object, events.subscription"
+ },
+ "service": {
+ "type": "string"
+ },
+ "project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "ovrn": {
+ "type": "string",
+ "maxLength": 200,
+ "pattern": "^ovrn:[a-z][a-z0-9-]{0,31}:prj_[0-9A-HJKMNP-TV-Z]{26}:[a-z][a-z0-9-]{0,39}/[a-z]{3}_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "Optional resource name (ADR-034 section 2), the name grants, audit, events, usage records and bills use: ovrn:<service>:<project_id>:<type>/<id>, e.g. ovrn:watch:<project_id>:watch/<wch_id>, with the pattern of common.usage-recorded@1 resource. Its service, project and id are this summary's service, project_id and id. Added in 1.1.0."
+ },
+ "owner": {
+ "$ref": "#/components/schemas/identity.subject-ref.v1"
+ },
+ "name": {
+ "type": "string"
+ },
+ "state": {
+ "type": "string"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "usage": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "number",
+ "minimum": 0
+ },
+ "properties": {},
+ "description": "cost units by name (WS-Z1)"
+ },
+ "links": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "self": {
+ "type": "string",
+ "format": "uri"
+ },
+ "console": {
+ "type": "string",
+ "format": "uri"
+ },
+ "docs": {
+ "type": "string",
+ "format": "uri"
+ }
+ }
+ }
+ }
+ },
+ "errors.problem.v1": {
+ "title": "Problem",
+ "description": "RFC 9457 problem details (application/problem+json) with a stable OpenVibe error code and trace identifiers. The legacy 'error' string is allowed during migration so existing clients keep working.",
+ "type": "object",
+ "required": [
+ "type",
+ "title",
+ "status",
+ "code"
+ ],
+ "additionalProperties": true,
+ "properties": {
+ "type": {
+ "type": "string",
+ "format": "uri-reference"
+ },
+ "title": {
+ "type": "string",
+ "minLength": 1
+ },
+ "status": {
+ "type": "integer",
+ "minimum": 400,
+ "maximum": 599
+ },
+ "detail": {
+ "type": "string"
+ },
+ "instance": {
+ "type": "string",
+ "format": "uri-reference"
+ },
+ "code": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)*$"
+ },
+ "request_id": {
+ "type": "string"
+ },
+ "trace_id": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{32}$"
+ },
+ "errors": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "message"
+ ],
+ "properties": {
+ "path": {
+ "type": "string"
+ },
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "error": {
+ "type": "string",
+ "description": "Deprecated compatibility field: same text as title/detail for clients that read {error}."
+ }
+ }
+ },
+ "identity.subject-ref.v1": {
+ "title": "SubjectRef",
+ "description": "The one cross-platform reference to an actor. Issued by OpenVibe.Network. Never a service-local integer id.",
+ "oneOf": [
+ {
+ "type": "object",
+ "required": [
+ "type",
+ "id"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "type": {
+ "const": "user"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ }
+ },
+ "description": "UserSubject"
+ },
+ {
+ "type": "object",
+ "required": [
+ "type",
+ "id"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "type": {
+ "const": "guest"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^gst_[0-9A-HJKMNP-TV-Z]{26}$"
+ }
+ },
+ "description": "GuestSubject"
+ },
+ {
+ "type": "object",
+ "required": [
+ "type",
+ "id"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "type": {
+ "const": "app"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^app_[0-9A-HJKMNP-TV-Z]{26}$"
+ }
+ },
+ "description": "AppSubject"
+ },
+ {
+ "type": "object",
+ "required": [
+ "type",
+ "id"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "type": {
+ "const": "mod"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^mod_[0-9A-HJKMNP-TV-Z]{26}$"
+ }
+ },
+ "description": "ModSubject"
+ },
+ {
+ "type": "object",
+ "required": [
+ "type",
+ "id"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "type": {
+ "enum": [
+ "service",
+ "system"
+ ]
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9-]{1,39}$"
+ }
+ },
+ "description": "PrincipalSubject"
+ },
+ {
+ "type": "object",
+ "required": [
+ "type",
+ "id"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "type": {
+ "const": "agent"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^agt_[0-9A-HJKMNP-TV-Z]{26}$"
+ }
+ },
+ "description": "AgentSubject: an agent (OpenVibe.Actor, or a developer app's agent) acting under grants its owner delegated (roadmap WS-Z2). In an event envelope, on_behalf_of names the owner."
+ }
+ ]
+ },
+ "space.deploy-create-request.v1": {
+ "title": "SpaceDeployCreateRequest",
+ "description": "space.deploy-create-request@1: bodies of space.deploy.create on OpenVibe.Space (a deployer or above of the project; an app principal added as deployer can deploy from CI). POST /api/v1/sites/:id/deploys uploads a deploy: the raw body is a tar or tar.gz archive (Content-Type application/gzip, application/x-gzip, application/x-tar or application/octet-stream), or multipart/form-data with archive=<one archive> or files=<file>… (each part's filename is its path); optional fields activate=1, root=<subdirectory to deploy> and strip=folder (removes a shared first segment) also work as query parameters. It is validated (path traversal, links, special and hidden files, server-side code, file types, sizes and quotas) and never executed; a refusal is a problem whose extra carries the failed deploy_id and its log (413 upload.too_large, quota.storage; 422 validation codes; 429 quota.deploys_per_day; 403 site.taken_down; 503 upload.busy; 507 storage.host_full). POST /api/v1/deploys/:id/activate { expected_active? } and POST /api/v1/sites/:id/rollback { deploy_id?, expected_active? } switch the site's pointer in one step (rollback without deploy_id goes to the deploy active before; 409 deploy.not_ready, site.active_changed when expected_active is stale, deploy.no_previous). GET /api/v1/sites/:id/deploys, /deploys/:id and /deploys/:id/log take no body.",
+ "anyOf": [
+ {
+ "type": "string",
+ "contentEncoding": "binary",
+ "description": "The upload itself: the tar or tar.gz bytes, or the multipart/form-data body (as common.binary@1)."
+ },
+ {
+ "type": "object",
+ "properties": {
+ "deploy_id": {
+ "anyOf": [
+ {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "expected_active": {
+ "anyOf": [
+ {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The deploy you saw active (null: none); refused with 409 site.active_changed when it moved."
+ }
+ }
+ },
+ {
+ "$ref": "#/components/schemas/common.no-body.v1"
+ }
+ ]
+ },
+ "space.deploy-create-result.v1": {
+ "title": "SpaceDeployCreateResult",
+ "description": "space.deploy-create-result@1: answers of space.deploy.create on OpenVibe.Space. GET /api/v1/sites/:id/deploys?limit= → { active_deploy_id, deploys, activations } (newest first; the last 20 pointer switches). POST /api/v1/sites/:id/deploys → 201 { deploy, activated, url } (the ready deploy with its validation log; activated when activate=1 switched the site to it). GET /api/v1/deploys/:id → { deploy } with its files. GET /api/v1/deploys/:id/log → { deploy_id, state, log }. POST /api/v1/deploys/:id/activate and /sites/:id/rollback → { active_deploy_id, previous_deploy_id, changed } (changed false when it was already active). Emits space.deploy.created|activated|failed.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "active_deploy_id",
+ "deploys",
+ "activations"
+ ],
+ "properties": {
+ "active_deploy_id": {
+ "anyOf": [
+ {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "deploys": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/space.deploy.v1"
+ }
+ },
+ "activations": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "deploy_id",
+ "previous_deploy_id",
+ "kind",
+ "actor",
+ "at"
+ ],
+ "properties": {
+ "deploy_id": {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "previous_deploy_id": {
+ "anyOf": [
+ {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "kind": {
+ "enum": [
+ "activate",
+ "rollback"
+ ]
+ },
+ "actor": {
+ "type": "string"
+ },
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "deploy"
+ ],
+ "properties": {
+ "deploy": {
+ "$ref": "#/components/schemas/space.deploy.v1"
+ },
+ "activated": {
+ "type": "boolean"
+ },
+ "url": {
+ "type": "string"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "deploy_id",
+ "state",
+ "log"
+ ],
+ "properties": {
+ "deploy_id": {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "state": {
+ "enum": [
+ "ready",
+ "failed",
+ "deleted"
+ ]
+ },
+ "log": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "level",
+ "message",
+ "at"
+ ],
+ "properties": {
+ "level": {
+ "enum": [
+ "info",
+ "warn",
+ "error"
+ ]
+ },
+ "message": {
+ "type": "string"
+ },
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "active_deploy_id",
+ "previous_deploy_id",
+ "changed"
+ ],
+ "properties": {
+ "active_deploy_id": {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "previous_deploy_id": {
+ "anyOf": [
+ {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "changed": {
+ "type": "boolean"
+ }
+ }
+ }
+ ]
+ },
+ "space.deploy.v1": {
+ "title": "SpaceDeploy",
+ "description": "space.deploy@1: one immutable static deploy on OpenVibe.Space (server/http/serialize.js deploy): ready (validated and stored, never executed) or failed (refused, with its failure code and log), whether the site serves it, its size and manifest hash. GET /api/v1/deploys/:id adds the files; the upload answer adds the log.",
+ "type": "object",
+ "required": [
+ "id",
+ "site_id",
+ "project_id",
+ "state",
+ "active",
+ "source",
+ "file_count",
+ "total_bytes",
+ "new_bytes",
+ "manifest_sha256",
+ "failure_code",
+ "created_by",
+ "created_at"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "site_id": {
+ "type": "string",
+ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "state": {
+ "enum": [
+ "ready",
+ "failed",
+ "deleted"
+ ]
+ },
+ "active": {
+ "type": "boolean"
+ },
+ "source": {
+ "enum": [
+ "archive",
+ "files",
+ "preview",
+ "git"
+ ]
+ },
+ "git": {
+ "type": "object",
+ "required": [
+ "provider",
+ "repo_url",
+ "ref",
+ "commit_sha"
+ ],
+ "properties": {
+ "provider": {
+ "type": "string"
+ },
+ "repo_url": {
+ "type": "string"
+ },
+ "ref": {
+ "type": "string"
+ },
+ "commit_sha": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{40}$"
+ }
+ },
+ "description": "Present on source git: the commit this deploy was built from (public provenance, never a credential)."
+ },
+ "file_count": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "total_bytes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "new_bytes": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "Bytes this deploy added to the project's store."
+ },
+ "manifest_sha256": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "pattern": "^[0-9a-f]{64}$"
+ },
+ "failure_code": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "created_by": {
+ "type": "string"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "files": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "path",
+ "sha256",
+ "size",
+ "content_type"
+ ],
+ "properties": {
+ "path": {
+ "type": "string"
+ },
+ "sha256": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{64}$"
+ },
+ "size": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "content_type": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "log": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "level",
+ "message",
+ "at"
+ ],
+ "properties": {
+ "level": {
+ "enum": [
+ "info",
+ "warn",
+ "error"
+ ]
+ },
+ "message": {
+ "type": "string"
+ },
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ }
+ }
+ }
+ },
+ "space.domain-manage-request.v1": {
+ "title": "SpaceDomainManageRequest",
+ "description": "space.domain-manage-request@1: bodies of space.domain.manage on OpenVibe.Space (a maintainer or above of the project). POST /api/v1/sites/:id/domains { hostname } adds a custom domain, pending until its TXT record is verified: a fully qualified host name, lowercased (422 domain.invalid_hostname; 422 domain.reserved for any OpenVibe domain; 403 domain.sandbox in sandbox projects; 429 quota.custom_domains; 409 domain.exists, domain.taken). GET /api/v1/sites/:id/domains, POST /api/v1/domains/:id/verify (checks the TXT record now) and DELETE /api/v1/domains/:id (409 domain.default: the default domain goes only with the site) take no body.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "hostname"
+ ],
+ "properties": {
+ "hostname": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 254,
+ "description": "e.g. www.example.org (a trailing dot is dropped)."
+ }
+ }
+ },
+ {
+ "$ref": "#/components/schemas/common.no-body.v1"
+ }
+ ]
+ },
+ "space.domain-manage-result.v1": {
+ "title": "SpaceDomainManageResult",
+ "description": "space.domain-manage-result@1: answers of space.domain.manage on OpenVibe.Space. GET /api/v1/sites/:id/domains → { domains } (the default domain first). POST /api/v1/sites/:id/domains → 201 { domain } with the DNS records to publish. POST /api/v1/domains/:id/verify → { domain } after the check (status verified and served once the TXT record matches, else last_error says what was found; emits space.domain.verified). DELETE /api/v1/domains/:id → { deleted: true }.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "domains"
+ ],
+ "properties": {
+ "domains": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/space.domain.v1"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "domain"
+ ],
+ "properties": {
+ "domain": {
+ "$ref": "#/components/schemas/space.domain.v1"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "deleted"
+ ],
+ "properties": {
+ "deleted": {
+ "const": true
+ }
+ }
+ }
+ ]
+ },
+ "space.domain.v1": {
+ "title": "SpaceDomain",
+ "description": "space.domain@1: one domain of a static site on OpenVibe.Space (server/http/serialize.js domain): the default <site>.openvibe.website, or a custom domain that is served only once its DNS TXT record is verified (re-checked daily; it lapses when the record is gone). A custom domain carries the records its owner must publish (the TXT verification value is public by design and shown only to members); TLS certificates are issued by an operator.",
+ "type": "object",
+ "required": [
+ "id",
+ "site_id",
+ "hostname",
+ "kind",
+ "status",
+ "served",
+ "verified_at",
+ "last_checked_at",
+ "last_error"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^dom_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "site_id": {
+ "type": "string",
+ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "hostname": {
+ "type": "string"
+ },
+ "kind": {
+ "enum": [
+ "default",
+ "custom"
+ ]
+ },
+ "status": {
+ "enum": [
+ "pending",
+ "verified",
+ "failed",
+ "lapsed"
+ ]
+ },
+ "served": {
+ "type": "boolean"
+ },
+ "verified_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "last_checked_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "last_error": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "instructions": {
+ "type": "object",
+ "required": [
+ "verification",
+ "routing",
+ "tls"
+ ],
+ "properties": {
+ "verification": {
+ "type": "object",
+ "required": [
+ "type",
+ "name",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "TXT"
+ },
+ "name": {
+ "type": "string",
+ "description": "_openvibe-space.<hostname>"
+ },
+ "value": {
+ "type": "string",
+ "description": "openvibe-space-verification=<token>"
+ },
+ "note": {
+ "type": "string"
+ }
+ }
+ },
+ "routing": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "type",
+ "name",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string"
+ },
+ "note": {
+ "type": "string"
+ }
+ }
+ },
+ "description": "A CNAME for a subdomain; A/AAAA for an apex domain when the operator configured them."
+ },
+ "tls": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ "space.project.v1": {
+ "title": "SpaceProject",
+ "description": "space.project@1: one OpenVibe.Space tenant project (server/http/serialize.js project): owned by a person (usr_…), production or sandbox, with the caller's role in it and any staff takedown. POST /api/v1/projects and GET /api/v1/projects/:id add the effective quota and the usage.",
+ "type": "object",
+ "required": [
+ "id",
+ "name",
+ "environment",
+ "network_project_id",
+ "owner",
+ "role",
+ "created_at",
+ "takedown"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 80
+ },
+ "environment": {
+ "enum": [
+ "production",
+ "sandbox"
+ ]
+ },
+ "network_project_id": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "The OpenVibe.Network project once Network has projects."
+ },
+ "owner": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "role": {
+ "enum": [
+ "owner",
+ "maintainer",
+ "deployer",
+ "staff",
+ null
+ ],
+ "description": "The caller's role (staff: a Network admin who is not a member)."
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "takedown": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "required": [
+ "scope",
+ "reason",
+ "since"
+ ],
+ "description": "A staff takedown covering it: serving stops while the content is kept for review.",
+ "properties": {
+ "scope": {
+ "enum": [
+ "project",
+ "site"
+ ]
+ },
+ "reason": {
+ "type": "string"
+ },
+ "since": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ },
+ "quota": {
+ "type": "object",
+ "required": [
+ "storage_bytes",
+ "deploys_per_day",
+ "max_files",
+ "max_file_bytes",
+ "sites",
+ "custom_domains"
+ ],
+ "description": "The effective quota: the project's override where set, else its environment's default.",
+ "properties": {
+ "storage_bytes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "deploys_per_day": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "max_files": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "max_file_bytes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "sites": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "custom_domains": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ },
+ "usage": {
+ "type": "object",
+ "required": [
+ "storage_bytes",
+ "objects",
+ "deploys_last_24h",
+ "sites",
+ "custom_domains"
+ ],
+ "properties": {
+ "storage_bytes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "objects": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "deploys_last_24h": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "sites": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "custom_domains": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ }
+ }
+ },
+ "space.site-config-request.v1": {
+ "title": "SpaceSiteConfigRequest",
+ "description": "space.site-config-request@1: the body of PUT /api/v1/sites/:id/config on OpenVibe.Space (space.site.config; the acting principal must be a maintainer or owner of the site's project). { headers?, redirects?, spa? } replaces the whole configuration: a field left out is reset to its default. A redirect's status defaults to 301. Refusals are 422 problem+json: site_config.reserved_header, site_config.header_name, site_config.header_value (longer than 1024 characters or a control character), site_config.too_many (over 50 headers or 200 redirects), site_config.redirect (not a local path, a duplicate from, or a status outside 301/302/307/308), site_config.headers and site_config.redirects (wrong type). GET and DELETE /api/v1/sites/:id/config take no body (DELETE resets to the defaults).",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "headers": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "redirects": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "from",
+ "to"
+ ],
+ "properties": {
+ "from": {
+ "type": "string"
+ },
+ "to": {
+ "type": "string"
+ },
+ "status": {
+ "enum": [
+ 301,
+ 302,
+ 307,
+ 308
+ ]
+ }
+ }
+ }
+ },
+ "spa": {
+ "type": "boolean"
+ }
+ }
+ },
+ "space.site-config-result.v1": {
+ "title": "SpaceSiteConfigResult",
+ "description": "space.site-config-result@1: every answer of space.site.config on OpenVibe.Space. GET, PUT and DELETE /api/v1/sites/:id/config → { config } (after a PUT, the configuration as stored; after a DELETE, the defaults).",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "config"
+ ],
+ "properties": {
+ "config": {
+ "$ref": "#/components/schemas/space.site-config.v1"
+ }
+ }
+ },
+ "space.site-config.v1": {
+ "title": "SpaceSiteConfig",
+ "description": "space.site-config@1: one Host site's serving configuration (plan T12 J3). headers are applied to every response of the site before the platform's own, which always win; the platform's security, routing, caching, scope and transport headers (Content-Security-Policy, Strict-Transport-Security, Set-Cookie, Cache-Control, Content-Type, Location, Service-Worker-Allowed, Alt-Svc and the rest of Host's reserved list) and every X-Forwarded-* header are refused. redirects are local paths only (no scheme, host, protocol-relative URL, backslash or control character, also after percent-decoding). spa serves /index.html for an extensionless path that has no file. A site with no configuration answers { headers: {}, redirects: [], spa: false }.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "headers",
+ "redirects",
+ "spa"
+ ],
+ "properties": {
+ "headers": {
+ "type": "object",
+ "maxProperties": 50,
+ "propertyNames": {
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9-]{0,63}$"
+ },
+ "additionalProperties": {
+ "type": "string",
+ "maxLength": 1024
+ }
+ },
+ "redirects": {
+ "type": "array",
+ "maxItems": 200,
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "from",
+ "to",
+ "status"
+ ],
+ "properties": {
+ "from": {
+ "type": "string",
+ "pattern": "^/(?!/)",
+ "maxLength": 1024
+ },
+ "to": {
+ "type": "string",
+ "pattern": "^/(?!/)",
+ "maxLength": 1024
+ },
+ "status": {
+ "enum": [
+ 301,
+ 302,
+ 307,
+ 308
+ ]
+ }
+ }
+ }
+ },
+ "spa": {
+ "type": "boolean"
+ }
+ }
+ },
+ "space.site-manage-request.v1": {
+ "title": "SpaceSiteManageRequest",
+ "description": "space.site-manage-request@1: bodies of space.site.manage on OpenVibe.Space (judged by the acting principal's project role too; a sandbox token is refused on production projects). POST /api/v1/projects { name, environment?, network_project_id? } creates a project owned by the person named with X-OV-Subject (403 project.owner_must_be_user; 422 project.invalid_name over 80 characters, project.invalid_environment; 429 quota.projects; 409 project.network_project_taken). PUT /api/v1/projects/:id/quota { storage_bytes?, deploys_per_day?, max_files?, max_file_bytes?, sites?, custom_domains? } (a Network staff user: 403 auth.staff_only; fields left out keep their value). PUT /api/v1/projects/:id/members/:principal { role } (the owner: 422 member.invalid_role, 409 member.owner_fixed). POST /api/v1/projects/:id/sites { name } (a maintainer; lowercased and trimmed, one DNS label of 3–40 characters: 422 site.invalid_name, site.name_reserved; 409 site.name_taken, site.name_held; 429 quota.sites). POST /api/v1/projects/:id/takedown and /sites/:id/takedown { reason } (staff: stop serving, keep the content; 409 takedown.exists); DELETE on those { note? } lifts it. GET /api/v1/projects, /projects/:id, /projects/:id/quota, /projects/:id/sites and /sites/:id, and DELETE /api/v1/projects/:id, /projects/:id/members/:principal, /sites/:id and /deploys/:id take no body (deleting refuses taken-down content for non-staff: 409 site.taken_down; an active deploy: 409 deploy.active).",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "1–80 characters once trimmed."
+ },
+ "environment": {
+ "enum": [
+ "production",
+ "sandbox",
+ null
+ ],
+ "description": "Default production; a sandbox credential creates sandbox projects only."
+ },
+ "network_project_id": {
+ "anyOf": [
+ {
+ "type": "string",
+ "pattern": "^([A-Za-z0-9_:-]{3,80})?$"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ }
+ }
+ },
+ {
+ "type": "object",
+ "properties": {
+ "storage_bytes": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 0,
+ "description": "A non-negative integer, or null for the environment default."
+ },
+ "deploys_per_day": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 0,
+ "description": "A non-negative integer, or null for the environment default."
+ },
+ "max_files": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 0,
+ "description": "A non-negative integer, or null for the environment default."
+ },
+ "max_file_bytes": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 0,
+ "description": "A non-negative integer, or null for the environment default."
+ },
+ "sites": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 0,
+ "description": "A non-negative integer, or null for the environment default."
+ },
+ "custom_domains": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 0,
+ "description": "A non-negative integer, or null for the environment default."
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "role"
+ ],
+ "properties": {
+ "role": {
+ "enum": [
+ "owner",
+ "maintainer",
+ "deployer"
+ ],
+ "description": "Only a person (usr_…) can be an owner."
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "pattern": "^\\s*[A-Za-z0-9](?:[A-Za-z0-9]|-(?=[A-Za-z0-9])){2,39}\\s*$",
+ "description": "The site name (lowercased)."
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "reason"
+ ],
+ "properties": {
+ "reason": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500,
+ "description": "Members see it."
+ }
+ }
+ },
+ {
+ "type": "object",
+ "properties": {
+ "note": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "Cut to 500 characters."
+ }
+ }
+ },
+ {
+ "$ref": "#/components/schemas/common.no-body.v1"
+ }
+ ]
+ },
+ "space.site-manage-result.v1": {
+ "title": "SpaceSiteManageResult",
+ "description": "space.site-manage-result@1: answers of space.site.manage on OpenVibe.Space. GET /api/v1/projects → { projects } (the caller's projects with their role). POST /api/v1/projects → 201 { project } (with quota and usage). GET /api/v1/projects/:id → { project, sites, members }. GET and PUT /api/v1/projects/:id/quota → { quota, usage }. PUT and DELETE /api/v1/projects/:id/members/:principal → { members }. GET /api/v1/projects/:id/sites → { sites }. POST /api/v1/projects/:id/sites → 201 { site }. GET /api/v1/sites/:id → { site, domains }. DELETE /api/v1/projects/:id and /sites/:id → { deleted: true } (serving stops at once; the objects are removed). DELETE /api/v1/deploys/:id → { deleted: true, objects_removed }. POST …/takedown → 201 { takedown }; DELETE …/takedown → { lifted: true }.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "projects"
+ ],
+ "properties": {
+ "projects": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/space.project.v1"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "project"
+ ],
+ "properties": {
+ "project": {
+ "$ref": "#/components/schemas/space.project.v1"
+ },
+ "sites": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/space.site.v1"
+ }
+ },
+ "members": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "principal",
+ "role",
+ "added_at"
+ ],
+ "properties": {
+ "principal": {
+ "type": "string",
+ "description": "usr_…, app:app_… or svc:…"
+ },
+ "role": {
+ "enum": [
+ "owner",
+ "maintainer",
+ "deployer"
+ ]
+ },
+ "added_at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "quota",
+ "usage"
+ ],
+ "properties": {
+ "quota": {
+ "type": "object",
+ "required": [
+ "storage_bytes",
+ "deploys_per_day",
+ "max_files",
+ "max_file_bytes",
+ "sites",
+ "custom_domains"
+ ],
+ "description": "The effective quota: the project's override where set, else its environment's default.",
+ "properties": {
+ "storage_bytes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "deploys_per_day": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "max_files": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "max_file_bytes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "sites": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "custom_domains": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ },
+ "usage": {
+ "type": "object",
+ "required": [
+ "storage_bytes",
+ "objects",
+ "deploys_last_24h",
+ "sites",
+ "custom_domains"
+ ],
+ "properties": {
+ "storage_bytes": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "objects": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "deploys_last_24h": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "sites": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "custom_domains": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "members"
+ ],
+ "properties": {
+ "members": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "principal",
+ "role",
+ "added_at"
+ ],
+ "properties": {
+ "principal": {
+ "type": "string",
+ "description": "usr_…, app:app_… or svc:…"
+ },
+ "role": {
+ "enum": [
+ "owner",
+ "maintainer",
+ "deployer"
+ ]
+ },
+ "added_at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "sites"
+ ],
+ "properties": {
+ "sites": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/space.site.v1"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "site"
+ ],
+ "properties": {
+ "site": {
+ "$ref": "#/components/schemas/space.site.v1"
+ },
+ "domains": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/space.domain.v1"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "deleted"
+ ],
+ "properties": {
+ "deleted": {
+ "const": true
+ },
+ "objects_removed": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "takedown"
+ ],
+ "properties": {
+ "takedown": {
+ "type": "object",
+ "required": [
+ "scope",
+ "reason",
+ "since"
+ ],
+ "description": "The takedown now in force.",
+ "properties": {
+ "scope": {
+ "enum": [
+ "project",
+ "site"
+ ]
+ },
+ "reason": {
+ "type": "string"
+ },
+ "since": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "lifted"
+ ],
+ "properties": {
+ "lifted": {
+ "const": true
+ }
+ }
+ }
+ ]
+ },
+ "space.site.v1": {
+ "title": "SpaceSite",
+ "description": "space.site@1: one static site on OpenVibe.Space (server/http/serialize.js site): its name (one DNS label), the default hostname and URL it is served at, the deploy it serves (one pointer; null until a deploy is activated) and any staff takedown.",
+ "type": "object",
+ "required": [
+ "id",
+ "project_id",
+ "name",
+ "hostname",
+ "url",
+ "active_deploy_id",
+ "takedown",
+ "created_at",
+ "updated_at"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^sit_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "name": {
+ "type": "string",
+ "pattern": "^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){2,39}$"
+ },
+ "hostname": {
+ "type": "string"
+ },
+ "url": {
+ "type": "string"
+ },
+ "active_deploy_id": {
+ "anyOf": [
+ {
+ "type": "string",
+ "pattern": "^dpl_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "takedown": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "required": [
+ "scope",
+ "reason",
+ "since"
+ ],
+ "description": "A staff takedown covering it: serving stops while the content is kept for review.",
+ "properties": {
+ "scope": {
+ "enum": [
+ "project",
+ "site"
+ ]
+ },
+ "reason": {
+ "type": "string"
+ },
+ "since": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ }
+ },
+ "securitySchemes": {
+ "openvibeToken": {
+ "type": "http",
+ "scheme": "bearer",
+ "bearerFormat": "JWT",
+ "description": "An OpenVibe Network token for this service: a person's token, an app token (client credentials, audience of the service) or a first-party service token. Its `cap` claim must include the capability the operation names."
+ }
+ }
+ }
+}
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Tips API",
"title": "OpenVibe.Tips API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Tools API",
"title": "OpenVibe.Tools API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Trade API",
"title": "OpenVibe.Trade API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.VIP API",
"title": "OpenVibe.VIP API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Watch API",
"title": "OpenVibe.Watch API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Wiki API",
"title": "OpenVibe.Wiki API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Work API",
"title": "OpenVibe.Work API",
- "version": "0.148.0",
- "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.148.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.149.0",
+ "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.149.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -173071,3 +173071,516 @@ export type FoodRecipeResult =
@@ -173071,3 +173071,516 @@ export type FoodRecipeResult =
FoodRecipeSuggestion
FoodRecipeSuggestion
];
];
};
};
+
+/** [email protected] (owner: space) */
+/**
+ * space.deploy-create-request@1: bodies of space.deploy.create on OpenVibe.Space (a deployer or above of the project; an app principal added as deployer can deploy from CI). POST /api/v1/sites/:id/deploys uploads a deploy: the raw body is a tar or tar.gz archive (Content-Type application/gzip, application/x-gzip, application/x-tar or application/octet-stream), or multipart/form-data with archive=<one archive> or files=<file>… (each part's filename is its path); optional fields activate=1, root=<subdirectory to deploy> and strip=folder (removes a shared first segment) also work as query parameters. It is validated (path traversal, links, special and hidden files, server-side code, file types, sizes and quotas) and never executed; a refusal is a problem whose extra carries the failed deploy_id and its log (413 upload.too_large, quota.storage; 422 validation codes; 429 quota.deploys_per_day; 403 site.taken_down; 503 upload.busy; 507 storage.host_full). POST /api/v1/deploys/:id/activate { expected_active? } and POST /api/v1/sites/:id/rollback { deploy_id?, expected_active? } switch the site's pointer in one step (rollback without deploy_id goes to the deploy active before; 409 deploy.not_ready, site.active_changed when expected_active is stale, deploy.no_previous). GET /api/v1/sites/:id/deploys, /deploys/:id and /deploys/:id/log take no body.
+ */
+export type SpaceDeployCreateRequest =
+ | string
+ | {
+ deploy_id?: string | null;
+ /**
+ * The deploy you saw active (null: none); refused with 409 site.active_changed when it moved.
+ */
+ expected_active?: string | null;
+ }
+ | NoBody;
+
+/** [email protected] (owner: space) */
+/**
+ * space.deploy-create-result@1: answers of space.deploy.create on OpenVibe.Space. GET /api/v1/sites/:id/deploys?limit= → { active_deploy_id, deploys, activations } (newest first; the last 20 pointer switches). POST /api/v1/sites/:id/deploys → 201 { deploy, activated, url } (the ready deploy with its validation log; activated when activate=1 switched the site to it). GET /api/v1/deploys/:id → { deploy } with its files. GET /api/v1/deploys/:id/log → { deploy_id, state, log }. POST /api/v1/deploys/:id/activate and /sites/:id/rollback → { active_deploy_id, previous_deploy_id, changed } (changed false when it was already active). Emits space.deploy.created|activated|failed.
+ */
+export type SpaceDeployCreateResult =
+ | {
+ active_deploy_id: string | null;
+ deploys: SpaceDeploy[];
+ activations: {
+ deploy_id: string;
+ previous_deploy_id: string | null;
+ kind: "activate" | "rollback";
+ actor: string;
+ at: string;
+ }[];
+ }
+ | {
+ deploy: SpaceDeploy;
+ activated?: boolean;
+ url?: string;
+ }
+ | {
+ deploy_id: string;
+ state: "ready" | "failed" | "deleted";
+ log: {
+ level: "info" | "warn" | "error";
+ message: string;
+ at: string;
+ }[];
+ }
+ | {
+ active_deploy_id: string;
+ previous_deploy_id: string | null;
+ changed: boolean;
+ };
+
+/** [email protected] (owner: space) */
+/**
+ * space.deploy@1: one immutable static deploy on OpenVibe.Space (server/http/serialize.js deploy): ready (validated and stored, never executed) or failed (refused, with its failure code and log), whether the site serves it, its size and manifest hash. GET /api/v1/deploys/:id adds the files; the upload answer adds the log.
+ */
+export interface SpaceDeploy {
+ id: string;
+ site_id: string;
+ project_id: string;
+ state: "ready" | "failed" | "deleted";
+ active: boolean;
+ source: "archive" | "files" | "preview" | "git";
+ /**
+ * Present on source git: the commit this deploy was built from (public provenance, never a credential).
+ */
+ git?: {
+ provider: string;
+ repo_url: string;
+ ref: string;
+ commit_sha: string;
+ };
+ file_count: number;
+ total_bytes: number;
+ /**
+ * Bytes this deploy added to the project's store.
+ */
+ new_bytes: number;
+ manifest_sha256: string | null;
+ failure_code: string | null;
+ created_by: string;
+ created_at: string;
+ files?: {
+ path: string;
+ sha256: string;
+ size: number;
+ content_type: string;
+ }[];
+ log?: {
+ level: "info" | "warn" | "error";
+ message: string;
+ at: string;
+ }[];
+}
+
+/** [email protected] (owner: space) */
+/**
+ * space.domain-manage-request@1: bodies of space.domain.manage on OpenVibe.Space (a maintainer or above of the project). POST /api/v1/sites/:id/domains { hostname } adds a custom domain, pending until its TXT record is verified: a fully qualified host name, lowercased (422 domain.invalid_hostname; 422 domain.reserved for any OpenVibe domain; 403 domain.sandbox in sandbox projects; 429 quota.custom_domains; 409 domain.exists, domain.taken). GET /api/v1/sites/:id/domains, POST /api/v1/domains/:id/verify (checks the TXT record now) and DELETE /api/v1/domains/:id (409 domain.default: the default domain goes only with the site) take no body.
+ */
+export type SpaceDomainManageRequest =
+ | {
+ /**
+ * e.g. www.example.org (a trailing dot is dropped).
+ */
+ hostname: string;
+ }
+ | NoBody;
+
+/** [email protected] (owner: space) */
+/**
+ * space.domain-manage-result@1: answers of space.domain.manage on OpenVibe.Space. GET /api/v1/sites/:id/domains → { domains } (the default domain first). POST /api/v1/sites/:id/domains → 201 { domain } with the DNS records to publish. POST /api/v1/domains/:id/verify → { domain } after the check (status verified and served once the TXT record matches, else last_error says what was found; emits space.domain.verified). DELETE /api/v1/domains/:id → { deleted: true }.
+ */
+export type SpaceDomainManageResult =
+ | {
+ domains: SpaceDomain[];
+ }
+ | {
+ domain: SpaceDomain;
+ }
+ | {
+ deleted: true;
+ };
+
+/** [email protected] (owner: space) */
+/**
+ * space.domain@1: one domain of a static site on OpenVibe.Space (server/http/serialize.js domain): the default <site>.openvibe.website, or a custom domain that is served only once its DNS TXT record is verified (re-checked daily; it lapses when the record is gone). A custom domain carries the records its owner must publish (the TXT verification value is public by design and shown only to members); TLS certificates are issued by an operator.
+ */
+export interface SpaceDomain {
+ id: string;
+ site_id: string;
+ hostname: string;
+ kind: "default" | "custom";
+ status: "pending" | "verified" | "failed" | "lapsed";
+ served: boolean;
+ verified_at: string | null;
+ last_checked_at: string | null;
+ last_error: string | null;
+ instructions?: {
+ verification: {
+ type: "TXT";
+ /**
+ * _openvibe-space.<hostname>
+ */
+ name: string;
+ /**
+ * openvibe-space-verification=<token>
+ */
+ value: string;
+ note?: string;
+ };
+ /**
+ * A CNAME for a subdomain; A/AAAA for an apex domain when the operator configured them.
+ */
+ routing: {
+ type: string;
+ name: string;
+ value: string;
+ note?: string;
+ }[];
+ tls: string;
+ };
+}
+
+/** [email protected] (owner: space) */
+/**
+ * space.project@1: one OpenVibe.Space tenant project (server/http/serialize.js project): owned by a person (usr_…), production or sandbox, with the caller's role in it and any staff takedown. POST /api/v1/projects and GET /api/v1/projects/:id add the effective quota and the usage.
+ */
+export interface SpaceProject {
+ id: string;
+ name: string;
+ environment: "production" | "sandbox";
+ /**
+ * The OpenVibe.Network project once Network has projects.
+ */
+ network_project_id: string | null;
+ owner: string;
+ /**
+ * The caller's role (staff: a Network admin who is not a member).
+ */
+ role: "owner" | "maintainer" | "deployer" | "staff" | null;
+ created_at: string;
+ /**
+ * A staff takedown covering it: serving stops while the content is kept for review.
+ */
+ takedown: {
+ scope: "project" | "site";
+ reason: string;
+ since: string | null;
+ } | null;
+ /**
+ * The effective quota: the project's override where set, else its environment's default.
+ */
+ quota?: {
+ storage_bytes: number;
+ deploys_per_day: number;
+ max_files: number;
+ max_file_bytes: number;
+ sites: number;
+ custom_domains: number;
+ };
+ usage?: {
+ storage_bytes: number;
+ objects: number;
+ deploys_last_24h: number;
+ sites: number;
+ custom_domains: number;
+ };
+}
+
+/** [email protected] (owner: space) */
+/**
+ * space.site-manage-request@1: bodies of space.site.manage on OpenVibe.Space (judged by the acting principal's project role too; a sandbox token is refused on production projects). POST /api/v1/projects { name, environment?, network_project_id? } creates a project owned by the person named with X-OV-Subject (403 project.owner_must_be_user; 422 project.invalid_name over 80 characters, project.invalid_environment; 429 quota.projects; 409 project.network_project_taken). PUT /api/v1/projects/:id/quota { storage_bytes?, deploys_per_day?, max_files?, max_file_bytes?, sites?, custom_domains? } (a Network staff user: 403 auth.staff_only; fields left out keep their value). PUT /api/v1/projects/:id/members/:principal { role } (the owner: 422 member.invalid_role, 409 member.owner_fixed). POST /api/v1/projects/:id/sites { name } (a maintainer; lowercased and trimmed, one DNS label of 3–40 characters: 422 site.invalid_name, site.name_reserved; 409 site.name_taken, site.name_held; 429 quota.sites). POST /api/v1/projects/:id/takedown and /sites/:id/takedown { reason } (staff: stop serving, keep the content; 409 takedown.exists); DELETE on those { note? } lifts it. GET /api/v1/projects, /projects/:id, /projects/:id/quota, /projects/:id/sites and /sites/:id, and DELETE /api/v1/projects/:id, /projects/:id/members/:principal, /sites/:id and /deploys/:id take no body (deleting refuses taken-down content for non-staff: 409 site.taken_down; an active deploy: 409 deploy.active).
+ */
+export type SpaceSiteManageRequest =
+ | {
+ /**
+ * 1–80 characters once trimmed.
+ */
+ name: string;
+ /**
+ * Default production; a sandbox credential creates sandbox projects only.
+ */
+ environment?: "production" | "sandbox" | null;
+ network_project_id?: string | null;
+ }
+ | {
+ /**
+ * A non-negative integer, or null for the environment default.
+ */
+ storage_bytes?: number | null;
+ /**
+ * A non-negative integer, or null for the environment default.
+ */
+ deploys_per_day?: number | null;
+ /**
+ * A non-negative integer, or null for the environment default.
+ */
+ max_files?: number | null;
+ /**
+ * A non-negative integer, or null for the environment default.
+ */
+ max_file_bytes?: number | null;
+ /**
+ * A non-negative integer, or null for the environment default.
+ */
+ sites?: number | null;
+ /**
+ * A non-negative integer, or null for the environment default.
+ */
+ custom_domains?: number | null;
+ }
+ | {
+ /**
+ * Only a person (usr_…) can be an owner.
+ */
+ role: "owner" | "maintainer" | "deployer";
+ }
+ | {
+ /**
+ * The site name (lowercased).
+ */
+ name: string;
+ }
+ | {
+ /**
+ * Members see it.
+ */
+ reason: string;
+ }
+ | {
+ /**
+ * Cut to 500 characters.
+ */
+ note?: string | null;
+ }
+ | NoBody;
+
+/** [email protected] (owner: space) */
+/**
+ * space.site-manage-result@1: answers of space.site.manage on OpenVibe.Space. GET /api/v1/projects → { projects } (the caller's projects with their role). POST /api/v1/projects → 201 { project } (with quota and usage). GET /api/v1/projects/:id → { project, sites, members }. GET and PUT /api/v1/projects/:id/quota → { quota, usage }. PUT and DELETE /api/v1/projects/:id/members/:principal → { members }. GET /api/v1/projects/:id/sites → { sites }. POST /api/v1/projects/:id/sites → 201 { site }. GET /api/v1/sites/:id → { site, domains }. DELETE /api/v1/projects/:id and /sites/:id → { deleted: true } (serving stops at once; the objects are removed). DELETE /api/v1/deploys/:id → { deleted: true, objects_removed }. POST …/takedown → 201 { takedown }; DELETE …/takedown → { lifted: true }.
+ */
+export type SpaceSiteManageResult =
+ | {
+ projects: SpaceProject[];
+ }
+ | {
+ project: SpaceProject;
+ sites?: SpaceSite[];
+ members?: {
+ /**
+ * usr_…, app:app_… or svc:…
+ */
+ principal: string;
+ role: "owner" | "maintainer" | "deployer";
+ added_at: string;
+ }[];
+ }
+ | {
+ /**
+ * The effective quota: the project's override where set, else its environment's default.
+ */
+ quota: {
+ storage_bytes: number;
+ deploys_per_day: number;
+ max_files: number;
+ max_file_bytes: number;
+ sites: number;
+ custom_domains: number;
+ };
+ usage: {
+ storage_bytes: number;
+ objects: number;
+ deploys_last_24h: number;
+ sites: number;
+ custom_domains: number;
+ };
+ }
+ | {
+ members: {
+ /**
+ * usr_…, app:app_… or svc:…
+ */
+ principal: string;
+ role: "owner" | "maintainer" | "deployer";
+ added_at: string;
+ }[];
+ }
+ | {
+ sites: SpaceSite[];
+ }
+ | {
+ site: SpaceSite;
+ domains?: SpaceDomain[];
+ }
+ | {
+ deleted: true;
+ objects_removed?: number;
+ }
+ | {
+ /**
+ * The takedown now in force.
+ */
+ takedown: {
+ scope: "project" | "site";
+ reason: string;
+ since: string | null;
+ };
+ }
+ | {
+ lifted: true;
+ };
+
+/** [email protected] (owner: space) */
+/**
+ * space.site@1: one static site on OpenVibe.Space (server/http/serialize.js site): its name (one DNS label), the default hostname and URL it is served at, the deploy it serves (one pointer; null until a deploy is activated) and any staff takedown.
+ */
+export interface SpaceSite {
+ id: string;
+ project_id: string;
+ name: string;
+ hostname: string;
+ url: string;
+ active_deploy_id: string | null;
+ /**
+ * A staff takedown covering it: serving stops while the content is kept for review.
+ */
+ takedown: {
+ scope: "project" | "site";
+ reason: string;
+ since: string | null;
+ } | null;
+ created_at: string;
+ updated_at: string;
+}
+
+/** [email protected] (owner: space) */
+/**
+ * space.site-config@1: one Host site's serving configuration (plan T12 J3). headers are applied to every response of the site before the platform's own, which always win; the platform's security, routing, caching, scope and transport headers (Content-Security-Policy, Strict-Transport-Security, Set-Cookie, Cache-Control, Content-Type, Location, Service-Worker-Allowed, Alt-Svc and the rest of Host's reserved list) and every X-Forwarded-* header are refused. redirects are local paths only (no scheme, host, protocol-relative URL, backslash or control character, also after percent-decoding). spa serves /index.html for an extensionless path that has no file. A site with no configuration answers { headers: {}, redirects: [], spa: false }.
+ */
+export interface SpaceSiteConfig {
+ headers: {
+ [k: string]: string | undefined;
+ };
+ /**
+ * @maxItems 200
+ */
+ redirects: {
+ from: string;
+ to: string;
+ status: 301 | 302 | 307 | 308;
+ }[];
+ spa: boolean;
+}
+
+/** [email protected] (owner: space) */
+/**
+ * space.site-config-request@1: the body of PUT /api/v1/sites/:id/config on OpenVibe.Space (space.site.config; the acting principal must be a maintainer or owner of the site's project). { headers?, redirects?, spa? } replaces the whole configuration: a field left out is reset to its default. A redirect's status defaults to 301. Refusals are 422 problem+json: site_config.reserved_header, site_config.header_name, site_config.header_value (longer than 1024 characters or a control character), site_config.too_many (over 50 headers or 200 redirects), site_config.redirect (not a local path, a duplicate from, or a status outside 301/302/307/308), site_config.headers and site_config.redirects (wrong type). GET and DELETE /api/v1/sites/:id/config take no body (DELETE resets to the defaults).
+ */
+export interface SpaceSiteConfigRequest {
+ headers?: {
+ [k: string]: string | undefined;
+ };
+ redirects?: {
+ from: string;
+ to: string;
+ status?: 301 | 302 | 307 | 308;
+ }[];
+ spa?: boolean;
+}
+
+/** [email protected] (owner: space) */
+/**
+ * space.site-config-result@1: every answer of space.site.config on OpenVibe.Space. GET, PUT and DELETE /api/v1/sites/:id/config → { config } (after a PUT, the configuration as stored; after a DELETE, the defaults).
+ */
+export interface SpaceSiteConfigResult {
+ config: SpaceSiteConfig;
+}
+
+/** [email protected] (owner: space) */
+/**
+ * space.deploy.activated v1 (OpenVibe.Space server/domain/deploys.js switchPointer, Stage B tenant hosting). A tenant site's active deploy changed, written into Host's event outbox in the transaction that moves the pointer (activate or rollback). Envelope: subject { type: deploy, id: <deploy id> }, visibility internal, priority low, actor the person or app that switched it. A network service's release going live is space.release.published, not this.
+ */
+export interface SpaceDeployActivatedPayload {
+ project_id: string;
+ site_id: string;
+ /**
+ * The site's name (<site>.openvibe.website).
+ */
+ site: string;
+ deploy_id: string;
+ /**
+ * The deploy that served before; null for the site's first activation.
+ */
+ previous_deploy_id: string | null;
+ /**
+ * true when the switch was a rollback.
+ */
+ rollback: boolean;
+}
+
+/** [email protected] (owner: space) */
+/**
+ * space.deploy.created v1 (OpenVibe.Space, subject deploy dpl_…, visibility internal): an upload or a Git ingest became an immutable, content-addressed deploy in the ready state. It is not yet serving: space.deploy.activated follows when it is made active. No file contents or paths.
+ */
+export interface SpaceDeployCreatedPayload {
+ project_id: string;
+ site_id: string;
+ /**
+ * the site name (one DNS label; the site is served at <site>.openvibe.website)
+ */
+ site: string;
+ /**
+ * how the deploy arrived: an archive or individual files, a private preview upload, or a Git ingest (always a preview)
+ */
+ source: "archive" | "files" | "preview" | "git";
+ file_count: number;
+ total_bytes: number;
+ /**
+ * sha256 of the deploy manifest (the content address of the whole deploy)
+ */
+ manifest_sha256: string;
+}
+
+/** [email protected] (owner: space) */
+/**
+ * space.deploy.failed v1 (OpenVibe.Space, subject deploy dpl_…, visibility internal): an upload was refused and recorded as a failed deploy with its log, so the owner can see why. Problem codes only, at most ten; never file contents.
+ */
+export interface SpaceDeployFailedPayload {
+ project_id: string;
+ site_id: string;
+ /**
+ * the site name (one DNS label; the site is served at <site>.openvibe.website)
+ */
+ site: string;
+ /**
+ * the refusal problem code (for example deploy.too_large or storage.space_full)
+ */
+ code: string;
+ /**
+ * the problem codes of the files that were refused
+ *
+ * @maxItems 10
+ */
+ problems:
+ | []
+ | [string]
+ | [string, string]
+ | [string, string, string]
+ | [string, string, string, string]
+ | [string, string, string, string, string]
+ | [string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string, string];
+}
+
+/** [email protected] (owner: space) */
+/**
+ * space.domain.verified v1 (OpenVibe.Space, subject domain dom_…, visibility internal): a custom domain's TXT record (_openvibe-space.<hostname>) was found, and the domain now serves the site.
+ */
+export interface SpaceDomainVerifiedPayload {
+ project_id: string;
+ site_id: string;
+ /**
+ * the site name (one DNS label; the site is served at <site>.openvibe.website)
+ */
+ site: string;
+ /**
+ * the custom domain, a fully qualified host name in lower case
+ */
+ hostname: string;
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,31 @@
@@ -0,0 +1,31 @@
+{
+ "id": "space.deploy.create",
+ "version": "1.0",
+ "owner": "space",
+ "status": "active",
+ "visibility": "first-party",
+ "description": "Upload an immutable static deploy (tar/tar.gz or multipart files; validated for path traversal, links, special and hidden files, server-side code, file types and quotas; never executed), list deploys and their upload logs, activate a deploy and roll a site back to any earlier ready deploy (one atomic pointer switch). Also judged by the acting principal's project role (deployer or above); an app principal added to a project as deployer can deploy from CI. A site with a Git source also accepts a CI-built deploy at /sites/:id/source/deploys naming the ref and full commit_sha; it is always a preview and records that commit as immutable provenance.",
+ "permissions": [
+ "host:deploy"
+ ],
+ "resourceConstraints": [
+ "project"
+ ],
+ "quotaClass": "space-deploy",
+ "events": [
+ "space.deploy.created",
+ "space.deploy.activated",
+ "space.deploy.failed"
+ ],
+ "implementedBy": [
+ "GET /api/v1/sites/:id/deploys",
+ "POST /api/v1/sites/:id/deploys",
+ "POST /api/v1/sites/:id/source/deploys",
+ "GET /api/v1/deploys/:id",
+ "GET /api/v1/deploys/:id/log",
+ "POST /api/v1/deploys/:id/activate",
+ "POST /api/v1/sites/:id/rollback"
+ ],
+ "inputSchema": "space.deploy-create-request@1",
+ "outputSchema": "space.deploy-create-result@1"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
+{
+ "id": "space.domain.manage",
+ "version": "1.0",
+ "owner": "space",
+ "status": "active",
+ "visibility": "first-party",
+ "description": "Add a custom domain to a static site (never an OpenVibe domain; not in sandbox projects), read its DNS TXT verification and routing records, trigger a verification check, and remove it. A custom domain is served only after its TXT record is verified; TLS certificates are issued by an operator and never pass through this capability.",
+ "permissions": [
+ "space:write"
+ ],
+ "resourceConstraints": [
+ "project"
+ ],
+ "quotaClass": "space-manage",
+ "events": [
+ "space.domain.verified"
+ ],
+ "implementedBy": [
+ "GET /api/v1/sites/:id/domains",
+ "POST /api/v1/sites/:id/domains",
+ "POST /api/v1/domains/:id/verify",
+ "DELETE /api/v1/domains/:id"
+ ],
+ "inputSchema": "space.domain-manage-request@1",
+ "outputSchema": "space.domain-manage-result@1"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
+{
+ "id": "space.resource.read",
+ "version": "1.0",
+ "owner": "space",
+ "status": "active",
+ "visibility": "first-party",
+ "description": "Read OpenVibe.Space's resource index (ADR-048): GET /api/v1/resources lists the resources Host owns as a page of common.resource-summary@1 (common.resource-list-result@1), and GET /api/v1/resources/:ovrn reads one by its OVRN (common.resource-summary@1). Every authority serves these for OpenVibe.Services to fan out and merge; Host's own resources are its static sites (kind space.site, id sit_<ULID>), the immutable deploys of those sites (kind space.deploy, id dpl_<ULID>) and their default and custom domains (kind space.domain, id dom_<ULID>), each with the project_id of the Network project that owns it. Host lists no projects; only Network lists projects. Planned until Host ships the index.",
+ "permissions": [
+ "resources:read"
+ ],
+ "resourceConstraints": [
+ "none"
+ ],
+ "quotaClass": "standard",
+ "events": [],
+ "implementedBy": [
+ "GET /api/v1/resources",
+ "GET /api/v1/resources/:ovrn"
+ ],
+ "inputSchema": "common.no-body@1",
+ "outputSchema": "common.resource-list-result@1"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
+{
+ "id": "space.site.config",
+ "version": "1.0",
+ "owner": "space",
+ "status": "active",
+ "visibility": "first-party",
+ "description": "Read, set and reset a Host site's serving configuration: response headers, local-only redirects and the SPA fallback (extensionless paths serve index.html). A capability holder must also hold the acting principal's project role (maintainer or owner to change, any role to read). Reserved headers (Content-Security-Policy, Strict-Transport-Security, Set-Cookie, X-Forwarded-*, caching, scope, transport and the platform's own) are refused, and a redirect target must be a local path, so a site can never become an open redirect.",
+ "permissions": [
+ "space:write"
+ ],
+ "resourceConstraints": [
+ "project"
+ ],
+ "quotaClass": "space-manage",
+ "events": [],
+ "implementedBy": [
+ "GET /api/v1/sites/:id/config",
+ "PUT /api/v1/sites/:id/config",
+ "DELETE /api/v1/sites/:id/config"
+ ],
+ "inputSchema": "space.site-config-request@1",
+ "outputSchema": "space.site-config-result@1"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
+{
+ "id": "space.site.manage",
+ "version": "1.0",
+ "owner": "space",
+ "status": "active",
+ "visibility": "first-party",
+ "description": "Manage OpenVibe.Space tenant projects and static sites: create a project (owned by the person named with X-OV-Subject; apps cannot own projects), read projects, quotas and usage, manage members (owner role), create and delete sites, delete a deploy that is not active. Also judged by the acting principal's project role; a sandbox token (env=sandbox) is refused on production projects. Quota changes and takedowns (stop serving a site or a whole project while keeping its content for review; lift) additionally need a Network staff user. Read, set or remove a site's Git source (provider, public repository URL and branch; never a credential).",
+ "permissions": [
+ "space:write"
+ ],
+ "resourceConstraints": [
+ "project"
+ ],
+ "quotaClass": "space-manage",
+ "events": [],
+ "implementedBy": [
+ "GET /api/v1/projects",
+ "POST /api/v1/projects",
+ "GET /api/v1/projects/:id",
+ "DELETE /api/v1/projects/:id",
+ "GET /api/v1/projects/:id/quota",
+ "PUT /api/v1/projects/:id/quota",
+ "PUT /api/v1/projects/:id/members/:principal",
+ "DELETE /api/v1/projects/:id/members/:principal",
+ "GET /api/v1/projects/:id/sites",
+ "POST /api/v1/projects/:id/sites",
+ "GET /api/v1/sites/:id",
+ "GET /api/v1/sites/:id/source",
+ "PUT /api/v1/sites/:id/source",
+ "DELETE /api/v1/sites/:id/source",
+ "DELETE /api/v1/sites/:id",
+ "DELETE /api/v1/deploys/:id",
+ "POST /api/v1/projects/:id/takedown",
+ "DELETE /api/v1/projects/:id/takedown",
+ "POST /api/v1/sites/:id/takedown",
+ "DELETE /api/v1/sites/:id/takedown"
+ ],
+ "inputSchema": "space.site-manage-request@1",
+ "outputSchema": "space.site-manage-result@1"
+}
@@ -18,36 +18,47 @@
@@ -18,36 +18,47 @@
"site": {
"site": {
"name": "Space",
"name": "Space",
"icon": "space",
"icon": "space",
- "tagline": "Code hosting and spaces",
- "what": "OpenVibe's code hosting, and \"spaces\": apps that use your OpenVibe account and the SDK, coming next. The forum returned to OpenVibe.Community.",
+ "tagline": "Static sites, hosted by OpenVibe",
+ "what": "Static site hosting moved here from OpenVibe.Host: immutable deploys, one-step rollback, previews, Git ingest and verified custom domains, at <name>.openvibe.website. Open to OpenVibe staff until the public launch; dynamic pages and spaces come next. The forum is on OpenVibe.Community.",
"legalProfile": "ugc",
"legalProfile": "ugc",
"position": 19,
"position": 19,
"tld": "space",
"tld": "space",
"accent": "#818cf8",
"accent": "#818cf8",
- "description": "OpenVibe.Space will host code and \"spaces\" — apps that use your OpenVibe account and the SDK — and both are still being built. Until they land it serves a home, and its former /s/* forum paths redirect permanently to openvibe.community.",
+ "description": "OpenVibe.Space hosts static sites: every deploy is immutable and content-addressed, the active one rolls back in one step, previews are private to the project, a site can deploy from a public Git repository, and custom domains are served after a TXT check. Sites get a name under openvibe.website. Hosting is open to OpenVibe staff until the public launch, which waits for abuse handling and a designated DMCA agent. Dynamic pages and spaces (apps on your OpenVibe account) are next and not built yet. The former /s/* forum paths redirect permanently to openvibe.community.",
"pillars": [
"pillars": [
{
{
- "title": "Code hosting",
- "text": "The network's code, hosted here."
+ "title": "Immutable deploys",
+ "text": "Every deploy is kept, content-addressed, and the active one rolls back in one step."
},
},
{
{
- "title": "Spaces",
- "text": "Apps that use your OpenVibe account and the SDK."
+ "title": "Previews and Git",
+ "text": "Private previews for the project, and deploys from a public Git repository."
},
},
{
{
- "title": "Coming next",
- "text": "Neither is built yet; Space serves a home until they land."
+ "title": "Your own address",
+ "text": "A name under openvibe.website, or a custom domain verified by a TXT record."
},
},
{
{
- "title": "One account",
- "text": "Your OpenVibe identity, everywhere on the network."
+ "title": "Staff first",
+ "text": "Open to OpenVibe staff until abuse handling and a DMCA agent are in place."
}
}
],
],
"keywords": "openvibe space, code hosting, openvibe spaces, openvibe account, openvibe sdk",
"keywords": "openvibe space, code hosting, openvibe spaces, openvibe account, openvibe sdk",
"launch": "Code hosting and spaces are still to be built; the forum moved back to OpenVibe.Community (2026-10-08)."
"launch": "Code hosting and spaces are still to be built; the forum moved back to OpenVibe.Community (2026-10-08)."
},
},
- "capabilities": [],
- "eventsProduced": [],
+ "capabilities": [
+ "space.site.manage",
+ "space.deploy.create",
+ "space.domain.manage",
+ "space.site.config",
+ "space.resource.read"
+ ],
+ "eventsProduced": [
+ "space.deploy.created",
+ "space.deploy.activated",
+ "space.deploy.failed",
+ "space.domain.verified"
+ ],
"eventsConsumed": [],
"eventsConsumed": [],
"namespacesOwned": [
"namespacesOwned": [
"space.*"
"space.*"
@@ -91,7 +102,7 @@
@@ -91,7 +102,7 @@
"none": "no claims: one Space process runs and owns its database"
"none": "no claims: one Space process runs and owns its database"
}
}
},
},
- "notes": "Space is OpenVibe's code hosting, and \"spaces\" — apps that use your OpenVibe account and the SDK — come next. The forum returned to OpenVibe.Community (owner decision 2026-10-08), so Space serves a home and its former /s/* forum paths redirect permanently to openvibe.community; it keeps no forum capability, contract or event.",
+ "notes": "Space is OpenVibe's static site hosting since T20 step 2 (2026-10-12): the engine moved from OpenVibe.Host with its tables (Space migrations 0002-0005), under space.* capabilities and events. Tenant sites are served by Host header at <site>.openvibe.website and on TXT-verified custom domains; deploys are immutable and content-addressed with one-step rollback, previews, Git ingest from public repositories, per-site headers and redirects, takedowns and a resource index. SPACE_PUBLIC_HOSTING is off: only OpenVibe staff create projects, everyone else gets 403 space.not_launched. Consumes OpenVibe.Network (SSO, JWKS, client credentials) and OpenVibe.Media (objects, when the object store is media) and publishes through OpenVibe.Events (events.event.publish). The former forum paths /s/* redirect permanently to openvibe.community; the forum capabilities stay retired. The host.* hosting ids are retired once Host has dropped the engine.",
"health": "/api/health",
"health": "/api/health",
"ready": "/api/ready",
"ready": "/api/ready",
"internalOrigin": "http://127.0.0.1:4940"
"internalOrigin": "http://127.0.0.1:4940"
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.148.0",
+ "version": "0.149.0",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"main": "index.js",
"main": "index.js",
"types": "generated/typescript/index.d.ts",
"types": "generated/typescript/index.d.ts",