openvibers/openvibe.contracts Commits
Capability schemas (WS-C task 4), Search first: search.query@1, search.query-result@1, search.write-result@1 on search.query.run, search.query.delegate and search.document.write (unreleased)
Changes
@@@ -4,6 +4,10 @@ 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`).++## Unreleased++++Capability schemas (roadmap WS-C task 4), starting with Search: **`search.query@1`** (the query string of `GET /api/v1/search` and `/suggest`: q, owner, type, lang, facets, limit, cursor, `facet.<key>`), **`search.query-result@1`** (a page of hits the caller may see, `next_cursor`, facet counts; never an ACL or a total) and **`search.write-result@1`** (`PUT /api/v1/documents/…`: applied or unchanged; stale and conflicting revisions are 409 problems). `search.query.run` and `search.query.delegate` name the first two; `search.document.write` names the third as its output. Fixtures for each. Additive.++## 0.49.0 — 2026-09-25Platform blocks (roadmap WS-E task 5). **`network.block.changed`**: a person blocked or unblocked someone (`blocker`, `blocked`, `active`, a per-pair `revision`); Network keeps the blocks, keyed by subjects, and Chat and Community consume the event. New capability **`network.blocks.read`** (first-party, never for apps): `GET /internal/blocks?subject=` lists who a person blocked and who blocked them. Fixtures for a block and an unblock. Additive.@@@ -146,6 +146,33 @@"status": "active","adr": "ADR-018"},++ {++ "id": "search.query",++ "version": "1.0.0",++ "owner": "search",++ "schema": "search/query.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "search.query-result",++ "version": "1.0.0",++ "owner": "search",++ "schema": "search/query-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "search.write-result",++ "version": "1.0.0",++ "owner": "search",++ "schema": "search/write-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },{"id": "sources.source","version": "1.0.0",nnew file mode 100644@@@ -0,0 +1,40 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/search/query-result.v1.json",++ "title": "SearchQueryResult",++ "description": "search.query-result@1: the answer of GET /api/v1/search — one page of hits the caller may see, a cursor for the next page, and facet counts when asked. Never a total count and never an ACL.",++ "type": "object",++ "required": ["results", "next_cursor"],++ "additionalProperties": false,++ "properties": {++ "results": {++ "type": "array",++ "maxItems": 100,++ "items": {++ "type": "object",++ "required": ["owner", "type", "id"],++ "additionalProperties": false,++ "properties": {++ "owner": { "type": "string" },++ "type": { "type": "string" },++ "id": { "type": "string" },++ "revision": { "type": "integer", "minimum": 0 },++ "visibility": { "type": "string" },++ "title": { "type": ["string", "null"] },++ "summary": { "type": ["string", "null"] },++ "canonical_url": { "type": ["string", "null"] },++ "facets": { "type": ["object", "null"] },++ "language": { "type": ["string", "null"] },++ "authorship": { "type": ["string", "null"] },++ "provenance": { "type": ["array", "object", "null"] },++ "published_at": { "type": ["string", "null"] },++ "updated_at": { "type": ["string", "null"] },++ "indexable": { "type": "boolean", "description": "false for a noindex document the caller may still see." },++ "snippet_html": { "type": ["string", "null"], "description": "Escaped text with <mark> around the matches; nothing else." }++ }++ }++ },++ "next_cursor": { "type": ["string", "null"] },++ "facets": { "type": "object", "additionalProperties": { "type": "array", "items": { "type": "object", "required": ["value", "count"], "properties": { "value": { "type": "string" }, "count": { "type": "integer", "minimum": 0 } } } } }++ }++}nnew file mode 100644@@@ -0,0 +1,23 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/search/query.v1.json",++ "title": "SearchQuery",++ "description": "search.query@1: the query string of GET /api/v1/search (and, with q/owner/type/limit only, GET /api/v1/suggest), capabilities search.query.run and search.query.delegate. Who sees what is decided by Search per hit: anonymous callers get public, published, indexable documents; a person also restricted documents naming them; a delegating service passes X-OV-Subject. Filters narrow; they never widen visibility.",++ "type": "object",++ "additionalProperties": false,++ "properties": {++ "q": { "type": "string", "maxLength": 200, "description": "Words to match (FTS5; empty lists by recency)." },++ "owner": { "type": "string", "pattern": "^[a-z][a-z0-9_-]{0,31}$", "description": "One owner service (live, community, wiki, …)." },++ "type": { "type": "string", "pattern": "^[a-z][a-z0-9_-]{0,31}$", "description": "One document type within the owner (channel, vod, thread, …)." },++ "lang": { "type": "string", "pattern": "^[a-zA-Z]{2,3}(-[A-Za-z0-9]{2,8})*$", "description": "BCP 47 language." },++ "facets": { "type": "string", "maxLength": 400, "description": "Comma-separated facet keys to count over what the caller can see." },++ "limit": { "type": ["integer", "string"], "pattern": "^[0-9]{1,3}$", "minimum": 1, "maximum": 100 },++ "cursor": { "type": "string", "maxLength": 512, "description": "next_cursor of the previous page." }++ },++ "patternProperties": {++ "^facet\\.[a-z][a-z0-9_]{0,39}$": {++ "description": "A facet filter (facet.<key>=<value>); repeat the parameter for several values.",++ "oneOf": [ { "type": "string", "maxLength": 200 }, { "type": "array", "maxItems": 20, "items": { "type": "string", "maxLength": 200 } } ]++ }++ }++}nnew file mode 100644@@@ -0,0 +1,15 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/search/write-result.v1.json",++ "title": "SearchWriteResult",++ "description": "search.write-result@1: the answer of PUT /api/v1/documents/:owner/:type/:id (search.document.write). applied: stored (or a tombstone recorded); unchanged: the same revision with the same content. A revision older than the stored one is a 409 problem search.stale_revision; the same revision with different content is 409 search.revision_conflict.",++ "type": "object",++ "required": ["outcome", "revision"],++ "additionalProperties": false,++ "properties": {++ "outcome": { "enum": ["applied", "unchanged"] },++ "revision": { "type": "integer", "minimum": 1 },++ "stored_revision": { "type": ["integer", "null"], "description": "What Search held before this write (null: nothing)." },++ "deleted": { "type": "boolean" }++ }++}nnew file mode 100644@@@ -0,0 +1 @@++{ "results": [ { "owner": "live", "type": "vod", "id": "912", "acl": ["usr_x"] } ], "next_cursor": null }nnew file mode 100644@@@ -0,0 +1 @@++{ "results": [ { "owner": "live", "type": "vod", "id": "912", "revision": 3, "visibility": "public", "title": "Building a forum", "summary": null, "canonical_url": "https://openvibe.live/vod/912", "facets": { "channel": "alex" }, "language": null, "authorship": "human", "provenance": [], "published_at": "2026-09-24T19:00:00.000Z", "updated_at": null, "indexable": true, "snippet_html": "building a <mark>forum</mark>" } ], "next_cursor": null }nnew file mode 100644@@@ -0,0 +1 @@++{ "q": "x", "facet.Bad Key": "y" }nnew file mode 100644@@@ -0,0 +1 @@++{ "q": "minecraft", "owner": "live", "type": "vod", "limit": "24", "facet.channel": "alex", "facets": "category,channel" }nnew file mode 100644@@@ -0,0 +1 @@++{ "outcome": "stale", "revision": 1 }nnew file mode 100644@@@ -0,0 +1 @@++{ "outcome": "applied", "revision": 2, "stored_revision": 1, "deleted": false }@@@ -1860,6 +1860,262 @@}}},++ "search.query": {++ "version": "1.0.0",++ "owner": "search",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/search/query.v1.json",++ "title": "SearchQuery",++ "description": "search.query@1: the query string of GET /api/v1/search (and, with q/owner/type/limit only, GET /api/v1/suggest), capabilities search.query.run and search.query.delegate. Who sees what is decided by Search per hit: anonymous callers get public, published, indexable documents; a person also restricted documents naming them; a delegating service passes X-OV-Subject. Filters narrow; they never widen visibility.",++ "type": "object",++ "additionalProperties": false,++ "properties": {++ "q": {++ "type": "string",++ "maxLength": 200,++ "description": "Words to match (FTS5; empty lists by recency)."++ },++ "owner": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_-]{0,31}$",++ "description": "One owner service (live, community, wiki, …)."++ },++ "type": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_-]{0,31}$",++ "description": "One document type within the owner (channel, vod, thread, …)."++ },++ "lang": {++ "type": "string",++ "pattern": "^[a-zA-Z]{2,3}(-[A-Za-z0-9]{2,8})*$",++ "description": "BCP 47 language."++ },++ "facets": {++ "type": "string",++ "maxLength": 400,++ "description": "Comma-separated facet keys to count over what the caller can see."++ },++ "limit": {++ "type": [++ "integer",++ "string"++ ],++ "pattern": "^[0-9]{1,3}$",++ "minimum": 1,++ "maximum": 100++ },++ "cursor": {++ "type": "string",++ "maxLength": 512,++ "description": "next_cursor of the previous page."++ }++ },++ "patternProperties": {++ "^facet\\.[a-z][a-z0-9_]{0,39}$": {++ "description": "A facet filter (facet.<key>=<value>); repeat the parameter for several values.",++ "oneOf": [++ {++ "type": "string",++ "maxLength": 200++ },++ {++ "type": "array",++ "maxItems": 20,++ "items": {++ "type": "string",++ "maxLength": 200++ }++ }++ ]++ }++ }++ }++ },++ "search.query-result": {++ "version": "1.0.0",++ "owner": "search",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/search/query-result.v1.json",++ "title": "SearchQueryResult",++ "description": "search.query-result@1: the answer of GET /api/v1/search — one page of hits the caller may see, a cursor for the next page, and facet counts when asked. Never a total count and never an ACL.",++ "type": "object",++ "required": [++ "results",++ "next_cursor"++ ],++ "additionalProperties": false,++ "properties": {++ "results": {++ "type": "array",++ "maxItems": 100,++ "items": {++ "type": "object",++ "required": [++ "owner",++ "type",++ "id"++ ],++ "additionalProperties": false,++ "properties": {++ "owner": {++ "type": "string"++ },++ "type": {++ "type": "string"++ },++ "id": {++ "type": "string"++ },++ "revision": {++ "type": "integer",++ "minimum": 0++ },++ "visibility": {++ "type": "string"++ },++ "title": {++ "type": [++ "string",++ "null"++ ]++ },++ "summary": {++ "type": [++ "string",++ "null"++ ]++ },++ "canonical_url": {++ "type": [++ "string",++ "null"++ ]++ },++ "facets": {++ "type": [++ "object",++ "null"++ ]++ },++ "language": {++ "type": [++ "string",++ "null"++ ]++ },++ "authorship": {++ "type": [++ "string",++ "null"++ ]++ },++ "provenance": {++ "type": [++ "array",++ "object",++ "null"++ ]++ },++ "published_at": {++ "type": [++ "string",++ "null"++ ]++ },++ "updated_at": {++ "type": [++ "string",++ "null"++ ]++ },++ "indexable": {++ "type": "boolean",++ "description": "false for a noindex document the caller may still see."++ },++ "snippet_html": {++ "type": [++ "string",++ "null"++ ],++ "description": "Escaped text with <mark> around the matches; nothing else."++ }++ }++ }++ },++ "next_cursor": {++ "type": [++ "string",++ "null"++ ]++ },++ "facets": {++ "type": "object",++ "additionalProperties": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "value",++ "count"++ ],++ "properties": {++ "value": {++ "type": "string"++ },++ "count": {++ "type": "integer",++ "minimum": 0++ }++ }++ }++ }++ }++ }++ }++ },++ "search.write-result": {++ "version": "1.0.0",++ "owner": "search",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/search/write-result.v1.json",++ "title": "SearchWriteResult",++ "description": "search.write-result@1: the answer of PUT /api/v1/documents/:owner/:type/:id (search.document.write). applied: stored (or a tombstone recorded); unchanged: the same revision with the same content. A revision older than the stored one is a 409 problem search.stale_revision; the same revision with different content is 409 search.revision_conflict.",++ "type": "object",++ "required": [++ "outcome",++ "revision"++ ],++ "additionalProperties": false,++ "properties": {++ "outcome": {++ "enum": [++ "applied",++ "unchanged"++ ]++ },++ "revision": {++ "type": "integer",++ "minimum": 1++ },++ "stored_revision": {++ "type": [++ "integer",++ "null"++ ],++ "description": "What Search held before this write (null: nothing)."++ },++ "deleted": {++ "type": "boolean"++ }++ }++ }++ },"sources.source": {"version": "1.0.0","owner": "sources",@@@ -839,6 +839,235 @@ export interface IndexDocument {};}++/** [email protected] (owner: search) */++/**++ * search.query@1: the query string of GET /api/v1/search (and, with q/owner/type/limit only, GET /api/v1/suggest), capabilities search.query.run and search.query.delegate. Who sees what is decided by Search per hit: anonymous callers get public, published, indexable documents; a person also restricted documents naming them; a delegating service passes X-OV-Subject. Filters narrow; they never widen visibility.++ */++export interface SearchQuery {++ /**++ * Words to match (FTS5; empty lists by recency).++ */++ q?: string;++ /**++ * One owner service (live, community, wiki, …).++ */++ owner?: string;++ /**++ * One document type within the owner (channel, vod, thread, …).++ */++ type?: string;++ /**++ * BCP 47 language.++ */++ lang?: string;++ /**++ * Comma-separated facet keys to count over what the caller can see.++ */++ facets?: string;++ limit?: number | string;++ /**++ * next_cursor of the previous page.++ */++ cursor?: string;++ /**++ * A facet filter (facet.<key>=<value>); repeat the parameter for several values.++ *++ * This interface was referenced by `SearchQuery`'s JSON-Schema definition++ * via the `patternProperty` "^facet\.[a-z][a-z0-9_]{0,39}$".++ */++ [k: 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, 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,++ 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,++ 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: search) */++/**++ * search.query-result@1: the answer of GET /api/v1/search — one page of hits the caller may see, a cursor for the next page, and facet counts when asked. Never a total count and never an ACL.++ */++export interface SearchQueryResult {++ /**++ * @maxItems 100++ */++ results: {++ owner: string;++ type: string;++ id: string;++ revision?: number;++ visibility?: string;++ title?: string | null;++ summary?: string | null;++ canonical_url?: string | null;++ facets?: {} | null;++ language?: string | null;++ authorship?: string | null;++ provenance?: unknown[] | {} | null;++ published_at?: string | null;++ updated_at?: string | null;++ /**++ * false for a noindex document the caller may still see.++ */++ indexable?: boolean;++ /**++ * Escaped text with <mark> around the matches; nothing else.++ */++ snippet_html?: string | null;++ }[];++ next_cursor: string | null;++ facets?: {++ [k: string]:++ | {++ value: string;++ count: number;++ }[]++ | undefined;++ };++}++++/** [email protected] (owner: search) */++/**++ * search.write-result@1: the answer of PUT /api/v1/documents/:owner/:type/:id (search.document.write). applied: stored (or a tombstone recorded); unchanged: the same revision with the same content. A revision older than the stored one is a 409 problem search.stale_revision; the same revision with different content is 409 search.revision_conflict.++ */++export interface SearchWriteResult {++ outcome: "applied" | "unchanged";++ revision: number;++ /**++ * What Search held before this write (null: nothing).++ */++ stored_revision?: number | null;++ deleted?: boolean;++}++/** [email protected] (owner: sources) *//*** A registered source (roadmap §4.2 B, Wave 14): where it is fetched from and on what terms. Credentials are referenced by environment-variable NAME only. A source's existence is never permission to fetch: robots and terms are verified before it is enabled, and the fetcher re-checks robots.txt on every run.@@@ -22,5 +22,6 @@"GET /api/v1/owners/:owner/documents","GET /api/v1/owners/:owner/documents/:type/:id","GET /api/v1/owners/:owner/rejections"-- ]++ ],++ "outputSchema": "search.write-result@1"}@@@ -17,5 +17,7 @@"GET /api/v1/search","GET /api/v1/suggest","GET /api/v1/documents/:owner/:type/:id"-- ]++ ],++ "inputSchema": "search.query@1",++ "outputSchema": "search.query-result@1"}@@@ -17,5 +17,7 @@"GET /api/v1/search","GET /api/v1/suggest","GET /api/v1/documents/:owner/:type/:id"-- ]++ ],++ "inputSchema": "search.query@1",++ "outputSchema": "search.query-result@1"}
@@ -4,6 +4,10 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
@@ -4,6 +4,10 @@ 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`).
+## Unreleased
+
+Capability schemas (roadmap WS-C task 4), starting with Search: **`search.query@1`** (the query string of `GET /api/v1/search` and `/suggest`: q, owner, type, lang, facets, limit, cursor, `facet.<key>`), **`search.query-result@1`** (a page of hits the caller may see, `next_cursor`, facet counts; never an ACL or a total) and **`search.write-result@1`** (`PUT /api/v1/documents/…`: applied or unchanged; stale and conflicting revisions are 409 problems). `search.query.run` and `search.query.delegate` name the first two; `search.document.write` names the third as its output. Fixtures for each. Additive.
+
## 0.49.0 — 2026-09-25
## 0.49.0 — 2026-09-25
Platform blocks (roadmap WS-E task 5). **`network.block.changed`**: a person blocked or unblocked someone (`blocker`, `blocked`, `active`, a per-pair `revision`); Network keeps the blocks, keyed by subjects, and Chat and Community consume the event. New capability **`network.blocks.read`** (first-party, never for apps): `GET /internal/blocks?subject=` lists who a person blocked and who blocked them. Fixtures for a block and an unblock. Additive.
Platform blocks (roadmap WS-E task 5). **`network.block.changed`**: a person blocked or unblocked someone (`blocker`, `blocked`, `active`, a per-pair `revision`); Network keeps the blocks, keyed by subjects, and Chat and Community consume the event. New capability **`network.blocks.read`** (first-party, never for apps): `GET /internal/blocks?subject=` lists who a person blocked and who blocked them. Fixtures for a block and an unblock. Additive.
@@ -146,6 +146,33 @@
@@ -146,6 +146,33 @@
"status": "active",
"status": "active",
"adr": "ADR-018"
"adr": "ADR-018"
},
},
+ {
+ "id": "search.query",
+ "version": "1.0.0",
+ "owner": "search",
+ "schema": "search/query.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "search.query-result",
+ "version": "1.0.0",
+ "owner": "search",
+ "schema": "search/query-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "search.write-result",
+ "version": "1.0.0",
+ "owner": "search",
+ "schema": "search/write-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
{
{
"id": "sources.source",
"id": "sources.source",
"version": "1.0.0",
"version": "1.0.0",
new file mode 100644
new file mode 100644
@@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/search/query-result.v1.json",
+ "title": "SearchQueryResult",
+ "description": "search.query-result@1: the answer of GET /api/v1/search — one page of hits the caller may see, a cursor for the next page, and facet counts when asked. Never a total count and never an ACL.",
+ "type": "object",
+ "required": ["results", "next_cursor"],
+ "additionalProperties": false,
+ "properties": {
+ "results": {
+ "type": "array",
+ "maxItems": 100,
+ "items": {
+ "type": "object",
+ "required": ["owner", "type", "id"],
+ "additionalProperties": false,
+ "properties": {
+ "owner": { "type": "string" },
+ "type": { "type": "string" },
+ "id": { "type": "string" },
+ "revision": { "type": "integer", "minimum": 0 },
+ "visibility": { "type": "string" },
+ "title": { "type": ["string", "null"] },
+ "summary": { "type": ["string", "null"] },
+ "canonical_url": { "type": ["string", "null"] },
+ "facets": { "type": ["object", "null"] },
+ "language": { "type": ["string", "null"] },
+ "authorship": { "type": ["string", "null"] },
+ "provenance": { "type": ["array", "object", "null"] },
+ "published_at": { "type": ["string", "null"] },
+ "updated_at": { "type": ["string", "null"] },
+ "indexable": { "type": "boolean", "description": "false for a noindex document the caller may still see." },
+ "snippet_html": { "type": ["string", "null"], "description": "Escaped text with <mark> around the matches; nothing else." }
+ }
+ }
+ },
+ "next_cursor": { "type": ["string", "null"] },
+ "facets": { "type": "object", "additionalProperties": { "type": "array", "items": { "type": "object", "required": ["value", "count"], "properties": { "value": { "type": "string" }, "count": { "type": "integer", "minimum": 0 } } } } }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/search/query.v1.json",
+ "title": "SearchQuery",
+ "description": "search.query@1: the query string of GET /api/v1/search (and, with q/owner/type/limit only, GET /api/v1/suggest), capabilities search.query.run and search.query.delegate. Who sees what is decided by Search per hit: anonymous callers get public, published, indexable documents; a person also restricted documents naming them; a delegating service passes X-OV-Subject. Filters narrow; they never widen visibility.",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "q": { "type": "string", "maxLength": 200, "description": "Words to match (FTS5; empty lists by recency)." },
+ "owner": { "type": "string", "pattern": "^[a-z][a-z0-9_-]{0,31}$", "description": "One owner service (live, community, wiki, …)." },
+ "type": { "type": "string", "pattern": "^[a-z][a-z0-9_-]{0,31}$", "description": "One document type within the owner (channel, vod, thread, …)." },
+ "lang": { "type": "string", "pattern": "^[a-zA-Z]{2,3}(-[A-Za-z0-9]{2,8})*$", "description": "BCP 47 language." },
+ "facets": { "type": "string", "maxLength": 400, "description": "Comma-separated facet keys to count over what the caller can see." },
+ "limit": { "type": ["integer", "string"], "pattern": "^[0-9]{1,3}$", "minimum": 1, "maximum": 100 },
+ "cursor": { "type": "string", "maxLength": 512, "description": "next_cursor of the previous page." }
+ },
+ "patternProperties": {
+ "^facet\\.[a-z][a-z0-9_]{0,39}$": {
+ "description": "A facet filter (facet.<key>=<value>); repeat the parameter for several values.",
+ "oneOf": [ { "type": "string", "maxLength": 200 }, { "type": "array", "maxItems": 20, "items": { "type": "string", "maxLength": 200 } } ]
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/search/write-result.v1.json",
+ "title": "SearchWriteResult",
+ "description": "search.write-result@1: the answer of PUT /api/v1/documents/:owner/:type/:id (search.document.write). applied: stored (or a tombstone recorded); unchanged: the same revision with the same content. A revision older than the stored one is a 409 problem search.stale_revision; the same revision with different content is 409 search.revision_conflict.",
+ "type": "object",
+ "required": ["outcome", "revision"],
+ "additionalProperties": false,
+ "properties": {
+ "outcome": { "enum": ["applied", "unchanged"] },
+ "revision": { "type": "integer", "minimum": 1 },
+ "stored_revision": { "type": ["integer", "null"], "description": "What Search held before this write (null: nothing)." },
+ "deleted": { "type": "boolean" }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "results": [ { "owner": "live", "type": "vod", "id": "912", "acl": ["usr_x"] } ], "next_cursor": null }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "results": [ { "owner": "live", "type": "vod", "id": "912", "revision": 3, "visibility": "public", "title": "Building a forum", "summary": null, "canonical_url": "https://openvibe.live/vod/912", "facets": { "channel": "alex" }, "language": null, "authorship": "human", "provenance": [], "published_at": "2026-09-24T19:00:00.000Z", "updated_at": null, "indexable": true, "snippet_html": "building a <mark>forum</mark>" } ], "next_cursor": null }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "q": "x", "facet.Bad Key": "y" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "q": "minecraft", "owner": "live", "type": "vod", "limit": "24", "facet.channel": "alex", "facets": "category,channel" }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "outcome": "stale", "revision": 1 }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "outcome": "applied", "revision": 2, "stored_revision": 1, "deleted": false }
@@ -1860,6 +1860,262 @@
@@ -1860,6 +1860,262 @@
}
}
}
}
},
},
+ "search.query": {
+ "version": "1.0.0",
+ "owner": "search",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/search/query.v1.json",
+ "title": "SearchQuery",
+ "description": "search.query@1: the query string of GET /api/v1/search (and, with q/owner/type/limit only, GET /api/v1/suggest), capabilities search.query.run and search.query.delegate. Who sees what is decided by Search per hit: anonymous callers get public, published, indexable documents; a person also restricted documents naming them; a delegating service passes X-OV-Subject. Filters narrow; they never widen visibility.",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "q": {
+ "type": "string",
+ "maxLength": 200,
+ "description": "Words to match (FTS5; empty lists by recency)."
+ },
+ "owner": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_-]{0,31}$",
+ "description": "One owner service (live, community, wiki, …)."
+ },
+ "type": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_-]{0,31}$",
+ "description": "One document type within the owner (channel, vod, thread, …)."
+ },
+ "lang": {
+ "type": "string",
+ "pattern": "^[a-zA-Z]{2,3}(-[A-Za-z0-9]{2,8})*$",
+ "description": "BCP 47 language."
+ },
+ "facets": {
+ "type": "string",
+ "maxLength": 400,
+ "description": "Comma-separated facet keys to count over what the caller can see."
+ },
+ "limit": {
+ "type": [
+ "integer",
+ "string"
+ ],
+ "pattern": "^[0-9]{1,3}$",
+ "minimum": 1,
+ "maximum": 100
+ },
+ "cursor": {
+ "type": "string",
+ "maxLength": 512,
+ "description": "next_cursor of the previous page."
+ }
+ },
+ "patternProperties": {
+ "^facet\\.[a-z][a-z0-9_]{0,39}$": {
+ "description": "A facet filter (facet.<key>=<value>); repeat the parameter for several values.",
+ "oneOf": [
+ {
+ "type": "string",
+ "maxLength": 200
+ },
+ {
+ "type": "array",
+ "maxItems": 20,
+ "items": {
+ "type": "string",
+ "maxLength": 200
+ }
+ }
+ ]
+ }
+ }
+ }
+ },
+ "search.query-result": {
+ "version": "1.0.0",
+ "owner": "search",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/search/query-result.v1.json",
+ "title": "SearchQueryResult",
+ "description": "search.query-result@1: the answer of GET /api/v1/search — one page of hits the caller may see, a cursor for the next page, and facet counts when asked. Never a total count and never an ACL.",
+ "type": "object",
+ "required": [
+ "results",
+ "next_cursor"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "results": {
+ "type": "array",
+ "maxItems": 100,
+ "items": {
+ "type": "object",
+ "required": [
+ "owner",
+ "type",
+ "id"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "owner": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ },
+ "id": {
+ "type": "string"
+ },
+ "revision": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "visibility": {
+ "type": "string"
+ },
+ "title": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "summary": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "canonical_url": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "facets": {
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "language": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "authorship": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "provenance": {
+ "type": [
+ "array",
+ "object",
+ "null"
+ ]
+ },
+ "published_at": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "updated_at": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "indexable": {
+ "type": "boolean",
+ "description": "false for a noindex document the caller may still see."
+ },
+ "snippet_html": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "Escaped text with <mark> around the matches; nothing else."
+ }
+ }
+ }
+ },
+ "next_cursor": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "facets": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "value",
+ "count"
+ ],
+ "properties": {
+ "value": {
+ "type": "string"
+ },
+ "count": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "search.write-result": {
+ "version": "1.0.0",
+ "owner": "search",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/search/write-result.v1.json",
+ "title": "SearchWriteResult",
+ "description": "search.write-result@1: the answer of PUT /api/v1/documents/:owner/:type/:id (search.document.write). applied: stored (or a tombstone recorded); unchanged: the same revision with the same content. A revision older than the stored one is a 409 problem search.stale_revision; the same revision with different content is 409 search.revision_conflict.",
+ "type": "object",
+ "required": [
+ "outcome",
+ "revision"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "outcome": {
+ "enum": [
+ "applied",
+ "unchanged"
+ ]
+ },
+ "revision": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "stored_revision": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "description": "What Search held before this write (null: nothing)."
+ },
+ "deleted": {
+ "type": "boolean"
+ }
+ }
+ }
+ },
"sources.source": {
"sources.source": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "sources",
"owner": "sources",
@@ -839,6 +839,235 @@ export interface IndexDocument {
@@ -839,6 +839,235 @@ export interface IndexDocument {
};
};
}
}
+/** [email protected] (owner: search) */
+/**
+ * search.query@1: the query string of GET /api/v1/search (and, with q/owner/type/limit only, GET /api/v1/suggest), capabilities search.query.run and search.query.delegate. Who sees what is decided by Search per hit: anonymous callers get public, published, indexable documents; a person also restricted documents naming them; a delegating service passes X-OV-Subject. Filters narrow; they never widen visibility.
+ */
+export interface SearchQuery {
+ /**
+ * Words to match (FTS5; empty lists by recency).
+ */
+ q?: string;
+ /**
+ * One owner service (live, community, wiki, …).
+ */
+ owner?: string;
+ /**
+ * One document type within the owner (channel, vod, thread, …).
+ */
+ type?: string;
+ /**
+ * BCP 47 language.
+ */
+ lang?: string;
+ /**
+ * Comma-separated facet keys to count over what the caller can see.
+ */
+ facets?: string;
+ limit?: number | string;
+ /**
+ * next_cursor of the previous page.
+ */
+ cursor?: string;
+ /**
+ * A facet filter (facet.<key>=<value>); repeat the parameter for several values.
+ *
+ * This interface was referenced by `SearchQuery`'s JSON-Schema definition
+ * via the `patternProperty` "^facet\.[a-z][a-z0-9_]{0,39}$".
+ */
+ [k: 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, 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,
+ 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,
+ 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: search) */
+/**
+ * search.query-result@1: the answer of GET /api/v1/search — one page of hits the caller may see, a cursor for the next page, and facet counts when asked. Never a total count and never an ACL.
+ */
+export interface SearchQueryResult {
+ /**
+ * @maxItems 100
+ */
+ results: {
+ owner: string;
+ type: string;
+ id: string;
+ revision?: number;
+ visibility?: string;
+ title?: string | null;
+ summary?: string | null;
+ canonical_url?: string | null;
+ facets?: {} | null;
+ language?: string | null;
+ authorship?: string | null;
+ provenance?: unknown[] | {} | null;
+ published_at?: string | null;
+ updated_at?: string | null;
+ /**
+ * false for a noindex document the caller may still see.
+ */
+ indexable?: boolean;
+ /**
+ * Escaped text with <mark> around the matches; nothing else.
+ */
+ snippet_html?: string | null;
+ }[];
+ next_cursor: string | null;
+ facets?: {
+ [k: string]:
+ | {
+ value: string;
+ count: number;
+ }[]
+ | undefined;
+ };
+}
+
+/** [email protected] (owner: search) */
+/**
+ * search.write-result@1: the answer of PUT /api/v1/documents/:owner/:type/:id (search.document.write). applied: stored (or a tombstone recorded); unchanged: the same revision with the same content. A revision older than the stored one is a 409 problem search.stale_revision; the same revision with different content is 409 search.revision_conflict.
+ */
+export interface SearchWriteResult {
+ outcome: "applied" | "unchanged";
+ revision: number;
+ /**
+ * What Search held before this write (null: nothing).
+ */
+ stored_revision?: number | null;
+ deleted?: boolean;
+}
+
/** [email protected] (owner: sources) */
/** [email protected] (owner: sources) */
/**
/**
* A registered source (roadmap §4.2 B, Wave 14): where it is fetched from and on what terms. Credentials are referenced by environment-variable NAME only. A source's existence is never permission to fetch: robots and terms are verified before it is enabled, and the fetcher re-checks robots.txt on every run.
* A registered source (roadmap §4.2 B, Wave 14): where it is fetched from and on what terms. Credentials are referenced by environment-variable NAME only. A source's existence is never permission to fetch: robots and terms are verified before it is enabled, and the fetcher re-checks robots.txt on every run.
@@ -22,5 +22,6 @@
@@ -22,5 +22,6 @@
"GET /api/v1/owners/:owner/documents",
"GET /api/v1/owners/:owner/documents",
"GET /api/v1/owners/:owner/documents/:type/:id",
"GET /api/v1/owners/:owner/documents/:type/:id",
"GET /api/v1/owners/:owner/rejections"
"GET /api/v1/owners/:owner/rejections"
- ]
+ ],
+ "outputSchema": "search.write-result@1"
}
}
@@ -17,5 +17,7 @@
@@ -17,5 +17,7 @@
"GET /api/v1/search",
"GET /api/v1/search",
"GET /api/v1/suggest",
"GET /api/v1/suggest",
"GET /api/v1/documents/:owner/:type/:id"
"GET /api/v1/documents/:owner/:type/:id"
- ]
+ ],
+ "inputSchema": "search.query@1",
+ "outputSchema": "search.query-result@1"
}
}
@@ -17,5 +17,7 @@
@@ -17,5 +17,7 @@
"GET /api/v1/search",
"GET /api/v1/search",
"GET /api/v1/suggest",
"GET /api/v1/suggest",
"GET /api/v1/documents/:owner/:type/:id"
"GET /api/v1/documents/:owner/:type/:id"
- ]
+ ],
+ "inputSchema": "search.query@1",
+ "outputSchema": "search.query-result@1"
}
}